All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/24] Make Nokia N900 cameras working
       [not found] <20160420081427.GZ32125@valkosipuli.retiisi.org.uk>
@ 2016-04-24 21:08 ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
                     ` (29 more replies)
  0 siblings, 30 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Those patch series make cameras on Nokia N900 partially working.
Some more patches are needed, but I've already sent them for
upstreaming so they are not part of the series:

https://lkml.org/lkml/2016/4/16/14
https://lkml.org/lkml/2016/4/16/33

As omap3isp driver supports only one endpoint on ccp2 interface,
but cameras on N900 require different strobe settings, so far
it is not possible to have both cameras correctly working with
the same board DTS. DTS patch in the series has the correct
settings for the front camera. This is a problem still to be
solved.

The needed pipeline could be made with:

media-ctl -r
media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'

and tested with:

mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://


Ivaylo Dimitrov (8):
  smiaregs: Generic i2c register writing
  et8ek8: Toshiba 5MP sensor driver
  v4l: of: Support CSI-1 and CCP2 busses
  media: video-bus-switch: new driver
  ARM: dts: omap3-n900: enable cameras
  [media] omap3isp: Correctly set IO_OUT_SEL and VP_CLK_POL for CCP2
    mode
  [media] omap3isp: Make sure CSI1 interface is enabled in CPP2 mode
  ARM: dts: omap3-n900: enable cameras - remove invalid entry

Sakari Ailus (10):
  smiapp-pll: Take existing divisor into account in minimum divisor
    check
  smiapp: Add smiapp_has_quirk() to tell whether a quirk is implemented
  smiapp: Add quirk control support
  v4l: of: Call CSI2 bus csi2, not csi
  v4l: of: Obtain data bus type from bus-type property
  v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type
  v4l: of: Separate lane parsing from CSI-2 bus parameter parsing
  dt: bindings: v4l: Add bus-type video interface property
  dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt
  omap3isp: dt: Add support for CSI1/CCP2 busses

Sebastian Reichel (5):
  media: et8ek8: add device tree binding document
  media: add subdev type for bus switch
  smiapp: add CCP2 support
  v4l2-async: per notifier locking
  v4l2_device_register_subdev_nodes: allow calling multiple times

Tuukka.O Toivonen (1):
  V4L fixes

 .../bindings/media/i2c/toshiba,et8ek8.txt          |   56 +
 .../devicetree/bindings/media/video-interfaces.txt |   11 +-
 arch/arm/boot/dts/omap3-n900.dts                   |  139 ++
 drivers/media/i2c/Kconfig                          |    1 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/smia/Kconfig                     |   17 +
 drivers/media/i2c/smia/Makefile                    |    2 +
 drivers/media/i2c/smia/et8ek8.c                    | 1788 ++++++++++++++++++++
 drivers/media/i2c/smia/smiaregs.c                  |  724 ++++++++
 drivers/media/i2c/smiapp-pll.c                     |    3 +-
 drivers/media/i2c/smiapp/smiapp-core.c             |   18 +-
 drivers/media/i2c/smiapp/smiapp-quirk.h            |   10 +-
 drivers/media/platform/Kconfig                     |   10 +
 drivers/media/platform/Makefile                    |    2 +
 drivers/media/platform/omap3isp/isp.c              |  112 +-
 drivers/media/platform/omap3isp/ispccp2.c          |   34 +-
 drivers/media/platform/omap3isp/ispreg.h           |    4 +
 drivers/media/platform/omap3isp/omap3isp.h         |    1 +
 drivers/media/platform/video-bus-switch.c          |  366 ++++
 drivers/media/v4l2-core/v4l2-async.c               |   50 +-
 drivers/media/v4l2-core/v4l2-device.c              |    3 +
 drivers/media/v4l2-core/v4l2-of.c                  |  137 +-
 include/media/smiaregs.h                           |  143 ++
 include/media/v4l2-async.h                         |    2 +
 include/media/v4l2-mediabus.h                      |    4 +
 include/media/v4l2-of.h                            |   17 +
 include/uapi/linux/media.h                         |    1 +
 include/uapi/linux/v4l2-controls.h                 |   17 +
 28 files changed, 3579 insertions(+), 94 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
 create mode 100644 drivers/media/i2c/smia/Kconfig
 create mode 100644 drivers/media/i2c/smia/Makefile
 create mode 100644 drivers/media/i2c/smia/et8ek8.c
 create mode 100644 drivers/media/i2c/smia/smiaregs.c
 create mode 100644 drivers/media/platform/video-bus-switch.c
 create mode 100644 include/media/smiaregs.h

-- 
1.9.1


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

* [RFC PATCH 01/24] V4L fixes
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 22:05     ` Pavel Machek
                       ` (2 more replies)
  2016-04-24 21:08   ` [RFC PATCH 02/24] smiaregs: Generic i2c register writing Ivaylo Dimitrov
                     ` (28 subsequent siblings)
  29 siblings, 3 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Tuukka.O Toivonen

From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>

Squashed from the following upstream commits:

V4L: Create control class for sensor mode
V4L: add ad5820 focus specific custom controls
V4L: add V4L2_CID_TEST_PATTERN
V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock

Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
---
 include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..23011cc 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -62,6 +62,7 @@
 #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
 #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
 #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
+#define V4L2_CTRL_CLASS_MODE		0x00a40000	/* Sensor mode information */
 
 /* User-class control IDs */
 
@@ -974,4 +975,20 @@ enum v4l2_detect_md_mode {
 #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
 #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
 
+/* SMIA-type sensor information */
+#define V4L2_CID_MODE_CLASS_BASE		(V4L2_CTRL_CLASS_MODE | 0x900)
+#define V4L2_CID_MODE_CLASS			(V4L2_CTRL_CLASS_MODE | 1)
+#define V4L2_CID_MODE_FRAME_WIDTH		(V4L2_CID_MODE_CLASS_BASE+1)
+#define V4L2_CID_MODE_FRAME_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+2)
+#define V4L2_CID_MODE_VISIBLE_WIDTH		(V4L2_CID_MODE_CLASS_BASE+3)
+#define V4L2_CID_MODE_VISIBLE_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+4)
+#define V4L2_CID_MODE_PIXELCLOCK		(V4L2_CID_MODE_CLASS_BASE+5)
+#define V4L2_CID_MODE_SENSITIVITY		(V4L2_CID_MODE_CLASS_BASE+6)
+#define V4L2_CID_MODE_OPSYSCLOCK		(V4L2_CID_MODE_CLASS_BASE+7)
+
+/* Control IDs specific to the AD5820 driver as defined by V4L2 */
+#define V4L2_CID_FOCUS_AD5820_BASE 		(V4L2_CTRL_CLASS_CAMERA | 0x10af)
+#define V4L2_CID_FOCUS_AD5820_RAMP_TIME		(V4L2_CID_FOCUS_AD5820_BASE+0)
+#define V4L2_CID_FOCUS_AD5820_RAMP_MODE		(V4L2_CID_FOCUS_AD5820_BASE+1)
+
 #endif
-- 
1.9.1


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

* [RFC PATCH 02/24] smiaregs: Generic i2c register writing
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver Ivaylo Dimitrov
                     ` (27 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/i2c/Kconfig         |   1 +
 drivers/media/i2c/Makefile        |   1 +
 drivers/media/i2c/smia/Kconfig    |   9 +
 drivers/media/i2c/smia/Makefile   |   1 +
 drivers/media/i2c/smia/smiaregs.c | 724 ++++++++++++++++++++++++++++++++++++++
 include/media/smiaregs.h          | 143 ++++++++
 6 files changed, 879 insertions(+)
 create mode 100644 drivers/media/i2c/smia/Kconfig
 create mode 100644 drivers/media/i2c/smia/Makefile
 create mode 100644 drivers/media/i2c/smia/smiaregs.c
 create mode 100644 include/media/smiaregs.h

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 993dc50..254c106 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -629,6 +629,7 @@ config VIDEO_S5K5BAF
 	  camera sensor with an embedded SoC image signal processor.
 
 source "drivers/media/i2c/smiapp/Kconfig"
+source "drivers/media/i2c/smia/Kconfig"
 
 config VIDEO_S5C73M3
 	tristate "Samsung S5C73M3 sensor support"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 94f2c99..05e79aa 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_VIDEO_SMIAPP)	+= smiapp/
 obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 obj-y				+= soc_camera/
+obj-y				+= smia/
 
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
new file mode 100644
index 0000000..d9be497
--- /dev/null
+++ b/drivers/media/i2c/smia/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_SMIAREGS
+	tristate "Generic SMIA I2C register access and register list helper"
+	depends on I2C
+	---help---
+	  This allows writing and reading SMIA image sensors' I2C registers
+	  easily.
+
+	  Also a few helper functions are provided to work with binary
+	  register lists.
diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
new file mode 100644
index 0000000..cff67bc
--- /dev/null
+++ b/drivers/media/i2c/smia/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
diff --git a/drivers/media/i2c/smia/smiaregs.c b/drivers/media/i2c/smia/smiaregs.c
new file mode 100644
index 0000000..1479bba
--- /dev/null
+++ b/drivers/media/i2c/smia/smiaregs.c
@@ -0,0 +1,724 @@
+/*
+ * drivers/media/i2c/smia/smiaregs.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/sort.h>
+#include <linux/v4l2-subdev.h>
+#include <media/smiaregs.h>
+
+/*
+ * Video control helpers
+ */
+int smia_ctrl_find(const struct v4l2_queryctrl *ctrls, size_t nctrls, int id)
+{
+	size_t i;
+
+	for (i = 0; i < nctrls; i++)
+		if (ctrls[i].id == id)
+			break;
+
+	if (i == nctrls)
+		i = -EINVAL;
+
+	return i;
+}
+EXPORT_SYMBOL_GPL(smia_ctrl_find);
+
+int smia_ctrl_find_next(const struct v4l2_queryctrl *ctrls, size_t nctrls,
+			int id)
+{
+	int i;
+	u32 best = (u32)-1;
+
+	for (i = 0; i < nctrls; i++)
+		if (ctrls[i].id > id
+		    && (best == (u32)-1 || ctrls[i].id < ctrls[best].id))
+			best = i;
+
+	if (best == (u32)-1)
+		return -EINVAL;
+
+	return best;
+}
+EXPORT_SYMBOL_GPL(smia_ctrl_find_next);
+
+int smia_ctrl_query(const struct v4l2_queryctrl *ctrls, size_t nctrls,
+		    struct v4l2_queryctrl *a)
+{
+	int id, i;
+
+	id = a->id;
+	if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+		id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+		i = smia_ctrl_find_next(ctrls, nctrls, id);
+	} else {
+		i = smia_ctrl_find(ctrls, nctrls, id);
+	}
+
+	if (i < 0)
+		return -EINVAL;
+
+	*a = ctrls[i];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smia_ctrl_query);
+
+int smia_mode_query(const __u32 *ctrls, size_t nctrls, struct v4l2_queryctrl *a)
+{
+	static const struct {
+		__u32 id;
+		char *name;
+	} ctrl[] = {
+		{ .id = V4L2_CID_MODE_FRAME_WIDTH,    .name = "Frame width" },
+		{ .id = V4L2_CID_MODE_FRAME_HEIGHT,   .name = "Frame height" },
+		{ .id = V4L2_CID_MODE_VISIBLE_WIDTH,  .name = "Visible width" },
+		{ .id = V4L2_CID_MODE_VISIBLE_HEIGHT,
+		  .name = "Visible height" },
+		{ .id = V4L2_CID_MODE_PIXELCLOCK,
+		  .name = "Pixel clock [Hz]" },
+		{ .id = V4L2_CID_MODE_SENSITIVITY,    .name = "Sensitivity" },
+		{ .id = V4L2_CID_MODE_OPSYSCLOCK,
+		  .name = "Output pixel clock [Hz]" },
+	};
+	int id, next = 0, i;
+
+	id = a->id;
+	if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+		id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+		next = 1;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ctrl); i++) {
+		if ((!next && ctrl[i].id == id) ||
+		    (next && ctrl[i].id > id)) {
+			int j;
+			for (j = 0; j < nctrls; j++)
+				if (ctrl[i].id == ctrls[j])
+					goto found;
+		}
+	}
+	return -EINVAL;
+
+found:
+	a->id            = ctrl[i].id;
+	strcpy(a->name, ctrl[i].name);
+	a->type          = V4L2_CTRL_TYPE_INTEGER;
+	a->minimum       = 0;
+	a->maximum       = 0;
+	a->step          = 0;
+	a->default_value = 0;
+	a->flags         = V4L2_CTRL_FLAG_READ_ONLY;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smia_mode_query);
+
+int smia_mode_g_ctrl(const __u32 *ctrls, size_t nctrls, struct v4l2_control *vc,
+		     const struct smia_mode *sm)
+{
+	int i;
+
+	for (i = 0; i < nctrls; i++)
+		if (ctrls[i] == vc->id)
+			break;
+	if (i >= nctrls)
+		return -EINVAL;
+
+	switch (vc->id) {
+	case V4L2_CID_MODE_FRAME_WIDTH:
+		vc->value = sm->width;
+		break;
+	case V4L2_CID_MODE_FRAME_HEIGHT:
+		vc->value = sm->height;
+		break;
+	case V4L2_CID_MODE_VISIBLE_WIDTH:
+		vc->value = sm->window_width;
+		break;
+	case V4L2_CID_MODE_VISIBLE_HEIGHT:
+		vc->value = sm->window_height;
+		break;
+	case V4L2_CID_MODE_PIXELCLOCK:
+		vc->value = sm->pixel_clock;
+		break;
+	case V4L2_CID_MODE_SENSITIVITY:
+		vc->value = sm->sensitivity;
+		break;
+	case V4L2_CID_MODE_OPSYSCLOCK:
+		vc->value = sm->opsys_clock;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smia_mode_g_ctrl);
+
+/*
+ * Reglist helpers
+ */
+static int smia_reglist_cmp(const void *a, const void *b)
+{
+	const struct smia_reglist **list1 = (const struct smia_reglist **)a,
+		**list2 = (const struct smia_reglist **)b;
+
+	/* Put real modes in the beginning. */
+	if ((*list1)->type == SMIA_REGLIST_MODE &&
+	    (*list2)->type != SMIA_REGLIST_MODE)
+		return -1;
+	if ((*list1)->type != SMIA_REGLIST_MODE &&
+	    (*list2)->type == SMIA_REGLIST_MODE)
+		return 1;
+
+	/* Descending width. */
+	if ((*list1)->mode.window_width > (*list2)->mode.window_width)
+		return -1;
+	if ((*list1)->mode.window_width < (*list2)->mode.window_width)
+		return 1;
+
+	if ((*list1)->mode.window_height > (*list2)->mode.window_height)
+		return -1;
+	if ((*list1)->mode.window_height < (*list2)->mode.window_height)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Prepare register list created by dcc-pulautin for use in kernel.
+ */
+int smia_reglist_import(struct smia_meta_reglist *meta)
+{
+	uintptr_t nlists = 0;
+
+	if (meta->magic != SMIA_MAGIC) {
+		printk(KERN_ERR "invalid camera sensor firmware (0x%08X)\n",
+		       meta->magic);
+		return -EILSEQ;
+	}
+
+	printk(KERN_ALERT "%s: meta_reglist version %s\n",
+	       __func__, meta->version);
+
+	while (meta->reglist[nlists].ptr != NULL)
+		nlists++;
+
+	if (!nlists)
+		return -EINVAL;
+
+	sort(&meta->reglist[0].ptr, nlists, sizeof(meta->reglist[0].ptr),
+	     smia_reglist_cmp, NULL);
+
+	nlists = 0;
+	while (meta->reglist[nlists].ptr != NULL) {
+		struct smia_reglist *list;
+
+		list = meta->reglist[nlists].ptr;
+
+		printk(KERN_DEBUG
+		       "%s: type %d\tw %d\th %d\tfmt %x\tival %d/%d\tptr %p\n",
+		       __func__,
+		       list->type,
+		       list->mode.window_width, list->mode.window_height,
+		       list->mode.pixel_format,
+		       list->mode.timeperframe.numerator,
+		       list->mode.timeperframe.denominator,
+		       (void *)meta->reglist[nlists].ptr);
+
+		nlists++;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_import);
+
+struct smia_reglist *smia_reglist_find_type(struct smia_meta_reglist *meta,
+					    u16 type)
+{
+	struct smia_reglist **next = &meta->reglist[0].ptr;
+
+	while (*next) {
+		if ((*next)->type == type)
+			return *next;
+
+		next++;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_find_type);
+
+struct smia_reglist **smia_reglist_first(struct smia_meta_reglist *meta)
+{
+	return &meta->reglist[0].ptr;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_first);
+
+struct smia_reglist *smia_reglist_find_mode_fmt(struct smia_meta_reglist *meta,
+						struct v4l2_mbus_framefmt *fmt)
+{
+	struct smia_reglist **list = smia_reglist_first(meta);
+	struct smia_reglist *best_match = NULL;
+	struct smia_reglist *best_other = NULL;
+	struct v4l2_mbus_framefmt format;
+	unsigned int max_dist_match = (unsigned int)-1;
+	unsigned int max_dist_other = (unsigned int)-1;
+
+	/* Find the mode with the closest image size. The distance between
+	 * image sizes is the size in pixels of the non-overlapping regions
+	 * between the requested size and the frame-specified size.
+	 *
+	 * Store both the closest mode that matches the requested format, and
+	 * the closest mode for all other formats. The best match is returned
+	 * if found, otherwise the best mode with a non-matching format is
+	 * returned.
+	 */
+	for (; *list; list++) {
+		unsigned int dist;
+
+		if ((*list)->type != SMIA_REGLIST_MODE)
+			continue;
+
+		smia_reglist_to_mbus(*list, &format);
+
+		dist = min(fmt->width, format.width)
+		     * min(fmt->height, format.height);
+		dist = format.width * format.height
+		     + fmt->width * fmt->height - 2 * dist;
+
+
+		if (fmt->code == format.code) {
+			if (dist < max_dist_match || best_match == NULL) {
+				best_match = *list;
+				max_dist_match = dist;
+			}
+		} else {
+			if (dist < max_dist_other || best_other == NULL) {
+				best_other = *list;
+				max_dist_other = dist;
+			}
+		}
+	}
+
+	return best_match ? best_match : best_other;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_find_mode_fmt);
+
+#define TIMEPERFRAME_AVG_FPS(t)						\
+	(((t).denominator + ((t).numerator >> 1)) / (t).numerator)
+struct smia_reglist *smia_reglist_find_mode_ival(
+	struct smia_meta_reglist *meta,
+	struct smia_reglist *current_reglist,
+	struct v4l2_fract *timeperframe)
+{
+	int fps = TIMEPERFRAME_AVG_FPS(*timeperframe);
+	struct smia_reglist **list = smia_reglist_first(meta);
+	struct smia_mode *current_mode = &current_reglist->mode;
+
+	for (; *list; list++) {
+		struct smia_mode *mode = &(*list)->mode;
+
+		if ((*list)->type != SMIA_REGLIST_MODE)
+			continue;
+
+		if (mode->window_width != current_mode->window_width
+		    || mode->window_height != current_mode->window_height)
+			continue;
+
+		if (TIMEPERFRAME_AVG_FPS(mode->timeperframe) == fps)
+			return *list;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_find_mode_ival);
+
+#define MAX_FMTS 4
+int smia_reglist_enum_mbus_code(struct smia_meta_reglist *meta,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct smia_reglist **list = smia_reglist_first(meta);
+	u32 pixelformat[MAX_FMTS];
+	int npixelformat = 0;
+
+	if (code->index >= MAX_FMTS)
+		return -EINVAL;
+
+	for (; *list; list++) {
+		struct smia_mode *mode = &(*list)->mode;
+		int i;
+
+		if ((*list)->type != SMIA_REGLIST_MODE)
+			continue;
+
+		for (i = 0; i < npixelformat; i++) {
+			if (pixelformat[i] == mode->pixel_format)
+				break;
+		}
+		if (i != npixelformat)
+			continue;
+
+		if (code->index == npixelformat) {
+			if (mode->pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
+				code->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
+			else
+				code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+			return 0;
+		}
+
+		pixelformat[npixelformat] = mode->pixel_format;
+		npixelformat++;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_enum_mbus_code);
+
+int smia_reglist_enum_frame_size(struct smia_meta_reglist *meta,
+				 struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct smia_reglist **list = smia_reglist_first(meta);
+	struct v4l2_mbus_framefmt format;
+	int cmp_width = INT_MAX;
+	int cmp_height = INT_MAX;
+	int index = fse->index;
+
+	for (; *list; list++) {
+		if ((*list)->type != SMIA_REGLIST_MODE)
+			continue;
+
+		smia_reglist_to_mbus(*list, &format);
+		if (fse->code != format.code)
+			continue;
+
+		/* Assume that the modes are grouped by frame size. */
+		if (format.width == cmp_width && format.height == cmp_height)
+			continue;
+
+		cmp_width = format.width;
+		cmp_height = format.height;
+
+		if (index-- == 0) {
+			fse->min_width = format.width;
+			fse->min_height = format.height;
+			fse->max_width = format.width;
+			fse->max_height = format.height;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_enum_frame_size);
+
+int smia_reglist_enum_frame_ival(struct smia_meta_reglist *meta,
+				 struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct smia_reglist **list = smia_reglist_first(meta);
+	struct v4l2_mbus_framefmt format;
+	int index = fie->index;
+
+	for (; *list; list++) {
+		struct smia_mode *mode = &(*list)->mode;
+
+		if ((*list)->type != SMIA_REGLIST_MODE)
+			continue;
+
+		smia_reglist_to_mbus(*list, &format);
+		if (fie->code != format.code)
+			continue;
+
+		if (fie->width != format.width || fie->height != format.height)
+			continue;
+
+		if (index-- == 0) {
+			fie->interval = mode->timeperframe;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_enum_frame_ival);
+
+void smia_reglist_to_mbus(const struct smia_reglist *reglist,
+			  struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->width = reglist->mode.window_width;
+	fmt->height = reglist->mode.window_height;
+
+	if (reglist->mode.pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
+		fmt->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
+	else
+		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+}
+EXPORT_SYMBOL_GPL(smia_reglist_to_mbus);
+
+/*
+ *
+ * Register access helpers
+ *
+ */
+
+/*
+ * Read a 8/16/32-bit i2c register.  The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smia_i2c_read_reg(struct i2c_client *client, u16 data_length,
+		      u16 reg, u32 *val)
+{
+	int r;
+	struct i2c_msg msg[1];
+	unsigned char data[4];
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (data_length != SMIA_REG_8BIT && data_length != SMIA_REG_16BIT)
+		return -EINVAL;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+
+	/* high byte goes out first */
+	data[0] = (u8) (reg >> 8);;
+	data[1] = (u8) (reg & 0xff);
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		goto err;
+
+	msg->len = data_length;
+	msg->flags = I2C_M_RD;
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		goto err;
+
+	*val = 0;
+	/* high byte comes first */
+	if (data_length == SMIA_REG_8BIT)
+		*val = data[0];
+	else
+		*val = (data[0] << 8) + data[1];
+
+	return 0;
+
+err:
+	dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r);
+
+	return r;
+}
+EXPORT_SYMBOL_GPL(smia_i2c_read_reg);
+
+static void smia_i2c_create_msg(struct i2c_client *client, u16 len, u16 reg,
+				u32 val, struct i2c_msg *msg,
+				unsigned char *buf)
+{
+	msg->addr = client->addr;
+	msg->flags = 0; /* Write */
+	msg->len = 2 + len;
+	msg->buf = buf;
+
+	/* high byte goes out first */
+	buf[0] = (u8) (reg >> 8);;
+	buf[1] = (u8) (reg & 0xff);
+
+	switch (len) {
+	case SMIA_REG_8BIT:
+		buf[2] = (u8) (val) & 0xff;
+		break;
+	case SMIA_REG_16BIT:
+		buf[2] = (u8) (val >> 8) & 0xff;
+		buf[3] = (u8) (val & 0xff);
+		break;
+	case SMIA_REG_32BIT:
+		buf[2] = (u8) (val >> 24) & 0xff;
+		buf[3] = (u8) (val >> 16) & 0xff;
+		buf[4] = (u8) (val >> 8) & 0xff;
+		buf[5] = (u8) (val & 0xff);
+		break;
+	default:
+		BUG();
+	}
+}
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smia_i2c_write_reg(struct i2c_client *client, u16 data_length, u16 reg,
+		       u32 val)
+{
+	int r;
+	struct i2c_msg msg[1];
+	unsigned char data[6];
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (data_length != SMIA_REG_8BIT && data_length != SMIA_REG_16BIT)
+		return -EINVAL;
+
+	smia_i2c_create_msg(client, data_length, reg, val, msg, data);
+
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		dev_err(&client->dev,
+			"wrote 0x%x to offset 0x%x error %d\n", val, reg, r);
+	else
+		r = 0; /* on success i2c_transfer() return messages trasfered */
+
+	return r;
+}
+EXPORT_SYMBOL_GPL(smia_i2c_write_reg);
+
+/*
+ * A buffered write method that puts the wanted register write
+ * commands in a message list and passes the list to the i2c framework
+ */
+static int smia_i2c_buffered_write_regs(struct i2c_client *client,
+					const struct smia_reg *wnext, int cnt)
+{
+	/* FIXME: check how big cnt is */
+	struct i2c_msg msg[cnt];
+	unsigned char data[cnt][6];
+	int wcnt = 0;
+	u16 reg, data_length;
+	u32 val;
+
+	/* Create new write messages for all writes */
+	while (wcnt < cnt) {
+		data_length = wnext->type;
+		reg = wnext->reg;
+		val = wnext->val;
+		wnext++;
+
+		smia_i2c_create_msg(client, data_length, reg,
+				    val, &msg[wcnt], &data[wcnt][0]);
+
+		/* Update write count */
+		wcnt++;
+	}
+
+	/* Now we send everything ... */
+	return i2c_transfer(client->adapter, msg, wcnt);
+}
+
+/*
+ * Write a list of registers to i2c device.
+ *
+ * The list of registers is terminated by SMIA_REG_TERM.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smia_i2c_write_regs(struct i2c_client *client,
+			const struct smia_reg reglist[])
+{
+	int r, cnt = 0;
+	const struct smia_reg *next, *wnext;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (reglist == NULL)
+		return -EINVAL;
+
+	/* Initialize list pointers to the start of the list */
+	next = wnext = reglist;
+
+	do {
+		/*
+		 * We have to go through the list to figure out how
+		 * many regular writes we have in a row
+		 */
+		while (next->type != SMIA_REG_TERM
+		       && next->type != SMIA_REG_DELAY) {
+			/*
+			 * Here we check that the actual lenght fields
+			 * are valid
+			 */
+			if (next->type != SMIA_REG_8BIT
+			    &&  next->type != SMIA_REG_16BIT) {
+				dev_err(&client->dev,
+					"Invalid value on entry %d 0x%x\n",
+					cnt, next->type);
+				return -EINVAL;
+			}
+
+			/*
+			 * Increment count of successive writes and
+			 * read pointer
+			 */
+			cnt++;
+			next++;
+		}
+
+		/* Now we start writing ... */
+		r = smia_i2c_buffered_write_regs(client, wnext, cnt);
+
+		/* ... and then check that everything was OK */
+		if (r < 0) {
+			dev_err(&client->dev, "i2c transfer error !!!\n");
+			return r;
+		}
+
+		/*
+		 * If we ran into a sleep statement when going through
+		 * the list, this is where we snooze for the required time
+		 */
+		if (next->type == SMIA_REG_DELAY) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(msecs_to_jiffies(next->val));
+			/*
+			 * ZZZ ...
+			 * Update list pointers and cnt and start over ...
+			 */
+			next++;
+			wnext = next;
+			cnt = 0;
+		}
+	} while (next->type != SMIA_REG_TERM);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smia_i2c_write_regs);
+
+int smia_i2c_reglist_find_write(struct i2c_client *client,
+				struct smia_meta_reglist *meta, u16 type)
+{
+	struct smia_reglist *reglist;
+
+	reglist = smia_reglist_find_type(meta, type);
+	if (IS_ERR(reglist))
+		return PTR_ERR(reglist);
+
+	return smia_i2c_write_regs(client, reglist->regs);
+}
+EXPORT_SYMBOL_GPL(smia_i2c_reglist_find_write);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Generic SMIA configuration and i2c register access");
+MODULE_LICENSE("GPL");
diff --git a/include/media/smiaregs.h b/include/media/smiaregs.h
new file mode 100644
index 0000000..8d7e79d
--- /dev/null
+++ b/include/media/smiaregs.h
@@ -0,0 +1,143 @@
+/*
+ * include/media/smiaregs.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef SMIAREGS_H
+#define SMIAREGS_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-subdev.h>
+
+struct v4l2_mbus_framefmt;
+struct v4l2_subdev_pad_mbus_code_enum;
+
+#define SMIA_MAGIC			0x531A0002
+
+struct smia_mode {
+	/* Physical sensor resolution and current image window */
+	__u16 sensor_width;
+	__u16 sensor_height;
+	__u16 sensor_window_origin_x;
+	__u16 sensor_window_origin_y;
+	__u16 sensor_window_width;
+	__u16 sensor_window_height;
+
+	/* Image data coming from sensor (after scaling) */
+	__u16 width;
+	__u16 height;
+	__u16 window_origin_x;
+	__u16 window_origin_y;
+	__u16 window_width;
+	__u16 window_height;
+
+	__u32 pixel_clock;		/* in Hz */
+	__u32 opsys_clock;		/* in Hz */
+	__u32 ext_clock;		/* in Hz */
+	struct v4l2_fract timeperframe;
+	__u32 max_exp;			/* Maximum exposure value */
+	__u32 pixel_format;		/* V4L2_PIX_FMT_xxx */
+	__u32 sensitivity;		/* 16.16 fixed point */
+};
+
+#define SMIA_REG_8BIT			1
+#define SMIA_REG_16BIT			2
+#define SMIA_REG_32BIT			4
+#define SMIA_REG_DELAY			100
+#define SMIA_REG_TERM			0xff
+struct smia_reg {
+	u16 type;
+	u16 reg;			/* 16-bit offset */
+	u32 val;			/* 8/16/32-bit value */
+};
+
+/* Possible struct smia_reglist types. */
+#define SMIA_REGLIST_STANDBY		0
+#define SMIA_REGLIST_POWERON		1
+#define SMIA_REGLIST_RESUME		2
+#define SMIA_REGLIST_STREAMON		3
+#define SMIA_REGLIST_STREAMOFF		4
+#define SMIA_REGLIST_DISABLED		5
+
+#define SMIA_REGLIST_MODE		10
+
+#define SMIA_REGLIST_LSC_ENABLE		100
+#define SMIA_REGLIST_LSC_DISABLE	101
+#define SMIA_REGLIST_ANR_ENABLE		102
+#define SMIA_REGLIST_ANR_DISABLE	103
+
+struct smia_reglist {
+	u32 type;
+	struct smia_mode mode;
+	struct smia_reg regs[];
+};
+
+#define SMIA_MAX_LEN			32
+struct smia_meta_reglist {
+	u32 magic;
+	char version[SMIA_MAX_LEN];
+	union {
+		struct smia_reglist *ptr;
+	} reglist[];
+};
+
+int smia_ctrl_find(const struct v4l2_queryctrl *ctrls, size_t nctrls, int id);
+int smia_ctrl_find_next(const struct v4l2_queryctrl *ctrls, size_t nctrls,
+			int id);
+int smia_ctrl_query(const struct v4l2_queryctrl *ctrls, size_t nctrls,
+		    struct v4l2_queryctrl *a);
+int smia_mode_query(const __u32 *ctrls, size_t nctrls,
+		    struct v4l2_queryctrl *a);
+int smia_mode_g_ctrl(const __u32 *ctrls, size_t nctrls, struct v4l2_control *vc,
+		     const struct smia_mode *sm);
+
+int smia_reglist_import(struct smia_meta_reglist *meta);
+struct smia_reglist *smia_reglist_find_type(struct smia_meta_reglist *meta,
+					    u16 type);
+struct smia_reglist **smia_reglist_first(struct smia_meta_reglist *meta);
+struct smia_reglist *smia_reglist_find_mode_fmt(struct smia_meta_reglist *meta,
+						struct v4l2_mbus_framefmt *fmt);
+struct smia_reglist *smia_reglist_find_mode_ival(
+	struct smia_meta_reglist *meta,
+	struct smia_reglist *current_reglist,
+	struct v4l2_fract *timeperframe);
+int smia_reglist_enum_mbus_code(struct smia_meta_reglist *meta,
+				struct v4l2_subdev_mbus_code_enum *code);
+int smia_reglist_enum_frame_size(struct smia_meta_reglist *meta,
+				 struct v4l2_subdev_frame_size_enum *fse);
+int smia_reglist_enum_frame_ival(struct smia_meta_reglist *meta,
+				 struct v4l2_subdev_frame_interval_enum *fie);
+void smia_reglist_to_mbus(const struct smia_reglist *reglist,
+			  struct v4l2_mbus_framefmt *fmt);
+
+int smia_i2c_read_reg(struct i2c_client *client, u16 data_length,
+		      u16 reg, u32 *val);
+int smia_i2c_write_reg(struct i2c_client *client, u16 data_length, u16 reg,
+		       u32 val);
+int smia_i2c_write_regs(struct i2c_client *client,
+			const struct smia_reg reglist[]);
+int smia_i2c_reglist_find_write(struct i2c_client *client,
+				struct smia_meta_reglist *meta, u16 type);
+
+#endif
-- 
1.9.1


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

* [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 02/24] smiaregs: Generic i2c register writing Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-05-01 10:44     ` Sakari Ailus
  2016-04-24 21:08   ` [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check Ivaylo Dimitrov
                     ` (26 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

add driver

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/i2c/smia/Kconfig  |    8 +
 drivers/media/i2c/smia/Makefile |    1 +
 drivers/media/i2c/smia/et8ek8.c | 1788 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 1797 insertions(+)
 create mode 100644 drivers/media/i2c/smia/et8ek8.c

diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
index d9be497..13ca043 100644
--- a/drivers/media/i2c/smia/Kconfig
+++ b/drivers/media/i2c/smia/Kconfig
@@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
 
 	  Also a few helper functions are provided to work with binary
 	  register lists.
+
+config VIDEO_ET8EK8
+	tristate "ET8EK8 camera sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEO_SMIAREGS
+	---help---
+	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
+	  It is used for example in Nokia N900 (RX-51).
diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
index cff67bc..56cf15e 100644
--- a/drivers/media/i2c/smia/Makefile
+++ b/drivers/media/i2c/smia/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
+obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
diff --git a/drivers/media/i2c/smia/et8ek8.c b/drivers/media/i2c/smia/et8ek8.c
new file mode 100644
index 0000000..46c112d
--- /dev/null
+++ b/drivers/media/i2c/smia/et8ek8.c
@@ -0,0 +1,1788 @@
+/*
+ * drivers/media/video/et8ek8.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
+ *
+ * This driver is based on the Micron MT9T012 camera imager driver
+ * (C) Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#define DEBUG
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/media-entity.h>
+#include <media/smiaregs.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define ET8EK8_NAME		"et8ek8"
+#define ET8EK8_XCLK_HZ		9600000
+#define ET8EK8_PRIV_MEM_SIZE	128
+
+#define CTRL_GAIN		0
+#define CTRL_EXPOSURE		1
+#define CTRL_TEST_PATTERN	2
+
+#define CID_TO_CTRL(id)		((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
+				 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
+				 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
+				 -EINVAL)
+
+struct et8ek8_sensor {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt format;
+	struct gpio_desc *reset;
+	struct regulator *vana;
+	struct clk *ext_clk;
+
+	u16 version;
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *pixel_rate;
+	struct smia_reglist *current_reglist;
+
+	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
+
+	struct mutex power_lock;
+	int power_count;
+};
+
+#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
+
+enum et8ek8_versions {
+	ET8EK8_REV_1 = 0x0001,
+	ET8EK8_REV_2,
+};
+
+/*
+ * This table describes what should be written to the sensor register
+ * for each gain value. The gain(index in the table) is in terms of
+ * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
+ * the *analog gain, [1] in the digital gain
+ *
+ * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
+ */
+static struct et8ek8_gain {
+	u16 analog;
+	u16 digital;
+} const et8ek8_gain_table[] = {
+	{ 32,    0},  /* x1 */
+	{ 34,    0},
+	{ 37,    0},
+	{ 39,    0},
+	{ 42,    0},
+	{ 45,    0},
+	{ 49,    0},
+	{ 52,    0},
+	{ 56,    0},
+	{ 60,    0},
+	{ 64,    0},  /* x2 */
+	{ 69,    0},
+	{ 74,    0},
+	{ 79,    0},
+	{ 84,    0},
+	{ 91,    0},
+	{ 97,    0},
+	{104,    0},
+	{111,    0},
+	{119,    0},
+	{128,    0},  /* x4 */
+	{137,    0},
+	{147,    0},
+	{158,    0},
+	{169,    0},
+	{181,    0},
+	{194,    0},
+	{208,    0},
+	{223,    0},
+	{239,    0},
+	{256,    0},  /* x8 */
+	{256,   73},
+	{256,  152},
+	{256,  236},
+	{256,  327},
+	{256,  424},
+	{256,  528},
+	{256,  639},
+	{256,  758},
+	{256,  886},
+	{256, 1023},  /* x16 */
+};
+
+/* Register definitions */
+#define REG_REVISION_NUMBER_L	0x1200
+#define REG_REVISION_NUMBER_H	0x1201
+
+#define PRIV_MEM_START_REG	0x0008
+#define PRIV_MEM_WIN_SIZE	8
+
+#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
+
+#define USE_CRC			1
+
+/*
+ *
+ * Stingray sensor mode settings for Scooby
+ *
+ *
+ */
+
+/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
+static struct smia_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {	/* 1 */
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 640 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_POWERON,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 2016,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 1207
+		},
+		.max_exp = 2012,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x126C, 0xCC },	/* Need to set firstly */
+		{ SMIA_REG_8BIT, 0x1269, 0x00 },	/* Strobe and Data of CCP2 delay are minimized. */
+		{ SMIA_REG_8BIT, 0x1220, 0x89 },	/* Refined value of Min H_COUNT  */
+		{ SMIA_REG_8BIT, 0x123A, 0x07 },	/* Frequency of SPCK setting (SPCK=MRCK) */
+		{ SMIA_REG_8BIT, 0x1241, 0x94 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1242, 0x02 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x124B, 0x00 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1255, 0xFF },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1256, 0x9F },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1258, 0x00 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* From parallel out to serial out */
+		{ SMIA_REG_8BIT, 0x125E, 0xC0 },	/* From w/ embeded data to w/o embeded data */
+		{ SMIA_REG_8BIT, 0x1263, 0x98 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1268, 0xC6 },	/* CCP2 out is from STOP to ACTIVE */
+		{ SMIA_REG_8BIT, 0x1434, 0x00 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1163, 0x44 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1166, 0x29 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1140, 0x02 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1011, 0x24 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1151, 0x80 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1152, 0x23 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1014, 0x05 },	/* Initial setting( for improvement2 of lower frequency noise ) */
+		{ SMIA_REG_8BIT, 0x1033, 0x06 },
+		{ SMIA_REG_8BIT, 0x1034, 0x79 },
+		{ SMIA_REG_8BIT, 0x1423, 0x3F },
+		{ SMIA_REG_8BIT, 0x1424, 0x3F },
+		{ SMIA_REG_8BIT, 0x1426, 0x00 },
+		{ SMIA_REG_8BIT, 0x1439, 0x00 },	/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
+		{ SMIA_REG_8BIT, 0x161F, 0x60 },	/* Switch of blemish correction (0d:disable / 1d:enable) */
+		{ SMIA_REG_8BIT, 0x1634, 0x00 },	/* Switch of auto noise correction (0d:disable / 1d:enable) */
+		{ SMIA_REG_8BIT, 0x1646, 0x00 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1648, 0x00 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x113E, 0x01 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x113F, 0x22 },	/* Initial setting */
+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
+static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {	/* 2 */
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 560 MHz
+ * VCO        = 560 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 128 (3072)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 175
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 6
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3072,
+		.height = 2016,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 1292
+		},
+		.max_exp = 2012,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x57 },
+		{ SMIA_REG_8BIT, 0x1238, 0x82 },
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
+		{ SMIA_REG_8BIT, 0x123A, 0x06 },
+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0x80 },	/* <-changed to v14 7E->80 */
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
+static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {	/* 3 */
+/* (without the +1)
+ * SPCK       = 96.5333333333333 MHz
+ * CCP2       = 579.2 MHz
+ * VCO        = 579.2 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 133 (3192)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 181
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 5
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3192,
+		.height = 1008,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 96533333,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 3000
+		},
+		.max_exp = 1004,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x5A },	/*        */
+		{ SMIA_REG_8BIT, 0x1238, 0x82 },	/*        */
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
+		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
+		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
+		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },	/*        */
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
+		{ SMIA_REG_8BIT, 0x121D, 0x63 },
+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode4_SVGA_864x656_29.88fps */
+static struct smia_reglist mode4_svga_864x656_29_88fps = {	/* 4 */
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 320 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 166 (3984)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3984,
+		.height = 672,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 864,
+		.window_height = 656,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2988
+		},
+		.max_exp = 668,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
+		{ SMIA_REG_8BIT, 0x121B, 0x62 },
+		{ SMIA_REG_8BIT, 0x121D, 0x62 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0xA6 },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode5_VGA_648x492_29.93fps */
+static struct smia_reglist mode5_vga_648x492_29_93fps = {	/* 5 */
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 320 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 221 (5304)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 5304,
+		.height = 504,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 648,
+		.window_height = 492,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2993
+		},
+		.max_exp = 500,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
+		{ SMIA_REG_8BIT, 0x121B, 0x61 },
+		{ SMIA_REG_8BIT, 0x121D, 0x61 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0xDD },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode2_16VGA_2592x1968_3.99fps */
+static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {	/* 6 */
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 640 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 254 (6096)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 6096,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 399
+		},
+		.max_exp = 6092,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0xFE },
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode_648x492_5fps */
+static struct smia_reglist mode_648x492_5fps = {	/* 7 */
+/* (without the +1)
+ * SPCK       = 13.3333333333333 MHz
+ * CCP2       = 53.3333333333333 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 221 (5304)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 5
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 5304,
+		.height = 504,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 648,
+		.window_height = 492,
+		.pixel_clock = 13333333,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 499
+		},
+		.max_exp = 500,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
+		{ SMIA_REG_8BIT, 0x123A, 0x57 },
+		{ SMIA_REG_8BIT, 0x121B, 0x61 },
+		{ SMIA_REG_8BIT, 0x121D, 0x61 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0xDD },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode3_4VGA_1296x984_5fps */
+static struct smia_reglist mode3_4vga_1296x984_5fps = {	/* 8 */
+/* (without the +1)
+ * SPCK       = 49.4 MHz
+ * CCP2       = 395.2 MHz
+ * VCO        = 790.4 MHz
+ * VCOUNT     = 250 (6000)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 247
+ * VCO_DIV    = 1
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 3000,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 49400000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 501
+		},
+		.max_exp = 2996,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x7B },
+		{ SMIA_REG_8BIT, 0x1238, 0x82 },
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
+		{ SMIA_REG_8BIT, 0x123A, 0x17 },
+		{ SMIA_REG_8BIT, 0x121B, 0x63 },
+		{ SMIA_REG_8BIT, 0x121D, 0x63 },
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
+		{ SMIA_REG_8BIT, 0x1222, 0xFA },
+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
+static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {	/* 9 */
+/* (without the +1)
+ * SPCK       = 84.2666666666667 MHz
+ * CCP2       = 505.6 MHz
+ * VCO        = 505.6 MHz
+ * VCOUNT     = 88 (2112)
+ * HCOUNT     = 133 (3192)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 158
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 5
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = SMIA_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3192,
+		.height = 1056,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 84266667,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2500
+		},
+		.max_exp = 1052,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ SMIA_REG_8BIT, 0x1239, 0x4F },	/*        */
+		{ SMIA_REG_8BIT, 0x1238, 0x02 },	/*        */
+		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
+		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
+		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
+		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
+		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
+		{ SMIA_REG_8BIT, 0x1222, 0x58 },	/*        */
+		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
+		{ SMIA_REG_8BIT, 0x121D, 0x63 },	/*        */
+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/*        */
+		{ SMIA_REG_TERM, 0, 0}
+	}
+};
+
+static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
+	.magic   = SMIA_MAGIC,
+	.version = "V14 03-June-2008",
+	.reglist = {
+		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
+		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
+		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
+		{ .ptr = &mode4_svga_864x656_29_88fps },
+		{ .ptr = &mode5_vga_648x492_29_93fps },
+		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
+		{ .ptr = &mode_648x492_5fps },
+		{ .ptr = &mode3_4vga_1296x984_5fps },
+		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
+		{ .ptr = 0 }
+	}
+};
+
+/*
+ * Return time of one row in microseconds, .8 fixed point format.
+ * If the sensor is not set to any mode, return zero.
+ */
+static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
+{
+	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
+	unsigned int rt;	/* Row time in .8 fixed point */
+
+	if (!sensor->current_reglist)
+		return 0;
+
+	clock = sensor->current_reglist->mode.pixel_clock;
+	clock = (clock + (1 << 9)) >> 10;
+	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
+	rt = (rt + (clock >> 1)) / clock;
+
+	return rt;
+}
+
+/*
+ * Convert exposure time `us' to rows. Modify `us' to make it to
+ * correspond to the actual exposure time.
+ */
+static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
+{
+	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
+	unsigned int rt;	/* Row time in .8 fixed point */
+
+	/* Assume that the maximum exposure time is at most ~8 s,
+	 * and the maximum width (with blanking) ~8000 pixels.
+	 * The formula here is in principle as simple as
+	 *    rows = exptime / 1e6 / width * pixel_clock
+	 * but to get accurate results while coping with value ranges,
+	 * have to do some fixed point math.
+	 */
+
+	rt = et8ek8_get_row_time(sensor);
+	rows = ((*us << 8) + (rt >> 1)) / rt;
+
+	if (rows > sensor->current_reglist->mode.max_exp)
+		rows = sensor->current_reglist->mode.max_exp;
+
+	/* Set the exposure time to the rounded value */
+	*us = (rt * rows + (1 << 7)) >> 8;
+
+	return rows;
+}
+
+/*
+ * Convert exposure time in rows to microseconds
+ */
+static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
+{
+	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
+}
+
+/* Called to change the V4L2 gain control value. This function
+ * rounds and clamps the given value and updates the V4L2 control value.
+ * If power is on, also updates the sensor analog and digital gains.
+ * gain is in 0.1 EV (exposure value) units.
+ */
+static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	struct et8ek8_gain new;
+	int r;
+
+	new = et8ek8_gain_table[gain];
+
+	/* FIXME: optimise I2C writes! */
+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
+				0x124a, new.analog >> 8);
+	if (r)
+		return r;
+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
+				0x1249, new.analog & 0xff);
+	if (r)
+		return r;
+
+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
+				0x124d, new.digital >> 8);
+	if (r)
+		return r;
+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
+				0x124c, new.digital & 0xff);
+
+	return r;
+}
+
+static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
+
+	/* Values for normal mode */
+	cbh_mode = 0;
+	cbv_mode = 0;
+	tp_mode  = 0;
+	din_sw   = 0x00;
+	r1420    = 0xF0;
+
+	if (mode != 0) {
+		/* Test pattern mode */
+		if (mode < 5) {
+			cbh_mode = 1;
+			cbv_mode = 1;
+			tp_mode  = mode + 3;
+		} else {
+			cbh_mode = 0;
+			cbv_mode = 0;
+			tp_mode  = mode - 4 + 3;
+		}
+		din_sw   = 0x01;
+		r1420    = 0xE0;
+	}
+
+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode << 4);
+	if (rval)
+		goto out;
+
+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121, cbh_mode << 7);
+	if (rval)
+		goto out;
+
+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124, cbv_mode << 7);
+	if (rval)
+		goto out;
+
+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
+	if (rval)
+		goto out;
+
+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
+	if (rval)
+		goto out;
+
+out:
+	return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct et8ek8_sensor *sensor =
+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+	const struct smia_mode *mode = &sensor->current_reglist->mode;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MODE_FRAME_WIDTH:
+		ctrl->cur.val = mode->width;
+		break;
+	case V4L2_CID_MODE_FRAME_HEIGHT:
+		ctrl->cur.val = mode->height;
+		break;
+	case V4L2_CID_MODE_VISIBLE_WIDTH:
+		ctrl->cur.val = mode->window_width;
+		break;
+	case V4L2_CID_MODE_VISIBLE_HEIGHT:
+		ctrl->cur.val = mode->window_height;
+		break;
+	case V4L2_CID_MODE_PIXELCLOCK:
+		ctrl->cur.val = mode->pixel_clock;
+		break;
+	case V4L2_CID_MODE_SENSITIVITY:
+		ctrl->cur.val = mode->sensitivity;
+		break;
+	case V4L2_CID_MODE_OPSYSCLOCK:
+		ctrl->cur.val = mode->opsys_clock;
+		break;
+	}
+
+	return 0;
+}
+
+static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct et8ek8_sensor *sensor =
+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	int uninitialized_var(rows);
+
+	if (ctrl->id == V4L2_CID_EXPOSURE)
+		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		return et8ek8_set_gain(sensor, ctrl->val);
+
+	case V4L2_CID_EXPOSURE:
+		return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
+					  swab16(rows));
+
+	case V4L2_CID_TEST_PATTERN:
+		return et8ek8_set_test_pattern(sensor, ctrl->val);
+
+	case V4L2_CID_PIXEL_RATE:
+		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
+	.g_volatile_ctrl = et8ek8_get_ctrl,
+	.s_ctrl = et8ek8_set_ctrl,
+};
+
+static const char *et8ek8_test_pattern_menu[] = {
+	"Normal",
+	"Vertical colorbar",
+	"Horizontal colorbar",
+	"Scale",
+	"Ramp",
+	"Small vertical colorbar",
+	"Small horizontal colorbar",
+	"Small scale",
+	"Small ramp",
+};
+
+static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_TEST_PATTERN,
+		.type		= V4L2_CTRL_TYPE_MENU,
+		.name		= "Test pattern mode",
+		.min		= 0,
+		.max		= ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
+		.step		= 0,
+		.def		= 0,
+		.flags		= 0,
+		.qmenu		= et8ek8_test_pattern_menu,
+	},
+	{
+		.id		= V4L2_CID_MODE_CLASS,
+		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
+		.name		= "SMIA-type sensor information",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				| V4L2_CTRL_FLAG_WRITE_ONLY,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_FRAME_WIDTH,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Frame width",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_FRAME_HEIGHT,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Frame height",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_VISIBLE_WIDTH,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Visible width",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_VISIBLE_HEIGHT,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Visible height",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_PIXELCLOCK,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Pixel clock [Hz]",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_SENSITIVITY,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Sensivity",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= V4L2_CID_MODE_OPSYSCLOCK,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Output pixel clock [Hz]",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
+				  | V4L2_CTRL_FLAG_VOLATILE,
+	},
+};
+
+static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
+{
+	unsigned int i;
+	u32 min, max;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
+			       ARRAY_SIZE(et8ek8_ctrls) + 2);
+
+	/* V4L2_CID_GAIN */
+	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
+			  1, 0);
+
+	/* V4L2_CID_EXPOSURE */
+	min = et8ek8_exposure_rows_to_us(sensor, 1);
+	max = et8ek8_exposure_rows_to_us(sensor,
+				sensor->current_reglist->mode.max_exp);
+	sensor->exposure =
+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+				  V4L2_CID_EXPOSURE, min, max, min, max);
+	sensor->pixel_rate =
+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
+
+	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
+	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
+		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
+				     NULL);
+
+	if (sensor->ctrl_handler.error)
+		return sensor->ctrl_handler.error;
+
+	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
+	return 0;
+}
+
+static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_ctrl *ctrl = sensor->exposure;
+	u32 min, max;
+
+	min = et8ek8_exposure_rows_to_us(sensor, 1);
+	max = et8ek8_exposure_rows_to_us(sensor,
+					 sensor->current_reglist->mode.max_exp);
+
+	v4l2_ctrl_lock(ctrl);
+	ctrl->minimum = min;
+	ctrl->maximum = max;
+	ctrl->step = min;
+	ctrl->default_value = max;
+	ctrl->val = max;
+	ctrl->cur.val = max;
+	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
+				 sensor->current_reglist->mode.ext_clock);
+	v4l2_ctrl_unlock(ctrl);
+}
+
+static int et8ek8_configure(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_subdev *subdev = &sensor->subdev;
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int rval;
+
+	rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
+	if (rval)
+		goto fail;
+
+	/* Controls set while the power to the sensor is turned off are saved
+	 * but not applied to the hardware. Now that we're about to start
+	 * streaming apply all the current values to the hardware.
+	 */
+	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
+	if (rval)
+		goto fail;
+
+	return 0;
+
+fail:
+	dev_err(&client->dev, "sensor configuration failed\n");
+	return rval;
+}
+
+static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
+}
+
+static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
+}
+
+static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	int ret;
+
+	if (!streaming)
+		return et8ek8_stream_off(sensor);
+
+	ret = et8ek8_configure(sensor);
+	if (ret < 0)
+		return ret;
+
+	return et8ek8_stream_on(sensor);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int et8ek8_power_off(struct et8ek8_sensor *sensor)
+{
+	int rval;
+
+	gpiod_set_value(sensor->reset, 0);
+	udelay(1);
+
+	clk_disable_unprepare(sensor->ext_clk);
+
+	rval = regulator_disable(sensor->vana);
+	return rval;
+}
+
+static int et8ek8_power_on(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_subdev *subdev = &sensor->subdev;
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	unsigned int hz = ET8EK8_XCLK_HZ;
+	int val, rval;
+
+	rval = regulator_enable(sensor->vana);
+	if (rval) {
+		dev_err(&client->dev, "failed to enable vana regulator\n");
+		return rval;
+	}
+
+	if (sensor->current_reglist)
+		hz = sensor->current_reglist->mode.ext_clock;
+
+	rval = clk_set_rate(sensor->ext_clk, hz);
+	if (rval < 0) {
+		dev_err(&client->dev,
+			"unable to set extclk clock freq to %u\n", hz);
+		goto out;
+	}
+	rval = clk_prepare_enable(sensor->ext_clk);
+	if (rval < 0) {
+		dev_err(&client->dev, "failed to enable extclk\n");
+		goto out;
+	}
+
+	if (rval)
+		goto out;
+
+	udelay(10);			/* I wish this is a good value */
+
+	gpiod_set_value(sensor->reset, 1);
+
+	msleep(5000*1000/hz+1);				/* Wait 5000 cycles */
+
+	rval = smia_i2c_reglist_find_write(client,
+					   &et8ek8_smia_meta_reglist,
+					   SMIA_REGLIST_POWERON);
+	if (rval)
+		goto out;
+
+#ifdef USE_CRC
+	rval = smia_i2c_read_reg(client,
+				 SMIA_REG_8BIT, 0x1263, &val);
+	if (rval)
+		goto out;
+#if USE_CRC
+	val |= (1<<4);
+#else
+	val &= ~(1<<4);
+#endif
+	rval = smia_i2c_write_reg(client,
+				  SMIA_REG_8BIT, 0x1263, val);
+	if (rval)
+		goto out;
+#endif
+
+out:
+	if (rval)
+		et8ek8_power_off(sensor);
+
+	return rval;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist, code);
+}
+
+static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist, fse);
+}
+
+static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_interval_enum *fie)
+{
+	return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist, fie);
+}
+
+static struct v4l2_mbus_framefmt *
+__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
+			struct v4l2_subdev_pad_config *cfg,
+			unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &sensor->format;
+	default: 
+		return NULL;
+	}
+}
+
+static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct v4l2_mbus_framefmt *format;
+ 
+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct v4l2_mbus_framefmt *format;
+        struct smia_reglist *reglist;
+
+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
+					     &fmt->format);
+	smia_reglist_to_mbus(reglist, &fmt->format);
+	*format = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		sensor->current_reglist = reglist;
+		et8ek8_update_controls(sensor);
+	}
+
+	return 0;
+}
+
+static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	memset(fi, 0, sizeof(*fi));
+	fi->interval = sensor->current_reglist->mode.timeperframe;
+
+	return 0;
+}
+
+static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct smia_reglist *reglist;
+
+	reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
+					      sensor->current_reglist,
+					      &fi->interval);
+
+	if (!reglist)
+		return -EINVAL;
+
+	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
+		return -EINVAL;
+
+	sensor->current_reglist = reglist;
+	et8ek8_update_controls(sensor);
+
+	return 0;
+}
+
+static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
+	unsigned int offset = 0;
+	u8 *ptr  = sensor->priv_mem;
+	int rval = 0;
+
+	/* Read the EEPROM window-by-window, each window 8 bytes */
+	do {
+		u8 buffer[PRIV_MEM_WIN_SIZE];
+		struct i2c_msg msg;
+		int bytes, i;
+		int ofs;
+
+		/* Set the current window */
+		rval = smia_i2c_write_reg(client,
+					  SMIA_REG_8BIT,
+					  0x0001,
+					  0xe0 | (offset >> 3));
+		if (rval < 0)
+			goto out;
+
+		/* Wait for status bit */
+		for (i = 0; i < 1000; ++i) {
+			u32 status;
+			rval = smia_i2c_read_reg(client,
+						 SMIA_REG_8BIT,
+						 0x0003,
+						 &status);
+			if (rval < 0)
+				goto out;
+			if ((status & 0x08) == 0)
+				break;
+			msleep(1);
+		};
+
+		if (i == 1000) {
+			rval = -EIO;
+			goto out;
+		}
+
+		/* Read window, 8 bytes at once, and copy to user space */
+		ofs = offset & 0x07;	/* Offset within this window */
+		bytes = length + ofs > 8 ? 8-ofs : length;
+		msg.addr = client->addr;
+		msg.flags = 0;
+		msg.len = 2;
+		msg.buf = buffer;
+		ofs += PRIV_MEM_START_REG;
+		buffer[0] = (u8)(ofs >> 8);
+		buffer[1] = (u8)(ofs & 0xFF);
+		rval = i2c_transfer(client->adapter, &msg, 1);
+		if (rval < 0)
+			goto out;
+		mdelay(ET8EK8_I2C_DELAY);
+		msg.addr = client->addr;
+		msg.len = bytes;
+		msg.flags = I2C_M_RD;
+		msg.buf = buffer;
+		memset(buffer, 0, sizeof(buffer));
+		rval = i2c_transfer(client->adapter, &msg, 1);
+		if (rval < 0)
+			goto out;
+		rval = 0;
+		memcpy(ptr, buffer, bytes);
+
+		length -= bytes;
+		offset += bytes;
+		ptr    += bytes;
+	} while (length > 0);
+
+out:
+	return rval;
+}
+
+static int et8ek8_dev_init(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int rval, rev_l, rev_h;
+
+	rval = et8ek8_power_on(sensor);
+	if (rval) {
+		dev_err(&client->dev, "could not power on\n");
+		return rval;
+	}
+
+	if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
+			      REG_REVISION_NUMBER_L, &rev_l) != 0
+	    || smia_i2c_read_reg(client, SMIA_REG_8BIT,
+				 REG_REVISION_NUMBER_H, &rev_h) != 0) {
+		dev_err(&client->dev,
+			"no et8ek8 sensor detected\n");
+		rval = -ENODEV;
+		goto out_poweroff;
+	}
+	sensor->version = (rev_h << 8) + rev_l;
+	if (sensor->version != ET8EK8_REV_1
+	    && sensor->version != ET8EK8_REV_2)
+		dev_info(&client->dev,
+			 "unknown version 0x%x detected, "
+			 "continuing anyway\n", sensor->version);
+
+	rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
+	if (rval) {
+		dev_err(&client->dev,
+			"invalid register list %s, import failed\n",
+			ET8EK8_NAME);
+		goto out_poweroff;
+	}
+
+	sensor->current_reglist =
+		smia_reglist_find_type(&et8ek8_smia_meta_reglist,
+				       SMIA_REGLIST_MODE);
+	if (!sensor->current_reglist) {
+		dev_err(&client->dev,
+			"invalid register list %s, no mode found\n",
+			ET8EK8_NAME);
+		rval = -ENODEV;
+		goto out_poweroff;
+	}
+
+	smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
+
+	rval = smia_i2c_reglist_find_write(client,
+					   &et8ek8_smia_meta_reglist,
+					   SMIA_REGLIST_POWERON);
+	if (rval) {
+		dev_err(&client->dev,
+			"invalid register list %s, no POWERON mode found\n",
+			ET8EK8_NAME);
+		goto out_poweroff;
+	}
+	rval = et8ek8_stream_on(sensor);	/* Needed to be able to read EEPROM */
+	if (rval)
+		goto out_poweroff;
+	rval = et8ek8_g_priv_mem(subdev);
+	if (rval)
+		dev_warn(&client->dev,
+			"can not read OTP (EEPROM) memory from sensor\n");
+	rval = et8ek8_stream_off(sensor);
+	if (rval)
+		goto out_poweroff;
+
+	rval = et8ek8_power_off(sensor);
+	if (rval)
+		goto out_poweroff;
+
+	return 0;
+
+out_poweroff:
+	et8ek8_power_off(sensor);
+
+	return rval;
+}
+
+/* --------------------------------------------------------------------------
+ * sysfs attributes
+ */
+static ssize_t
+et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
+		     char *buf)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
+#error PAGE_SIZE too small!
+#endif
+
+	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
+
+	return ET8EK8_PRIV_MEM_SIZE;
+}
+static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int
+et8ek8_registered(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	struct v4l2_mbus_framefmt *format;
+	int rval;
+
+	dev_dbg(&client->dev, "registered!");
+
+	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
+		dev_err(&client->dev, "could not register sysfs entry\n");
+		return -EBUSY;
+	}
+
+	rval = et8ek8_dev_init(subdev);
+	if (rval)
+		return rval;
+
+	rval = et8ek8_init_controls(sensor);
+	if (rval) {
+		dev_err(&client->dev, "controls initialization failed\n");
+		return rval;
+	}
+
+	format = __et8ek8_get_pad_format(sensor, NULL, 0,
+					 V4L2_SUBDEV_FORMAT_ACTIVE);
+	return 0;
+}
+
+static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
+{
+	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
+}
+
+static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	int ret = 0;
+
+	mutex_lock(&sensor->power_lock);
+
+	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
+	 * update the power state.
+	 */
+	if (sensor->power_count == !on) {
+		ret = __et8ek8_set_power(sensor, !!on);
+		if (ret < 0)
+			goto done;
+	}
+
+	/* Update the power count. */
+	sensor->power_count += on ? 1 : -1;
+	WARN_ON(sensor->power_count < 0);
+
+done:
+	mutex_unlock(&sensor->power_lock);
+	return ret;
+}
+
+static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
+	struct v4l2_mbus_framefmt *format;
+	struct smia_reglist *reglist;
+
+	reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
+					 SMIA_REGLIST_MODE);
+	format = __et8ek8_get_pad_format(sensor, fh->pad, 0, V4L2_SUBDEV_FORMAT_TRY);
+	smia_reglist_to_mbus(reglist, format);
+
+	return et8ek8_set_power(sd, true);
+}
+
+static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return et8ek8_set_power(sd, false);
+}
+
+static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
+	.s_stream = et8ek8_s_stream,
+	.g_frame_interval = et8ek8_get_frame_interval,
+	.s_frame_interval = et8ek8_set_frame_interval,
+};
+
+static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
+	.s_power = et8ek8_set_power,
+};
+
+static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
+	.enum_mbus_code = et8ek8_enum_mbus_code,
+        .enum_frame_size = et8ek8_enum_frame_size,
+        .enum_frame_interval = et8ek8_enum_frame_ival,
+	.get_fmt = et8ek8_get_pad_format,
+	.set_fmt = et8ek8_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops et8ek8_ops = {
+	.core = &et8ek8_core_ops,
+	.video = &et8ek8_video_ops,
+	.pad = &et8ek8_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
+	.registered = et8ek8_registered,
+	.open = et8ek8_open,
+	.close = et8ek8_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int et8ek8_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (!sensor->power_count)
+		return 0;
+
+	return __et8ek8_set_power(sensor, false);
+}
+
+static int et8ek8_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (!sensor->power_count)
+		return 0;
+
+	return __et8ek8_set_power(sensor, true);
+}
+
+static struct dev_pm_ops et8ek8_pm_ops = {
+	.suspend	= et8ek8_suspend,
+	.resume		= et8ek8_resume,
+};
+
+#else
+
+#define et8ek8_pm_ops	NULL
+
+#endif /* CONFIG_PM */
+
+static int et8ek8_probe(struct i2c_client *client,
+			const struct i2c_device_id *devid)
+{
+	struct et8ek8_sensor *sensor;
+	int ret;
+
+	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(sensor->reset)) {
+		dev_dbg(&client->dev, "could not request reset gpio\n");
+		return PTR_ERR(sensor->reset);;
+	}
+
+	sensor->vana = devm_regulator_get(&client->dev, "vana");
+	if (IS_ERR(sensor->vana)) {
+		dev_err(&client->dev, "could not get regulator for vana\n");
+		return PTR_ERR(sensor->vana);
+	}
+
+	sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
+	if (IS_ERR(sensor->ext_clk)) {
+		dev_err(&client->dev, "could not get clock\n");
+		return PTR_ERR(sensor->ext_clk);
+	}
+
+	mutex_init(&sensor->power_lock);
+
+	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
+	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->subdev.internal_ops = &et8ek8_internal_ops;
+
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+	if (ret < 0) {
+		dev_err(&client->dev, "media entity init failed!\n");
+		return ret;
+	}
+
+	ret = v4l2_async_register_subdev(&sensor->subdev);
+	if (ret < 0) {
+		media_entity_cleanup(&sensor->subdev.entity);
+		return ret;
+	}
+
+	dev_dbg(&client->dev, "initialized!\n");
+
+	return 0;
+}
+
+static int __exit et8ek8_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (sensor->power_count) {
+		gpiod_set_value(sensor->reset, 0);
+		clk_disable_unprepare(sensor->ext_clk);
+		sensor->power_count = 0;
+	}
+
+	v4l2_device_unregister_subdev(&sensor->subdev);
+	device_remove_file(&client->dev, &dev_attr_priv_mem);
+	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
+	media_entity_cleanup(&sensor->subdev.entity);
+
+	return 0;
+}
+
+static const struct of_device_id et8ek8_of_table[] = {
+	{ .compatible = "toshiba,et8ek8" },
+	{ },
+};
+
+static const struct i2c_device_id et8ek8_id_table[] = {
+	{ ET8EK8_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
+
+static struct i2c_driver et8ek8_i2c_driver = {
+	.driver		= {
+		.name	= ET8EK8_NAME,
+		.pm	= &et8ek8_pm_ops,
+		.of_match_table	= et8ek8_of_table,
+	},
+	.probe		= et8ek8_probe,
+	.remove		= __exit_p(et8ek8_remove),
+	.id_table	= et8ek8_id_table,
+};
+
+module_i2c_driver(et8ek8_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
+MODULE_LICENSE("GPL");
-- 
1.9.1


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

* [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (2 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-05-01 10:45     ` Sakari Ailus
  2016-05-24 10:17     ` Pavel Machek
  2016-04-24 21:08   ` [RFC PATCH 05/24] smiapp: Add smiapp_has_quirk() to tell whether a quirk is implemented Ivaylo Dimitrov
                     ` (25 subsequent siblings)
  29 siblings, 2 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

Required added multiplier (and divisor) calculation did not take into
account the existing divisor when checking the values against the minimum
divisor. Do just that.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/i2c/smiapp-pll.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
index e3348db..5ad1edb 100644
--- a/drivers/media/i2c/smiapp-pll.c
+++ b/drivers/media/i2c/smiapp-pll.c
@@ -227,7 +227,8 @@ static int __smiapp_pll_calculate(
 
 	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
 	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
-	more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
+	more_mul_factor = lcm(more_mul_factor,
+			      DIV_ROUND_UP(op_limits->min_sys_clk_div, div));
 	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
 		more_mul_factor);
 	i = roundup(more_mul_min, more_mul_factor);
-- 
1.9.1


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

* [RFC PATCH 05/24] smiapp: Add smiapp_has_quirk() to tell whether a quirk is implemented
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (3 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 06/24] smiapp: Add quirk control support Ivaylo Dimitrov
                     ` (24 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/i2c/smiapp/smiapp-quirk.h | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
index dac5566..209818f 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.h
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -72,9 +72,12 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor,
 		.val = _val,		\
 	}
 
+#define smiapp_has_quirk(sensor, _quirk)	\
+	((sensor)->minfo.quirk &&		\
+	 (sensor)->minfo.quirk->_quirk)
+
 #define smiapp_call_quirk(sensor, _quirk, ...)				\
-	((sensor)->minfo.quirk &&					\
-	 (sensor)->minfo.quirk->_quirk ?				\
+	(smiapp_has_quirk(sensor, _quirk) ?				\
 	 (sensor)->minfo.quirk->_quirk(sensor, ##__VA_ARGS__) : 0)
 
 #define smiapp_needs_quirk(sensor, _quirk)		\
-- 
1.9.1


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

* [RFC PATCH 06/24] smiapp: Add quirk control support
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (4 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 05/24] smiapp: Add smiapp_has_quirk() to tell whether a quirk is implemented Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-05-01 10:46     ` Sakari Ailus
  2016-04-24 21:08   ` [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi Ivaylo Dimitrov
                     ` (23 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Sakari Ailus

From: Sakari Ailus <sakari.ailus@linux.intel.com>

Quirk controls can be set up in the init quirk.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/smiapp/smiapp-core.c  | 6 ++++++
 drivers/media/i2c/smiapp/smiapp-quirk.h | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 3dfe387..c6a897b 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -412,6 +412,12 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
 	int exposure;
 	int rval;
 
+	rval = smiapp_call_quirk(sensor, s_ctrl, ctrl);
+	if (rval < 0)
+		return rval;
+	if (rval > 0)
+		return 0;
+
 	switch (ctrl->id) {
 	case V4L2_CID_ANALOGUE_GAIN:
 		return smiapp_write(
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
index 209818f..504c16a 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.h
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -32,6 +32,8 @@ struct smiapp_sensor;
  * @pll_flags: Return flags for the PLL calculator.
  * @init: Quirk initialisation, called the last in probe(). This is
  *	  also appropriate for adding sensor specific controls, for instance.
+ * @s_ctrl: Set control quirk. Returns 0 if the control isn't
+ *	    implemented by the quirk, > 0 if it is.
  * @reg_access: Register access quirk. The quirk may divert the access
  *		to another register, or no register at all.
  *
@@ -51,6 +53,7 @@ struct smiapp_quirk {
 	int (*post_streamoff)(struct smiapp_sensor *sensor);
 	unsigned long (*pll_flags)(struct smiapp_sensor *sensor);
 	int (*init)(struct smiapp_sensor *sensor);
+	int (*s_ctrl)(struct smiapp_sensor *sensor, struct v4l2_ctrl *ctrl);
 	int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg,
 			  u32 *val);
 	unsigned long flags;
-- 
1.9.1


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

* [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (5 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 06/24] smiapp: Add quirk control support Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-29 13:22     ` Pavel Machek
  2016-04-24 21:08   ` [RFC PATCH 08/24] v4l: of: Obtain data bus type from bus-type property Ivaylo Dimitrov
                     ` (22 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

The function to parse CSI2 bus parameters was called
v4l2_of_parse_csi_bus(), rename it as v4l2_of_parse_csi2_bus() in
anticipation of CSI1/CCP2 support.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/v4l2-core/v4l2-of.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index 93b3368..f2618fd 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -20,7 +20,7 @@
 
 #include <media/v4l2-of.h>
 
-static int v4l2_of_parse_csi_bus(const struct device_node *node,
+static int v4l2_of_parse_csi2_bus(const struct device_node *node,
 				 struct v4l2_of_endpoint *endpoint)
 {
 	struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2;
@@ -158,7 +158,7 @@ int v4l2_of_parse_endpoint(const struct device_node *node,
 	memset(&endpoint->bus_type, 0, sizeof(*endpoint) -
 	       offsetof(typeof(*endpoint), bus_type));
 
-	rval = v4l2_of_parse_csi_bus(node, endpoint);
+	rval = v4l2_of_parse_csi2_bus(node, endpoint);
 	if (rval)
 		return rval;
 	/*
-- 
1.9.1


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

* [RFC PATCH 08/24] v4l: of: Obtain data bus type from bus-type property
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (6 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type Ivaylo Dimitrov
                     ` (21 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

Only try parsing bus specific properties in this case.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/v4l2-core/v4l2-of.c | 42 +++++++++++++++++++++++++++++----------
 1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index f2618fd..5304137 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -20,6 +20,11 @@
 
 #include <media/v4l2-of.h>
 
+enum v4l2_of_bus_type {
+	V4L2_OF_BUS_TYPE_CSI2 = 0,
+	V4L2_OF_BUS_TYPE_PARALLEL,
+};
+
 static int v4l2_of_parse_csi2_bus(const struct device_node *node,
 				 struct v4l2_of_endpoint *endpoint)
 {
@@ -151,6 +156,7 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node,
 int v4l2_of_parse_endpoint(const struct device_node *node,
 			   struct v4l2_of_endpoint *endpoint)
 {
+	u32 bus_type;
 	int rval;
 
 	of_graph_parse_endpoint(node, &endpoint->base);
@@ -158,17 +164,33 @@ int v4l2_of_parse_endpoint(const struct device_node *node,
 	memset(&endpoint->bus_type, 0, sizeof(*endpoint) -
 	       offsetof(typeof(*endpoint), bus_type));
 
-	rval = v4l2_of_parse_csi2_bus(node, endpoint);
-	if (rval)
-		return rval;
-	/*
-	 * Parse the parallel video bus properties only if none
-	 * of the MIPI CSI-2 specific properties were found.
-	 */
-	if (endpoint->bus.mipi_csi2.flags == 0)
-		v4l2_of_parse_parallel_bus(node, endpoint);
+	rval = of_property_read_u32(node, "bus-type", &bus_type);
+	if (rval < 0) {
+		endpoint->bus_type = 0;
+		rval = v4l2_of_parse_csi2_bus(node, endpoint);
+		if (rval)
+			return rval;
+		/*
+		 * Parse the parallel video bus properties only if none
+		 * of the MIPI CSI-2 specific properties were found.
+		 */
+		if (endpoint->bus.mipi_csi2.flags == 0)
+			v4l2_of_parse_parallel_bus(node, endpoint);
+
+		return 0;
+	}
 
-	return 0;
+	switch (bus_type) {
+	case V4L2_OF_BUS_TYPE_CSI2:
+		return v4l2_of_parse_csi2_bus(node, endpoint);
+	case V4L2_OF_BUS_TYPE_PARALLEL:
+		v4l2_of_parse_parallel_bus(node, endpoint);
+		return 0;
+	default:
+		pr_warn("bad bus-type %u, device_node \"%s\"\n",
+			bus_type, node->full_name);
+		return -EINVAL;
+	}
 }
 EXPORT_SYMBOL(v4l2_of_parse_endpoint);
 
-- 
1.9.1


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

* [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (7 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 08/24] v4l: of: Obtain data bus type from bus-type property Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-29 13:27     ` Pavel Machek
  2016-04-24 21:08   ` [RFC PATCH 10/24] v4l: of: Separate lane parsing from CSI-2 bus parameter parsing Ivaylo Dimitrov
                     ` (20 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

CCP2, or CSI-1, is an older single data lane serial bus.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 include/media/v4l2-mediabus.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index 34cc99e..315c167 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -69,11 +69,15 @@
  * @V4L2_MBUS_PARALLEL:	parallel interface with hsync and vsync
  * @V4L2_MBUS_BT656:	parallel interface with embedded synchronisation, can
  *			also be used for BT.1120
+ * @V4L2_MBUS_CSI1:	MIPI CSI-1 serial interface
+ * @V4L2_MBUS_CCP2:	CCP2 (Compact Camera Port 2)
  * @V4L2_MBUS_CSI2:	MIPI CSI-2 serial interface
  */
 enum v4l2_mbus_type {
 	V4L2_MBUS_PARALLEL,
 	V4L2_MBUS_BT656,
+	V4L2_MBUS_CSI1,
+	V4L2_MBUS_CCP2,
 	V4L2_MBUS_CSI2,
 };
 
-- 
1.9.1


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

* [RFC PATCH 10/24] v4l: of: Separate lane parsing from CSI-2 bus parameter parsing
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (8 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property Ivaylo Dimitrov
                     ` (19 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

The CSI-1 will need these as well, separate them into a different function.

have_clk_lane and num_data_lanes arguments may be NULL; the CSI-1 bus will
have no use for them.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/v4l2-core/v4l2-of.c | 60 +++++++++++++++++++++++++++++----------
 1 file changed, 45 insertions(+), 15 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index 5304137..60bbc5f 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -25,53 +25,83 @@ enum v4l2_of_bus_type {
 	V4L2_OF_BUS_TYPE_PARALLEL,
 };
 
-static int v4l2_of_parse_csi2_bus(const struct device_node *node,
-				 struct v4l2_of_endpoint *endpoint)
+static int v4l2_of_parse_lanes(const struct device_node *node,
+			       unsigned char *clock_lane,
+			       bool *have_clk_lane,
+			       unsigned char *data_lanes,
+			       bool *lane_polarities,
+			       unsigned short *__num_data_lanes,
+			       unsigned int max_data_lanes)
 {
-	struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2;
 	struct property *prop;
-	bool have_clk_lane = false;
-	unsigned int flags = 0;
+	unsigned short num_data_lanes = 0;
 	u32 v;
 
 	prop = of_find_property(node, "data-lanes", NULL);
 	if (prop) {
 		const __be32 *lane = NULL;
-		unsigned int i;
 
-		for (i = 0; i < ARRAY_SIZE(bus->data_lanes); i++) {
+		for (num_data_lanes = 0; num_data_lanes < max_data_lanes;
+		     num_data_lanes++) {
 			lane = of_prop_next_u32(prop, lane, &v);
 			if (!lane)
 				break;
-			bus->data_lanes[i] = v;
+			data_lanes[num_data_lanes] = v;
 		}
-		bus->num_data_lanes = i;
 	}
+	if (__num_data_lanes)
+		*__num_data_lanes = num_data_lanes;
 
 	prop = of_find_property(node, "lane-polarities", NULL);
 	if (prop) {
 		const __be32 *polarity = NULL;
 		unsigned int i;
 
-		for (i = 0; i < ARRAY_SIZE(bus->lane_polarities); i++) {
+		for (i = 0; i < 1 + max_data_lanes; i++) {
 			polarity = of_prop_next_u32(prop, polarity, &v);
 			if (!polarity)
 				break;
-			bus->lane_polarities[i] = v;
+			lane_polarities[i] = v;
 		}
 
-		if (i < 1 + bus->num_data_lanes /* clock + data */) {
+		if (i < 1 + num_data_lanes /* clock + data */) {
 			pr_warn("%s: too few lane-polarities entries (need %u, got %u)\n",
-				node->full_name, 1 + bus->num_data_lanes, i);
+				node->full_name, 1 + num_data_lanes, i);
 			return -EINVAL;
 		}
 	}
 
+	if (have_clk_lane)
+		*have_clk_lane = false;
+
 	if (!of_property_read_u32(node, "clock-lanes", &v)) {
-		bus->clock_lane = v;
-		have_clk_lane = true;
+		*clock_lane = v;
+		if (have_clk_lane)
+			*have_clk_lane = true;
 	}
 
+	return 0;
+}
+
+static int v4l2_of_parse_csi2_bus(const struct device_node *node,
+				 struct v4l2_of_endpoint *endpoint)
+{
+	struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2;
+	bool have_clk_lane = false;
+	unsigned int flags = 0;
+	int rval;
+	u32 v;
+
+	rval = v4l2_of_parse_lanes(node, &bus->clock_lane, &have_clk_lane,
+				   bus->data_lanes, bus->lane_polarities,
+				   &bus->num_data_lanes,
+				   ARRAY_SIZE(bus->data_lanes));
+	if (rval)
+		return rval;
+
+	BUILD_BUG_ON(1 + ARRAY_SIZE(bus->data_lanes)
+		       != ARRAY_SIZE(bus->lane_polarities));
+
 	if (of_get_property(node, "clock-noncontinuous", &v))
 		flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
 	else if (have_clk_lane || bus->num_data_lanes > 0)
-- 
1.9.1


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

* [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (9 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 10/24] v4l: of: Separate lane parsing from CSI-2 bus parameter parsing Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-29 13:28     ` Pavel Machek
  2016-04-24 21:08   ` [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt Ivaylo Dimitrov
                     ` (18 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

In the vast majority of cases the bus type is known to the driver(s) since
a receiver or transmitter can only support a single one. There are cases
however where different options are possible.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 Documentation/devicetree/bindings/media/video-interfaces.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
index 9cd2a36..f5b61bd 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
@@ -76,6 +76,10 @@ Optional endpoint properties
   mode horizontal and vertical synchronization signals are provided to the
   slave device (data source) by the master device (data sink). In the master
   mode the data source device is also the source of the synchronization signals.
+- bus-type: data bus type. Possible values are:
+  0 - CSI2
+  1 - parallel / Bt656
+  2 - CCP2
 - bus-width: number of data lines actively used, valid for the parallel busses.
 - data-shift: on the parallel data busses, if bus-width is used to specify the
   number of data lines, data-shift can be used to specify which data lines are
-- 
1.9.1


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

* [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (10 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-29 13:39     ` Pavel Machek
  2016-04-24 21:08   ` [RFC PATCH 13/24] v4l: of: Support CSI-1 and CCP2 busses Ivaylo Dimitrov
                     ` (17 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

Document the CSI1/CCP2 properties strobe_clk_inv and strobe_clock
properties. The former tells whether the strobe/clock signal is inverted,
while the latter signifies the clock or strobe mode.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 Documentation/devicetree/bindings/media/video-interfaces.txt | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
index f5b61bd..f0523f7 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
@@ -114,9 +114,10 @@ Optional endpoint properties
   lane and followed by the data lanes in the same order as in data-lanes.
   Valid values are 0 (normal) and 1 (inverted). The length of the array
   should be the combined length of data-lanes and clock-lanes properties.
-  If the lane-polarities property is omitted, the value must be interpreted
-  as 0 (normal). This property is valid for serial busses only.
-
+- clock-inv: Clock or strobe signal inversion.
+  Possible values: 0 -- not inverted; 1 -- inverted
+- strobe: Whether the clock signal is used as clock or strobe. Used
+  with CCP2, for instance.
 
 Example
 -------
-- 
1.9.1


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

* [RFC PATCH 13/24] v4l: of: Support CSI-1 and CCP2 busses
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (11 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 14/24] media: et8ek8: add device tree binding document Ivaylo Dimitrov
                     ` (16 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Add support for parsing of CSI-1 and CCP2 bus related properties documented
in video-interfaces.txt.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/v4l2-core/v4l2-of.c | 35 +++++++++++++++++++++++++++++++++++
 include/media/v4l2-of.h           | 17 +++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index 60bbc5f..5c0d0eb 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -23,6 +23,8 @@
 enum v4l2_of_bus_type {
 	V4L2_OF_BUS_TYPE_CSI2 = 0,
 	V4L2_OF_BUS_TYPE_PARALLEL,
+	V4L2_OF_BUS_TYPE_CSI1,
+	V4L2_OF_BUS_TYPE_CCP2,
 };
 
 static int v4l2_of_parse_lanes(const struct device_node *node,
@@ -163,6 +165,35 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node,
 
 }
 
+void v4l2_of_parse_csi1_bus(const struct device_node *node,
+			    struct v4l2_of_endpoint *endpoint,
+			    enum v4l2_of_bus_type bus_type)
+{
+	struct v4l2_of_bus_mipi_csi1 *bus = &endpoint->bus.mipi_csi1;
+	u32 v;
+
+	v4l2_of_parse_lanes(node, &bus->clock_lane, NULL,
+			    &bus->data_lane, bus->lane_polarity,
+			    NULL, 1);
+
+	if (!of_property_read_u32(node, "clock-inv", &v))
+		bus->clock_inv = v;
+
+	if (!of_property_read_u32(node, "strobe", &v))
+		bus->strobe = v;
+
+	if (!of_property_read_u32(node, "data-lane", &v))
+		bus->data_lane = v;
+
+	if (!of_property_read_u32(node, "clock-lane", &v))
+		bus->clock_lane = v;
+
+	if (bus_type == V4L2_OF_BUS_TYPE_CSI1)
+		endpoint->bus_type = V4L2_MBUS_CSI1;
+	else
+		endpoint->bus_type = V4L2_MBUS_CCP2;
+}
+
 /**
  * v4l2_of_parse_endpoint() - parse all endpoint node properties
  * @node: pointer to endpoint device_node
@@ -216,6 +247,10 @@ int v4l2_of_parse_endpoint(const struct device_node *node,
 	case V4L2_OF_BUS_TYPE_PARALLEL:
 		v4l2_of_parse_parallel_bus(node, endpoint);
 		return 0;
+	case V4L2_OF_BUS_TYPE_CSI1:
+	case V4L2_OF_BUS_TYPE_CCP2:
+		v4l2_of_parse_csi1_bus(node, endpoint, bus_type);
+		return 0;
 	default:
 		pr_warn("bad bus-type %u, device_node \"%s\"\n",
 			bus_type, node->full_name);
diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h
index 4dc34b2..63a52ee 100644
--- a/include/media/v4l2-of.h
+++ b/include/media/v4l2-of.h
@@ -53,6 +53,22 @@ struct v4l2_of_bus_parallel {
 };
 
 /**
+ * struct v4l2_of_bus_csi1 - CSI-1/CCP2 data bus structure
+ * @clock_inv: polarity of clock/strobe signal
+ *	       false - not inverted, true - inverted
+ * @strobe: false - data/clock, true - data/strobe
+ * @data_lane: the number of the data lane
+ * @clock_lane: the number of the clock lane
+ */
+struct v4l2_of_bus_mipi_csi1 {
+	bool clock_inv;
+	bool strobe;
+	bool lane_polarity[2];
+	unsigned char data_lane;
+	unsigned char clock_lane;
+};
+
+/**
  * struct v4l2_of_endpoint - the endpoint data structure
  * @base: struct of_endpoint containing port, id, and local of_node
  * @bus_type: bus type
@@ -66,6 +82,7 @@ struct v4l2_of_endpoint {
 	enum v4l2_mbus_type bus_type;
 	union {
 		struct v4l2_of_bus_parallel parallel;
+		struct v4l2_of_bus_mipi_csi1 mipi_csi1;
 		struct v4l2_of_bus_mipi_csi2 mipi_csi2;
 	} bus;
 	u64 *link_frequencies;
-- 
1.9.1


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

* [RFC PATCH 14/24] media: et8ek8: add device tree binding document
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (12 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 13/24] v4l: of: Support CSI-1 and CCP2 busses Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 15/24] media: add subdev type for bus switch Ivaylo Dimitrov
                     ` (15 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sebastian Reichel <sre@kernel.org>

Add the document for et8ek8 dt.
---
 .../bindings/media/i2c/toshiba,et8ek8.txt          | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
new file mode 100644
index 0000000..acb9290
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
@@ -0,0 +1,56 @@
+Toshiba ET8EK8 sensor
+
+Toshiba ET8EK8 is an image sensor used in the Nokia N900.
+
+Additional information about video interfaces in DT can be found in
+Documentation/devicetree/bindings/media/video-interfaces.txt .
+
+Mandatory properties
+--------------------
+
+- compatible: "toshiba,et8ek8"
+- reg: I2C address (0x3e, or an alternative address)
+- vana-supply: Analogue voltage supply (VANA), typically 2.8 volts (sensor
+  dependent).
+- clocks: External clock to the sensor
+- clock-frequency: Frequency of the external clock to the sensor
+
+Optional properties
+-------------------
+
+- reset-gpios: XSHUTDOWN GPIO
+
+
+Endpoint node mandatory properties
+----------------------------------
+
+- clock-lanes: <0>
+- data-lanes: <1>
+- remote-endpoint: A phandle to the bus receiver's endpoint node.
+
+Example
+-------
+
+&i2c2 {
+	clock-frequency = <400000>;
+
+	cam1: camera@3e {
+		compatible = "toshiba,et8ek8";
+		reg = <0x3e>;
+
+		vana-supply = <&vaux4>;
+
+		clocks = <&isp 0>;
+		clock-frequency = <9600000>;
+
+		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
+
+		port {
+			cam1_1: endpoint {
+				clock-lanes = <0>;
+				data-lanes = <1>;
+				remote-endpoint = <&isp_csi>;
+			};
+		};
+	};
+};
-- 
1.9.1


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

* [RFC PATCH 15/24] media: add subdev type for bus switch
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (13 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 14/24] media: et8ek8: add device tree binding document Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 16/24] media: video-bus-switch: new driver Ivaylo Dimitrov
                     ` (14 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sebastian Reichel <sre@kernel.org>

---
 include/uapi/linux/media.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index df59ede..244bea1 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -137,6 +137,7 @@ struct media_device_info {
  * MEDIA_ENT_F_IF_VID_DECODER and/or MEDIA_ENT_F_IF_AUD_DECODER.
  */
 #define MEDIA_ENT_F_TUNER		(MEDIA_ENT_F_OLD_SUBDEV_BASE + 5)
+#define MEDIA_ENT_F_SWITCH		(MEDIA_ENT_F_OLD_SUBDEV_BASE + 6)
 
 #define MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN	MEDIA_ENT_F_OLD_SUBDEV_BASE
 
-- 
1.9.1


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

* [RFC PATCH 16/24] media: video-bus-switch: new driver
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (14 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 15/24] media: add subdev type for bus switch Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 17/24] smiapp: add CCP2 support Ivaylo Dimitrov
                     ` (13 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/platform/Kconfig            |  10 +
 drivers/media/platform/Makefile           |   2 +
 drivers/media/platform/video-bus-switch.c | 366 ++++++++++++++++++++++++++++++
 3 files changed, 378 insertions(+)
 create mode 100644 drivers/media/platform/video-bus-switch.c

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 201f5c2..7c91fd2 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -91,6 +91,16 @@ config VIDEO_OMAP3_DEBUG
 	---help---
 	  Enable debug messages on OMAP 3 camera controller driver.
 
+config VIDEO_BUS_SWITCH
+	tristate "Video Bus switch"
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CONTROLLER
+	depends on OF
+	---help---
+	  Driver for a GPIO controlled video bus switch, which is used to
+	  connect two camera sensors to the same port a the image signal
+	  processor.
+
 config VIDEO_S3C_CAMIF
 	tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index bbb7bd1..c456917 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -10,6 +10,8 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
 
 obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
 
+obj-$(CONFIG_VIDEO_BUS_SWITCH) += video-bus-switch.o
+
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
diff --git a/drivers/media/platform/video-bus-switch.c b/drivers/media/platform/video-bus-switch.c
new file mode 100644
index 0000000..7c850fd
--- /dev/null
+++ b/drivers/media/platform/video-bus-switch.c
@@ -0,0 +1,366 @@
+/*
+ * Generic driver for video bus switches
+ *
+ * Copyright (C) 2015 Sebastian Reichel <sre@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/gpio/consumer.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+/*
+ * TODO:
+ * isp_subdev_notifier_complete() calls v4l2_device_register_subdev_nodes()
+ */
+
+#define CSI_SWITCH_SUBDEVS 2
+#define CSI_SWITCH_PORTS 3
+
+enum vbs_state {
+	CSI_SWITCH_DISABLED,
+	CSI_SWITCH_PORT_1,
+	CSI_SWITCH_PORT_2,
+};
+
+struct vbs_src_pads {
+	struct media_entity *src;
+	int src_pad;
+};
+
+struct vbs_data {
+	struct gpio_desc *swgpio;
+	struct v4l2_subdev subdev;
+	struct v4l2_async_notifier notifier;
+	struct media_pad pads[CSI_SWITCH_PORTS];
+	struct vbs_src_pads src_pads[CSI_SWITCH_PORTS];
+	enum vbs_state state;
+};
+
+struct vbs_async_subdev {
+	struct v4l2_subdev *sd;
+	struct v4l2_async_subdev asd;
+	u8 port;
+};
+
+static int vbs_of_parse_nodes(struct device *dev, struct vbs_data *pdata)
+{
+	struct v4l2_async_notifier *notifier = &pdata->notifier;
+	struct device_node *node = NULL;
+
+	notifier->subdevs = devm_kcalloc(dev, CSI_SWITCH_SUBDEVS,
+		sizeof(*notifier->subdevs), GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	notifier->num_subdevs = 0;
+	while (notifier->num_subdevs < CSI_SWITCH_SUBDEVS &&
+	       (node = of_graph_get_next_endpoint(dev->of_node, node))) {
+		struct v4l2_of_endpoint vep;
+		struct vbs_async_subdev *ssd;
+
+		/* skip first port (connected to isp) */
+		v4l2_of_parse_endpoint(node, &vep);
+		if (vep.base.port == 0) {
+			struct device_node *ispnode;
+
+			ispnode = of_graph_get_remote_port_parent(node);
+			if (!ispnode) {
+				dev_warn(dev, "bad remote port parent\n");
+				return -EINVAL;
+			}
+
+			of_node_put(node);
+			continue;
+		}
+
+		ssd = devm_kzalloc(dev, sizeof(*ssd), GFP_KERNEL);
+		if (!ssd) {
+			of_node_put(node);
+			return -ENOMEM;
+		}
+
+		ssd->port = vep.base.port;
+
+		notifier->subdevs[notifier->num_subdevs] = &ssd->asd;
+
+		ssd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+		of_node_put(node);
+		if (!ssd->asd.match.of.node) {
+			dev_warn(dev, "bad remote port parent\n");
+			return -EINVAL;
+		}
+
+		ssd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+		notifier->num_subdevs++;
+	}
+
+	return notifier->num_subdevs;
+}
+
+static int vbs_registered(struct v4l2_subdev *sd)
+{
+	struct v4l2_device *v4l2_dev = sd->v4l2_dev;
+	struct vbs_data *pdata;
+	int err;
+
+	dev_dbg(sd->dev, "registered, init notifier...\n");
+
+	pdata = v4l2_get_subdevdata(sd);
+
+	err = v4l2_async_notifier_register(v4l2_dev, &pdata->notifier);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static struct v4l2_subdev *vbs_get_remote_subdev(struct v4l2_subdev *sd)
+{
+	struct vbs_data *pdata = v4l2_get_subdevdata(sd);
+	struct media_entity *src;
+
+	if (pdata->state == CSI_SWITCH_DISABLED)
+		return ERR_PTR(-ENXIO);
+
+	src = pdata->src_pads[pdata->state].src;
+
+	return media_entity_to_v4l2_subdev(src);
+}
+
+static int vbs_link_setup(struct media_entity *entity,
+			  const struct media_pad *local,
+			  const struct media_pad *remote, u32 flags)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+	struct vbs_data *pdata = v4l2_get_subdevdata(sd);
+	bool enable = flags & MEDIA_LNK_FL_ENABLED;
+
+	if (local->index > CSI_SWITCH_PORTS - 1)
+		return -ENXIO;
+
+	/* no configuration needed on source port */
+	if (local->index == 0)
+		return 0;
+
+	if (!enable) {
+		if (local->index == pdata->state) {
+			pdata->state = CSI_SWITCH_DISABLED;
+
+			/* Make sure we have both cameras enabled */
+			gpiod_set_value(pdata->swgpio, 1);
+			return 0;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	/* there can only be one active sink at the same time */
+	if (pdata->state != CSI_SWITCH_DISABLED)
+		return -EBUSY;
+
+	gpiod_set_value(pdata->swgpio, local->index == CSI_SWITCH_PORT_2);
+	pdata->state = local->index;
+
+	sd = vbs_get_remote_subdev(sd);
+	if (IS_ERR(sd))
+		return PTR_ERR(sd);
+
+	pdata->subdev.ctrl_handler = sd->ctrl_handler;
+
+	return 0;
+}
+
+static int vbs_subdev_notifier_bound(struct v4l2_async_notifier *async,
+				     struct v4l2_subdev *subdev,
+				     struct v4l2_async_subdev *asd)
+{
+	struct vbs_data *pdata = container_of(async,
+		struct vbs_data, notifier);
+	struct vbs_async_subdev *ssd =
+		container_of(asd, struct vbs_async_subdev, asd);
+	struct media_entity *sink = &pdata->subdev.entity;
+	struct media_entity *src = &subdev->entity;
+	int sink_pad = ssd->port;
+	int src_pad;
+
+	if (sink_pad >= sink->num_pads) {
+		dev_err(pdata->subdev.dev, "no sink pad in internal entity!\n");
+		return -EINVAL;
+	}
+
+	for (src_pad = 0; src_pad < subdev->entity.num_pads; src_pad++) {
+		if (subdev->entity.pads[src_pad].flags & MEDIA_PAD_FL_SOURCE)
+			break;
+	}
+
+	if (src_pad >= src->num_pads) {
+		dev_err(pdata->subdev.dev, "no source pad in external entity\n");
+		return -EINVAL;
+	}
+
+	pdata->src_pads[sink_pad].src = src;
+	pdata->src_pads[sink_pad].src_pad = src_pad;
+	ssd->sd = subdev;
+
+	return 0;
+}
+
+static int vbs_subdev_notifier_complete(struct v4l2_async_notifier *async)
+{
+	struct vbs_data *pdata = container_of(async, struct vbs_data, notifier);
+	struct media_entity *sink = &pdata->subdev.entity;
+	int sink_pad;
+
+	for (sink_pad = 1; sink_pad < CSI_SWITCH_PORTS; sink_pad++) {
+		struct media_entity *src = pdata->src_pads[sink_pad].src;
+		int src_pad = pdata->src_pads[sink_pad].src_pad;
+		int err;
+
+		err = media_create_pad_link(src, src_pad, sink, sink_pad, 0);
+		if (err < 0)
+			return err;
+
+		dev_dbg(pdata->subdev.dev, "create link: %s[%d] -> %s[%d])\n",
+			src->name, src_pad, sink->name, sink_pad);
+	}
+
+	return v4l2_device_register_subdev_nodes(pdata->subdev.v4l2_dev);
+}
+
+static int vbs_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct v4l2_subdev *subdev = vbs_get_remote_subdev(sd);
+
+	if (IS_ERR(subdev))
+		return PTR_ERR(subdev);
+
+	return v4l2_subdev_call(subdev, video, s_stream, enable);
+}
+
+static const struct v4l2_subdev_internal_ops vbs_internal_ops = {
+	.registered = &vbs_registered,
+};
+
+static const struct media_entity_operations vbs_media_ops = {
+	.link_setup = vbs_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops vbs_video_ops = {
+	.s_stream = vbs_s_stream,
+};
+
+static const struct v4l2_subdev_ops vbs_ops = {
+	.video = &vbs_video_ops,
+};
+
+static int video_bus_switch_probe(struct platform_device *pdev)
+{
+	struct vbs_data *pdata;
+	int err = 0;
+
+	/* platform data */
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, pdata);
+
+	/* switch gpio */
+	pdata->swgpio = devm_gpiod_get(&pdev->dev, "switch", GPIOD_OUT_HIGH);
+	if (IS_ERR(pdata->swgpio)) {
+		err = PTR_ERR(pdata->swgpio);
+		dev_err(&pdev->dev, "Failed to request gpio: %d\n", err);
+		return err;
+	}
+
+	/* find sub-devices */
+	err = vbs_of_parse_nodes(&pdev->dev, pdata);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Failed to parse nodes: %d\n", err);
+		return err;
+	}
+
+	pdata->state = CSI_SWITCH_DISABLED;
+	pdata->notifier.bound = vbs_subdev_notifier_bound;
+	pdata->notifier.complete = vbs_subdev_notifier_complete;
+
+	/* setup subdev */
+	pdata->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+	pdata->pads[1].flags = MEDIA_PAD_FL_SINK;
+	pdata->pads[2].flags = MEDIA_PAD_FL_SINK;
+
+	v4l2_subdev_init(&pdata->subdev, &vbs_ops);
+	pdata->subdev.dev = &pdev->dev;
+	pdata->subdev.owner = pdev->dev.driver->owner;
+	strncpy(pdata->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
+	v4l2_set_subdevdata(&pdata->subdev, pdata);
+	pdata->subdev.entity.flags |= MEDIA_ENT_F_SWITCH;
+	pdata->subdev.entity.ops = &vbs_media_ops;
+	pdata->subdev.internal_ops = &vbs_internal_ops;
+	err = media_entity_pads_init(&pdata->subdev.entity, CSI_SWITCH_PORTS,
+				pdata->pads);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Failed to init media entity: %d\n", err);
+		return err;
+	}
+
+	/* register subdev */
+	err = v4l2_async_register_subdev(&pdata->subdev);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Failed to register v4l2 subdev: %d\n", err);
+		media_entity_cleanup(&pdata->subdev.entity);
+		return err;
+	}
+
+	return 0;
+}
+
+static int video_bus_switch_remove(struct platform_device *pdev)
+{
+	struct vbs_data *pdata = platform_get_drvdata(pdev);
+
+	v4l2_async_notifier_unregister(&pdata->notifier);
+	v4l2_async_unregister_subdev(&pdata->subdev);
+	media_entity_cleanup(&pdata->subdev.entity);
+
+	return 0;
+}
+
+static const struct of_device_id video_bus_switch_of_match[] = {
+	{ .compatible = "video-bus-switch" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, video_bus_switch_of_match);
+
+static struct platform_driver video_bus_switch_driver = {
+	.driver = {
+		.name	= "video-bus-switch",
+		.of_match_table = video_bus_switch_of_match,
+	},
+	.probe		= video_bus_switch_probe,
+	.remove		= video_bus_switch_remove,
+};
+
+module_platform_driver(video_bus_switch_driver);
+
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
+MODULE_DESCRIPTION("Video Bus Switch");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:video-bus-switch");
-- 
1.9.1


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

* [RFC PATCH 17/24] smiapp: add CCP2 support
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (15 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 16/24] media: video-bus-switch: new driver Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-05-01 10:57     ` Sakari Ailus
  2016-04-24 21:08   ` [RFC PATCH 18/24] v4l2-async: per notifier locking Ivaylo Dimitrov
                     ` (12 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sebastian Reichel <sre@kernel.org>

Add support for CCP2 connected SMIA sensors as found
on the Nokia N900.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 drivers/media/i2c/smiapp/smiapp-core.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index c6a897b..ec19532 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -3016,13 +3016,19 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev)
 	switch (bus_cfg->bus_type) {
 	case V4L2_MBUS_CSI2:
 		pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2;
+		pdata->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes;
+		break;
+	case V4L2_MBUS_CCP2:
+		pdata->csi_signalling_mode = (bus_cfg->bus.mipi_csi1.strobe) ?
+		SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
+		SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
+		pdata->lanes = 1;
 		break;
-		/* FIXME: add CCP2 support. */
 	default:
+		dev_err(dev, "unknown bus protocol\n");
 		goto out_err;
 	}
 
-	pdata->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes;
 	dev_dbg(dev, "lanes %u\n", pdata->lanes);
 
 	/* xshutdown GPIO is optional */
@@ -3039,7 +3045,7 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev)
 		goto out_err;
 	}
 
-	dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown,
+	dev_dbg(dev, "reset %d, nvm %d, clk %d, mode %d\n", pdata->xshutdown,
 		pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode);
 
 	if (!bus_cfg->nr_of_link_frequencies) {
-- 
1.9.1


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

* [RFC PATCH 18/24] v4l2-async: per notifier locking
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (16 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 17/24] smiapp: add CCP2 support Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 19/24] v4l2_device_register_subdev_nodes: allow calling multiple times Ivaylo Dimitrov
                     ` (11 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sebastian Reichel <sre@kernel.org>

---
 drivers/media/v4l2-core/v4l2-async.c | 50 ++++++++++++++++++------------------
 include/media/v4l2-async.h           |  2 ++
 2 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index a4b224d..27789cd 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -56,7 +56,6 @@ static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
 
 static LIST_HEAD(subdev_list);
 static LIST_HEAD(notifier_list);
-static DEFINE_MUTEX(list_lock);
 
 static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
 						    struct v4l2_subdev *sd)
@@ -106,14 +105,17 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
 
 	if (notifier->bound) {
 		ret = notifier->bound(notifier, sd, asd);
-		if (ret < 0)
+		if (ret < 0) {
+			dev_warn(notifier->v4l2_dev->dev, "subdev bound failed\n");
 			return ret;
+		}
 	}
 	/* Move from the global subdevice list to notifier's done */
 	list_move(&sd->async_list, &notifier->done);
 
 	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
 	if (ret < 0) {
+		dev_warn(notifier->v4l2_dev->dev, "subdev register failed\n");
 		if (notifier->unbind)
 			notifier->unbind(notifier, sd, asd);
 		return ret;
@@ -146,7 +148,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 {
 	struct v4l2_subdev *sd, *tmp;
 	struct v4l2_async_subdev *asd;
-	int i;
+	int ret = 0, i;
 
 	if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
 		return -EINVAL;
@@ -154,6 +156,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 	notifier->v4l2_dev = v4l2_dev;
 	INIT_LIST_HEAD(&notifier->waiting);
 	INIT_LIST_HEAD(&notifier->done);
+	mutex_init(&notifier->lock);
 
 	for (i = 0; i < notifier->num_subdevs; i++) {
 		asd = notifier->subdevs[i];
@@ -173,28 +176,22 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 		list_add_tail(&asd->list, &notifier->waiting);
 	}
 
-	mutex_lock(&list_lock);
-
 	/* Keep also completed notifiers on the list */
 	list_add(&notifier->list, &notifier_list);
+	mutex_lock(&notifier->lock);
 
 	list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
-		int ret;
-
 		asd = v4l2_async_belongs(notifier, sd);
 		if (!asd)
 			continue;
 
 		ret = v4l2_async_test_notify(notifier, sd, asd);
-		if (ret < 0) {
-			mutex_unlock(&list_lock);
-			return ret;
-		}
+		if (ret < 0)
+			break;
 	}
+	mutex_unlock(&notifier->lock);
 
-	mutex_unlock(&list_lock);
-
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL(v4l2_async_notifier_register);
 
@@ -215,7 +212,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 			"Failed to allocate device cache!\n");
 	}
 
-	mutex_lock(&list_lock);
+	mutex_lock(&notifier->lock);
 
 	list_del(&notifier->list);
 
@@ -242,7 +239,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 			put_device(d);
 	}
 
-	mutex_unlock(&list_lock);
+	mutex_unlock(&notifier->lock);
 
 	/*
 	 * Call device_attach() to reprobe devices
@@ -267,6 +264,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 	}
 	kfree(dev);
 
+	mutex_destroy(&notifier->lock);
 	notifier->v4l2_dev = NULL;
 
 	/*
@@ -279,6 +277,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister);
 int v4l2_async_register_subdev(struct v4l2_subdev *sd)
 {
 	struct v4l2_async_notifier *notifier;
+	struct v4l2_async_notifier *tmp;
 
 	/*
 	 * No reference taken. The reference is held by the device
@@ -288,24 +287,25 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
 	if (!sd->of_node && sd->dev)
 		sd->of_node = sd->dev->of_node;
 
-	mutex_lock(&list_lock);
-
 	INIT_LIST_HEAD(&sd->async_list);
 
-	list_for_each_entry(notifier, &notifier_list, list) {
-		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
+	list_for_each_entry_safe(notifier, tmp, &notifier_list, list) {
+		struct v4l2_async_subdev *asd;
+
+		/* TODO: FIXME: if this is called by ->bound() we will also iterate over the locked notifier */
+		mutex_lock_nested(&notifier->lock, SINGLE_DEPTH_NESTING);
+		asd = v4l2_async_belongs(notifier, sd);
 		if (asd) {
 			int ret = v4l2_async_test_notify(notifier, sd, asd);
-			mutex_unlock(&list_lock);
+			mutex_unlock(&notifier->lock);
 			return ret;
 		}
+		mutex_unlock(&notifier->lock);
 	}
 
 	/* None matched, wait for hot-plugging */
 	list_add(&sd->async_list, &subdev_list);
 
-	mutex_unlock(&list_lock);
-
 	return 0;
 }
 EXPORT_SYMBOL(v4l2_async_register_subdev);
@@ -320,7 +320,7 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
 		return;
 	}
 
-	mutex_lock(&list_lock);
+	mutex_lock_nested(&notifier->lock, SINGLE_DEPTH_NESTING);
 
 	list_add(&sd->asd->list, &notifier->waiting);
 
@@ -329,6 +329,6 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
 	if (notifier->unbind)
 		notifier->unbind(notifier, sd, sd->asd);
 
-	mutex_unlock(&list_lock);
+	mutex_unlock(&notifier->lock);
 }
 EXPORT_SYMBOL(v4l2_async_unregister_subdev);
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 1d6d7da..d2178c1 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -71,6 +71,7 @@ struct v4l2_async_subdev {
  * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
  * @done:	list of struct v4l2_subdev, already probed
  * @list:	member in a global list of notifiers
+ * @lock:       lock hold when the notifier is being processed
  * @bound:	a subdevice driver has successfully probed one of subdevices
  * @complete:	all subdevices have been probed successfully
  * @unbind:	a subdevice is leaving
@@ -82,6 +83,7 @@ struct v4l2_async_notifier {
 	struct list_head waiting;
 	struct list_head done;
 	struct list_head list;
+	struct mutex lock;
 	int (*bound)(struct v4l2_async_notifier *notifier,
 		     struct v4l2_subdev *subdev,
 		     struct v4l2_async_subdev *asd);
-- 
1.9.1


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

* [RFC PATCH 19/24] v4l2_device_register_subdev_nodes: allow calling multiple times
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (17 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 18/24] v4l2-async: per notifier locking Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 20/24] ARM: dts: omap3-n900: enable cameras Ivaylo Dimitrov
                     ` (10 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sebastian Reichel <sre@kernel.org>

---
 drivers/media/v4l2-core/v4l2-device.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 06fa5f1..5aebe0a 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -238,6 +238,9 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
 		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
 			continue;
 
+		if(sd->devnode)
+			continue;
+
 		vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
 		if (!vdev) {
 			err = -ENOMEM;
-- 
1.9.1


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

* [RFC PATCH 20/24] ARM: dts: omap3-n900: enable cameras
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (18 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 19/24] v4l2_device_register_subdev_nodes: allow calling multiple times Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 21/24] omap3isp: dt: Add support for CSI1/CCP2 busses Ivaylo Dimitrov
                     ` (9 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 arch/arm/boot/dts/omap3-n900.dts | 140 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index cc4cab2..03e1613 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -177,6 +177,84 @@
 		io-channels = <&twl_madc 0>, <&twl_madc 4>, <&twl_madc 12>;
 		io-channel-names = "temp", "bsi", "vbat";
 	};
+
+	rear_camera: camera@0 {
+		compatible = "linux,camera";
+
+		module {
+			model = "TCM8341MD";
+			sensor = <&cam1>;
+			focus = <&autofocus>;
+		};
+	};
+
+	front_camera: camera@1 {
+		compatible = "linux,camera";
+
+		module {
+			model = "VS6555";
+			sensor = <&cam2>;
+		};
+	};
+
+	video-bus-switch {
+		compatible = "video-bus-switch";
+
+		switch-gpios = <&gpio4 1 GPIO_ACTIVE_HIGH>; /* 97 */
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				csi_switch_in: endpoint {
+					remote-endpoint = <&csi_isp>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				csi_switch_out1: endpoint {
+					remote-endpoint = <&csi_cam1>;
+				};
+			};
+
+			port@2 {
+				reg = <2>;
+
+				csi_switch_out2: endpoint {
+					remote-endpoint = <&csi_cam2>;
+				};
+			};
+		};
+	};
+};
+
+&isp {
+	vdds_csib-supply = <&vaux2>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&camera_pins>;
+
+	ports {
+		port@1 {
+			reg = <1>;
+
+			csi_isp: endpoint {
+				remote-endpoint = <&csi_switch_in>;
+				bus-type = <3>; /* CCP2 */
+				clock-lanes = <0>;
+				data-lanes = <1>;
+				lane-polarity = <0 0>;
+				clock-inv = <0>;
+				strobe = <0>;
+				crc = <0>;
+			};
+		};
+	};
 };
 
 &omap3_pmx_core {
@@ -341,6 +419,22 @@
 			OMAP3_CORE1_IOPAD(0x218e, PIN_OUTPUT | MUX_MODE4)		/* gpio 157 => cmt_bsi */
 		>;
 	};
+
+	camera_pins: pinmux_camera {
+		pinctrl-single,pins = <
+			OMAP3_CORE1_IOPAD(0x210c, PIN_OUTPUT | MUX_MODE7)       /* cam_hs */
+			OMAP3_CORE1_IOPAD(0x210e, PIN_OUTPUT | MUX_MODE7)       /* cam_vs */
+			OMAP3_CORE1_IOPAD(0x2110, PIN_OUTPUT | MUX_MODE0)       /* cam_xclka */
+			OMAP3_CORE1_IOPAD(0x211e, PIN_OUTPUT | MUX_MODE7)       /* cam_d4 */
+			OMAP3_CORE1_IOPAD(0x2122, PIN_INPUT | MUX_MODE0)        /* cam_d6 */
+			OMAP3_CORE1_IOPAD(0x2124, PIN_INPUT | MUX_MODE0)        /* cam_d7 */
+			OMAP3_CORE1_IOPAD(0x2126, PIN_INPUT | MUX_MODE0)        /* cam_d8 */
+			OMAP3_CORE1_IOPAD(0x2128, PIN_INPUT | MUX_MODE0)        /* cam_d9 */
+			OMAP3_CORE1_IOPAD(0x212a, PIN_OUTPUT | MUX_MODE7)       /* cam_d10 */
+			OMAP3_CORE1_IOPAD(0x212e, PIN_OUTPUT | MUX_MODE7)       /* cam_xclkb */
+			OMAP3_CORE1_IOPAD(0x2132, PIN_OUTPUT | MUX_MODE0)       /* cam_strobe */
+		>;
+	};
 };
 
 &i2c1 {
@@ -529,6 +623,28 @@
 
 	clock-frequency = <100000>;
 
+	cam2: camera@10 {
+		compatible = "nokia,smia";
+		reg = <0x10>;
+
+		vana-supply = <&vaux4>;
+
+		clocks = <&isp 0>;
+		clock-frequency = <9600000>;
+
+		port {
+			csi_cam2: endpoint {
+				link-frequencies = /bits/ 64 <60000000>;
+				bus-type = <3>; /* CCP2 */
+				strobe = <0>;
+				clock-inv = <0>;
+				crc = <0>;
+
+				remote-endpoint = <&csi_switch_out2>;
+			};
+		};
+	};
+
 	tlv320aic3x: tlv320aic3x@18 {
 		compatible = "ti,tlv320aic3x";
 		reg = <0x18>;
@@ -738,6 +854,30 @@
 		st,max-limit-y = <32>;
 		st,max-limit-z = <32>;
 	};
+
+	cam1: camera@3e {
+		compatible = "toshiba,et8ek8";
+		reg = <0x3e>;
+
+		vana-supply = <&vaux4>;
+
+		clocks = <&isp 0>;
+		clock-names = "extclk";
+		clock-frequency = <9600000>;
+
+		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
+
+		port {
+			csi_cam1: endpoint {
+				bus-type = <3>; /* CCP2 */
+				strobe = <1>;
+				clock-inv = <0>;
+				crc = <1>;
+
+				remote-endpoint = <&csi_switch_out1>;
+			};
+		};
+	};
 };
 
 &mmc1 {
-- 
1.9.1


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

* [RFC PATCH 21/24] omap3isp: dt: Add support for CSI1/CCP2 busses
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (19 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 20/24] ARM: dts: omap3-n900: enable cameras Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 22/24] [media] omap3isp: Correctly set IO_OUT_SEL and VP_CLK_POL for CCP2 mode Ivaylo Dimitrov
                     ` (8 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media

From: Sakari Ailus <sakari.ailus@iki.fi>

Obtain the CSI1/CCP2 bus parameters from the OF node.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/media/platform/omap3isp/isp.c | 110 ++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 33 deletions(-)

diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 5d54e2c..e51a1f9 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2020,12 +2020,84 @@ enum isp_of_phy {
 	ISP_OF_PHY_CSIPHY2,
 };
 
+static void isp_of_parse_node_csi1(struct device *dev,
+				   struct isp_bus_cfg *buscfg,
+				   struct v4l2_of_endpoint *vep)
+{
+	if (vep->base.port == ISP_OF_PHY_CSIPHY1)
+		buscfg->interface = ISP_INTERFACE_CCP2B_PHY1;
+	else
+		buscfg->interface = ISP_INTERFACE_CCP2B_PHY2;
+	buscfg->bus.ccp2.lanecfg.clk.pos = vep->bus.mipi_csi1.clock_lane;
+	buscfg->bus.ccp2.lanecfg.clk.pol =
+		vep->bus.mipi_csi1.lane_polarity[0];
+	dev_dbg(dev, "clock lane polarity %u, pos %u\n",
+		buscfg->bus.ccp2.lanecfg.clk.pol,
+		buscfg->bus.ccp2.lanecfg.clk.pos);
+
+	buscfg->bus.ccp2.lanecfg.data[0].pos = vep->bus.mipi_csi2.data_lanes[0];
+	buscfg->bus.ccp2.lanecfg.data[0].pol =
+		vep->bus.mipi_csi2.lane_polarities[1];
+	dev_dbg(dev, "data lane polarity %u, pos %u\n",
+		buscfg->bus.ccp2.lanecfg.data[0].pol,
+		buscfg->bus.ccp2.lanecfg.data[0].pos);
+
+	buscfg->bus.ccp2.strobe_clk_pol = vep->bus.mipi_csi1.clock_inv;
+	buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe;
+	buscfg->bus.ccp2.ccp2_mode = vep->bus_type == V4L2_MBUS_CCP2;
+
+	dev_dbg(dev, "clock_inv %u strobe %u ccp2 %u\n",
+		buscfg->bus.ccp2.strobe_clk_pol,
+		buscfg->bus.ccp2.phy_layer,
+		buscfg->bus.ccp2.ccp2_mode);
+	/*
+	 * FIXME: now we assume the CRC is always there.
+	 * Implement a way to obtain this information from the
+	 * sensor. Frame descriptors, perhaps?
+	 */
+	buscfg->bus.ccp2.crc = 1;
+}
+
+static void isp_of_parse_node_csi2(struct device *dev,
+				   struct isp_bus_cfg *buscfg,
+				   struct v4l2_of_endpoint *vep)
+{
+	unsigned int i;
+
+	if (vep->base.port == ISP_OF_PHY_CSIPHY1)
+		buscfg->interface = ISP_INTERFACE_CSI2C_PHY1;
+	else
+		buscfg->interface = ISP_INTERFACE_CSI2A_PHY2;
+	buscfg->bus.csi2.lanecfg.clk.pos = vep->bus.mipi_csi2.clock_lane;
+	buscfg->bus.csi2.lanecfg.clk.pol =
+			vep->bus.mipi_csi2.lane_polarities[0];
+	dev_dbg(dev, "clock lane polarity %u, pos %u\n",
+		buscfg->bus.csi2.lanecfg.clk.pol,
+		buscfg->bus.csi2.lanecfg.clk.pos);
+
+	for (i = 0; i < ISP_CSIPHY2_NUM_DATA_LANES; i++) {
+		buscfg->bus.csi2.lanecfg.data[i].pos =
+			vep->bus.mipi_csi2.data_lanes[i];
+		buscfg->bus.csi2.lanecfg.data[i].pol =
+			vep->bus.mipi_csi2.lane_polarities[i + 1];
+		dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i,
+			buscfg->bus.csi2.lanecfg.data[i].pol,
+				buscfg->bus.csi2.lanecfg.data[i].pos);
+	}
+
+	/*
+	 * FIXME: now we assume the CRC is always there.
+	 * Implement a way to obtain this information from the
+	 * sensor. Frame descriptors, perhaps?
+	 */
+	buscfg->bus.csi2.crc = 1;
+}
+
 static int isp_of_parse_node(struct device *dev, struct device_node *node,
 			     struct isp_async_subdev *isd)
 {
 	struct isp_bus_cfg *buscfg = &isd->bus;
 	struct v4l2_of_endpoint vep;
-	unsigned int i;
 	int ret;
 
 	ret = v4l2_of_parse_endpoint(node, &vep);
@@ -2055,38 +2127,10 @@ static int isp_of_parse_node(struct device *dev, struct device_node *node,
 
 	case ISP_OF_PHY_CSIPHY1:
 	case ISP_OF_PHY_CSIPHY2:
-		/* FIXME: always assume CSI-2 for now. */
-		switch (vep.base.port) {
-		case ISP_OF_PHY_CSIPHY1:
-			buscfg->interface = ISP_INTERFACE_CSI2C_PHY1;
-			break;
-		case ISP_OF_PHY_CSIPHY2:
-			buscfg->interface = ISP_INTERFACE_CSI2A_PHY2;
-			break;
-		}
-		buscfg->bus.csi2.lanecfg.clk.pos = vep.bus.mipi_csi2.clock_lane;
-		buscfg->bus.csi2.lanecfg.clk.pol =
-			vep.bus.mipi_csi2.lane_polarities[0];
-		dev_dbg(dev, "clock lane polarity %u, pos %u\n",
-			buscfg->bus.csi2.lanecfg.clk.pol,
-			buscfg->bus.csi2.lanecfg.clk.pos);
-
-		for (i = 0; i < ISP_CSIPHY2_NUM_DATA_LANES; i++) {
-			buscfg->bus.csi2.lanecfg.data[i].pos =
-				vep.bus.mipi_csi2.data_lanes[i];
-			buscfg->bus.csi2.lanecfg.data[i].pol =
-				vep.bus.mipi_csi2.lane_polarities[i + 1];
-			dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i,
-				buscfg->bus.csi2.lanecfg.data[i].pol,
-				buscfg->bus.csi2.lanecfg.data[i].pos);
-		}
-
-		/*
-		 * FIXME: now we assume the CRC is always there.
-		 * Implement a way to obtain this information from the
-		 * sensor. Frame descriptors, perhaps?
-		 */
-		buscfg->bus.csi2.crc = 1;
+		if (vep.bus_type == V4L2_MBUS_CSI2)
+			isp_of_parse_node_csi2(dev, buscfg, &vep);
+		else
+			isp_of_parse_node_csi1(dev, buscfg, &vep);
 		break;
 
 	default:
-- 
1.9.1


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

* [RFC PATCH 22/24] [media] omap3isp: Correctly set IO_OUT_SEL and VP_CLK_POL for CCP2 mode
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (20 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 21/24] omap3isp: dt: Add support for CSI1/CCP2 busses Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 23/24] [media] omap3isp: Make sure CSI1 interface is enabled in CPP2 mode Ivaylo Dimitrov
                     ` (7 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

ISP CSI1 module needs all the bits correctly set to work.

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/platform/omap3isp/isp.c      | 2 ++
 drivers/media/platform/omap3isp/ispccp2.c  | 7 +++++--
 drivers/media/platform/omap3isp/ispreg.h   | 4 ++++
 drivers/media/platform/omap3isp/omap3isp.h | 1 +
 4 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index e51a1f9..6361fde 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2056,6 +2056,8 @@ static void isp_of_parse_node_csi1(struct device *dev,
 	 * sensor. Frame descriptors, perhaps?
 	 */
 	buscfg->bus.ccp2.crc = 1;
+
+	buscfg->bus.ccp2.vp_clk_pol = 1;
 }
 
 static void isp_of_parse_node_csi2(struct device *dev,
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index ca09523..7bb7feb 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -213,14 +213,17 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
 	struct isp_device *isp = to_isp_device(ccp2);
 	u32 val;
 
-	/* CCP2B mode */
 	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) |
-			    ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE;
+	      ISPCCP2_CTRL_MODE;
 	/* Data/strobe physical layer */
 	BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK,
 		buscfg->phy_layer);
+	BIT_SET(val, ISPCCP2_CTRL_IO_OUT_SEL_SHIFT,
+		ISPCCP2_CTRL_IO_OUT_SEL_MASK, buscfg->ccp2_mode);
 	BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK,
 		buscfg->strobe_clk_pol);
+	BIT_SET(val, ISPCCP2_CTRL_VP_CLK_POL_SHIFT,
+		ISPCCP2_CTRL_VP_CLK_POL_MASK, buscfg->vp_clk_pol);
 	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
 
 	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h
index b5ea8da..d084839 100644
--- a/drivers/media/platform/omap3isp/ispreg.h
+++ b/drivers/media/platform/omap3isp/ispreg.h
@@ -87,6 +87,8 @@
 #define ISPCCP2_CTRL_PHY_SEL_MASK	0x1
 #define ISPCCP2_CTRL_PHY_SEL_SHIFT	1
 #define ISPCCP2_CTRL_IO_OUT_SEL		(1 << 2)
+#define ISPCCP2_CTRL_IO_OUT_SEL_MASK	0x1
+#define ISPCCP2_CTRL_IO_OUT_SEL_SHIFT	2
 #define ISPCCP2_CTRL_MODE		(1 << 4)
 #define ISPCCP2_CTRL_VP_CLK_FORCE_ON	(1 << 9)
 #define ISPCCP2_CTRL_INV		(1 << 10)
@@ -94,6 +96,8 @@
 #define ISPCCP2_CTRL_INV_SHIFT		10
 #define ISPCCP2_CTRL_VP_ONLY_EN		(1 << 11)
 #define ISPCCP2_CTRL_VP_CLK_POL		(1 << 12)
+#define ISPCCP2_CTRL_VP_CLK_POL_MASK	0x1
+#define ISPCCP2_CTRL_VP_CLK_POL_SHIFT	12
 #define ISPCCP2_CTRL_VPCLK_DIV_SHIFT	15
 #define ISPCCP2_CTRL_VPCLK_DIV_MASK	0x1ffff /* [31:15] */
 #define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT	8 /* 3430 bits */
diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/omap3isp/omap3isp.h
index 443e8f7..f6d1d0d 100644
--- a/drivers/media/platform/omap3isp/omap3isp.h
+++ b/drivers/media/platform/omap3isp/omap3isp.h
@@ -108,6 +108,7 @@ struct isp_ccp2_cfg {
 	unsigned int ccp2_mode:1;
 	unsigned int phy_layer:1;
 	unsigned int vpclk_div:2;
+	unsigned int vp_clk_pol:1;
 	struct isp_csiphy_lanes_cfg lanecfg;
 };
 
-- 
1.9.1


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

* [RFC PATCH 23/24] [media] omap3isp: Make sure CSI1 interface is enabled in CPP2 mode
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (21 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 22/24] [media] omap3isp: Correctly set IO_OUT_SEL and VP_CLK_POL for CCP2 mode Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:08   ` [RFC PATCH 24/24] ARM: dts: omap3-n900: enable cameras - remove invalid entry Ivaylo Dimitrov
                     ` (6 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

OMAP3430 needs various syscon CONTROL_CSIRXFE bits set in order to operate.
Implement the missing functionality.

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/platform/omap3isp/ispccp2.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 7bb7feb..833eed4 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -21,6 +21,7 @@
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
 
 #include "isp.h"
 #include "ispreg.h"
@@ -160,6 +161,32 @@ static int ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
 			return ret;
 	}
 
+	if (isp->revision == ISP_REVISION_2_0) {
+		struct media_pad *pad;
+		struct v4l2_subdev *sensor;
+		const struct isp_ccp2_cfg *buscfg;
+		u32 csirxfe;
+
+		pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]);
+		sensor = media_entity_to_v4l2_subdev(pad->entity);
+		buscfg = &((struct isp_bus_cfg *)sensor->host_priv)->bus.ccp2;
+
+
+		if (enable) {
+			csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ |
+				  OMAP343X_CONTROL_CSIRXFE_RESET;
+
+			if (buscfg->phy_layer)
+				csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM;
+
+			if (buscfg->strobe_clk_pol)
+				csirxfe |= OMAP343X_CONTROL_CSIRXFE_CSIB_INV;
+		} else
+			csirxfe = 0;
+
+		regmap_write(isp->syscon, isp->syscon_offset, csirxfe);
+	}
+
 	/* Enable/Disable all the LCx channels */
 	for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
 		isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i),
-- 
1.9.1


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

* [RFC PATCH 24/24] ARM: dts: omap3-n900: enable cameras - remove invalid entry
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (22 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 23/24] [media] omap3isp: Make sure CSI1 interface is enabled in CPP2 mode Ivaylo Dimitrov
@ 2016-04-24 21:08   ` Ivaylo Dimitrov
  2016-04-24 21:55   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
                     ` (5 subsequent siblings)
  29 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-24 21:08 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 arch/arm/boot/dts/omap3-n900.dts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 03e1613..bf74f85 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -184,7 +184,6 @@
 		module {
 			model = "TCM8341MD";
 			sensor = <&cam1>;
-			focus = <&autofocus>;
 		};
 	};
 
-- 
1.9.1


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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (23 preceding siblings ...)
  2016-04-24 21:08   ` [RFC PATCH 24/24] ARM: dts: omap3-n900: enable cameras - remove invalid entry Ivaylo Dimitrov
@ 2016-04-24 21:55   ` Pavel Machek
  2016-04-25  6:33     ` Ivaylo Dimitrov
  2016-04-25 10:40   ` Pali Rohár
                     ` (4 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-24 21:55 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> Those patch series make cameras on Nokia N900 partially working.
> Some more patches are needed, but I've already sent them for
> upstreaming so they are not part of the series:
> 
> https://lkml.org/lkml/2016/4/16/14
> https://lkml.org/lkml/2016/4/16/33
> 
> As omap3isp driver supports only one endpoint on ccp2 interface,
> but cameras on N900 require different strobe settings, so far
> it is not possible to have both cameras correctly working with
> the same board DTS. DTS patch in the series has the correct
> settings for the front camera. This is a problem still to be
> solved.
> 
> The needed pipeline could be made with:

Would you have similar pipeline for the back camera? Autofocus and
5MPx makes it more interesting. I understand that different dts will
be needed.

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 01/24] V4L fixes
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
@ 2016-04-24 22:05     ` Pavel Machek
  2016-04-25  7:29     ` Hans Verkuil
  2016-04-25 13:25     ` Sakari Ailus
  2 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-24 22:05 UTC (permalink / raw)
  To: Ivaylo Dimitrov
  Cc: sakari.ailus, sre, pali.rohar, linux-media, Tuukka.O Toivonen

On Mon 2016-04-25 00:08:01, Ivaylo Dimitrov wrote:
> From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>
> 
> Squashed from the following upstream commits:
> 
> V4L: Create control class for sensor mode
> V4L: add ad5820 focus specific custom controls
> V4L: add V4L2_CID_TEST_PATTERN
> V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock
> 
> Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>

I guess you need to append your Signed-off-by: here.

Otherwise it looks good, so

Acked-by: Pavel Machek <pavel@ucw.cz>

(And thanks for all the work).
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:55   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
@ 2016-04-25  6:33     ` Ivaylo Dimitrov
  2016-04-25 17:09       ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25  6:33 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media



On 25.04.2016 00:55, Pavel Machek wrote:
> Hi!
>
>> Those patch series make cameras on Nokia N900 partially working.
>> Some more patches are needed, but I've already sent them for
>> upstreaming so they are not part of the series:
>>
>> https://lkml.org/lkml/2016/4/16/14
>> https://lkml.org/lkml/2016/4/16/33
>>
>> As omap3isp driver supports only one endpoint on ccp2 interface,
>> but cameras on N900 require different strobe settings, so far
>> it is not possible to have both cameras correctly working with
>> the same board DTS. DTS patch in the series has the correct
>> settings for the front camera. This is a problem still to be
>> solved.
>>
>> The needed pipeline could be made with:
>
> Would you have similar pipeline for the back camera? Autofocus and
> 5MPx makes it more interesting. I understand that different dts will
> be needed.
>

Try with:

media-ctl -r
media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'

media-ctl -V '"et8ek8 3-003e":0 [SGRBG10 864x656]'
media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 864x656]'
media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 864x656]'
media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 864x656]'
media-ctl -V '"OMAP3 ISP preview":1 [UYVY 864x656]'
media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 800x600]'


mplayer -tv 
driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6 -vo xv 
-vf screenshot tv://


Regards,
Ivo

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

* Re: [RFC PATCH 01/24] V4L fixes
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
  2016-04-24 22:05     ` Pavel Machek
@ 2016-04-25  7:29     ` Hans Verkuil
  2016-04-25 13:25     ` Sakari Ailus
  2 siblings, 0 replies; 102+ messages in thread
From: Hans Verkuil @ 2016-04-25  7:29 UTC (permalink / raw)
  To: Ivaylo Dimitrov, sakari.ailus
  Cc: sre, pali.rohar, pavel, linux-media, Tuukka.O Toivonen

On 04/24/2016 11:08 PM, Ivaylo Dimitrov wrote:
> From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>
> 
> Squashed from the following upstream commits:
> 
> V4L: Create control class for sensor mode
> V4L: add ad5820 focus specific custom controls
> V4L: add V4L2_CID_TEST_PATTERN
> V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock
> 
> Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> ---
>  include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index b6a357a..23011cc 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -62,6 +62,7 @@
>  #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
>  #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
>  #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
> +#define V4L2_CTRL_CLASS_MODE		0x00a40000	/* Sensor mode information */
>  
>  /* User-class control IDs */
>  
> @@ -974,4 +975,20 @@ enum v4l2_detect_md_mode {
>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
>  #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
>  
> +/* SMIA-type sensor information */
> +#define V4L2_CID_MODE_CLASS_BASE		(V4L2_CTRL_CLASS_MODE | 0x900)
> +#define V4L2_CID_MODE_CLASS			(V4L2_CTRL_CLASS_MODE | 1)
> +#define V4L2_CID_MODE_FRAME_WIDTH		(V4L2_CID_MODE_CLASS_BASE+1)
> +#define V4L2_CID_MODE_FRAME_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+2)
> +#define V4L2_CID_MODE_VISIBLE_WIDTH		(V4L2_CID_MODE_CLASS_BASE+3)
> +#define V4L2_CID_MODE_VISIBLE_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+4)
> +#define V4L2_CID_MODE_PIXELCLOCK		(V4L2_CID_MODE_CLASS_BASE+5)
> +#define V4L2_CID_MODE_SENSITIVITY		(V4L2_CID_MODE_CLASS_BASE+6)
> +#define V4L2_CID_MODE_OPSYSCLOCK		(V4L2_CID_MODE_CLASS_BASE+7)

The new controls need to be added to v4l2-ctrls.c and documented in DocBook.
But I would take a look at the existing Image Source and Image Process controls:

https://hverkuil.home.xs4all.nl/spec/media.html#image-source-controls

I think these controls belong there and that there is overlap anyway.

Sakari would probably know best what should be done with these controls.

Regards,

	Hans

> +
> +/* Control IDs specific to the AD5820 driver as defined by V4L2 */
> +#define V4L2_CID_FOCUS_AD5820_BASE 		(V4L2_CTRL_CLASS_CAMERA | 0x10af)
> +#define V4L2_CID_FOCUS_AD5820_RAMP_TIME		(V4L2_CID_FOCUS_AD5820_BASE+0)
> +#define V4L2_CID_FOCUS_AD5820_RAMP_MODE		(V4L2_CID_FOCUS_AD5820_BASE+1)
> +
>  #endif
> 


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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (24 preceding siblings ...)
  2016-04-24 21:55   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
@ 2016-04-25 10:40   ` Pali Rohár
  2016-04-25 14:06     ` Pavel Machek
  2016-04-25 16:58   ` Pavel Machek
                     ` (3 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Pali Rohár @ 2016-04-25 10:40 UTC (permalink / raw)
  To: linux-media, Ivaylo Dimitrov, sakari.ailus, sre, pavel

front camera:

On Monday 25 April 2016 00:08:00 Ivaylo Dimitrov wrote:
> The needed pipeline could be made with:
> 
> media-ctl -r
> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
> 
> and tested with:
> 
> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://

back camera:

On Monday 25 April 2016 09:33:18 Ivaylo Dimitrov wrote:
> Try with:
> 
> media-ctl -r
> media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> 
> media-ctl -V '"et8ek8 3-003e":0 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 864x656]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 800x600]'
> 
> 
> mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://

Hey!!! That is crazy! Who created such retard API?? In both cases you
are going to show video from /dev/video6 device. But in real I have two
independent camera devices: front and back.

Why on the earth I cannot have /dev/video0 for back camera and
/dev/video1 for front camera? And need to call such huge commands which
re-route pictures from correct camera to /dev/video6 device? I'm really
not interested in some hw details how are cameras connected, I just want
to show pictures in userspace...

And what are those others /dev/video[0-5] devices?

-- 
Pali Rohár
pali.rohar@gmail.com

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

* Re: [RFC PATCH 01/24] V4L fixes
  2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
  2016-04-24 22:05     ` Pavel Machek
  2016-04-25  7:29     ` Hans Verkuil
@ 2016-04-25 13:25     ` Sakari Ailus
  2016-04-25 16:32       ` Ivaylo Dimitrov
  2 siblings, 1 reply; 102+ messages in thread
From: Sakari Ailus @ 2016-04-25 13:25 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

Thanks for the set!

On Mon, Apr 25, 2016 at 12:08:01AM +0300, Ivaylo Dimitrov wrote:
> From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>
> 
> Squashed from the following upstream commits:
> 
> V4L: Create control class for sensor mode
> V4L: add ad5820 focus specific custom controls
> V4L: add V4L2_CID_TEST_PATTERN
> V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock
> 
> Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> ---
>  include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index b6a357a..23011cc 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -62,6 +62,7 @@
>  #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
>  #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
>  #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
> +#define V4L2_CTRL_CLASS_MODE		0x00a40000	/* Sensor mode information */
>  
>  /* User-class control IDs */
>  
> @@ -974,4 +975,20 @@ enum v4l2_detect_md_mode {
>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
>  #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
>  
> +/* SMIA-type sensor information */
> +#define V4L2_CID_MODE_CLASS_BASE		(V4L2_CTRL_CLASS_MODE | 0x900)
> +#define V4L2_CID_MODE_CLASS			(V4L2_CTRL_CLASS_MODE | 1)
> +#define V4L2_CID_MODE_FRAME_WIDTH		(V4L2_CID_MODE_CLASS_BASE+1)
> +#define V4L2_CID_MODE_FRAME_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+2)
> +#define V4L2_CID_MODE_VISIBLE_WIDTH		(V4L2_CID_MODE_CLASS_BASE+3)
> +#define V4L2_CID_MODE_VISIBLE_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+4)

The interface here pre-dates the selection API. The frame width and height
is today conveyed to the bridge driver by the smiapp driver in the scaler
(or binner in case of the lack of the scaler) sub-device's source pad
format.

(While that's the current interface, I'm not entirely happy with it; it
requires more sub-devices to be created for the same I2C device). I think in
this case you'd need one more to properly control the sensor.

> +#define V4L2_CID_MODE_PIXELCLOCK		(V4L2_CID_MODE_CLASS_BASE+5)
> +#define V4L2_CID_MODE_SENSITIVITY		(V4L2_CID_MODE_CLASS_BASE+6)
> +#define V4L2_CID_MODE_OPSYSCLOCK		(V4L2_CID_MODE_CLASS_BASE+7)

While I don't remember quite what the sensitivity value was about (it could
be e.g. binning summing / averaging), the other two values are passed to
(and also controlled by) the user space using controls. There are
V4L2_CID_PIXEL_RATE and V4L2_CID_LINK_FREQ or such.

> +
> +/* Control IDs specific to the AD5820 driver as defined by V4L2 */
> +#define V4L2_CID_FOCUS_AD5820_BASE 		(V4L2_CTRL_CLASS_CAMERA | 0x10af)
> +#define V4L2_CID_FOCUS_AD5820_RAMP_TIME		(V4L2_CID_FOCUS_AD5820_BASE+0)
> +#define V4L2_CID_FOCUS_AD5820_RAMP_MODE		(V4L2_CID_FOCUS_AD5820_BASE+1)

The ad5820 driver isn't in upstream at the moment. It should be investigated
whether there is a possibility to have standard V4L2 controls for lens
devices, or whether device specific controls should be implemented instead.
Device specific controls are a safe choice in this case, but they should be
in a separate patch, possibly one that would also include the lens driver
itself.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 10:40   ` Pali Rohár
@ 2016-04-25 14:06     ` Pavel Machek
  2016-04-25 14:09       ` Hans Verkuil
  2016-04-25 14:14       ` Pali Rohár
  0 siblings, 2 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 14:06 UTC (permalink / raw)
  To: Pali Rohár; +Cc: linux-media, Ivaylo Dimitrov, sakari.ailus, sre

Hi!

> On Monday 25 April 2016 00:08:00 Ivaylo Dimitrov wrote:
> > The needed pipeline could be made with:
> > 
> > media-ctl -r
> > media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2
...
> On Monday 25 April 2016 09:33:18 Ivaylo Dimitrov wrote:
> > Try with:
> > 
> > media-ctl -r
> > media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
...
> > mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
> 
> Hey!!! That is crazy! Who created such retard API?? In both cases you
> are going to show video from /dev/video6 device. But in real I have two
> independent camera devices: front and back.

Because Nokia, and because the hardware is complex, I'm afraid. First
we need to get it to work, than we can improve v4l... 

Anyway, does anyone know where to get the media-ctl tool? It does not
seem to be in debian 7 or debian 8...
									pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 14:06     ` Pavel Machek
@ 2016-04-25 14:09       ` Hans Verkuil
  2016-04-27 21:09         ` Pavel Machek
  2016-04-25 14:14       ` Pali Rohár
  1 sibling, 1 reply; 102+ messages in thread
From: Hans Verkuil @ 2016-04-25 14:09 UTC (permalink / raw)
  To: Pavel Machek, Pali Rohár
  Cc: linux-media, Ivaylo Dimitrov, sakari.ailus, sre

On 04/25/2016 04:06 PM, Pavel Machek wrote:
> Hi!
> 
>> On Monday 25 April 2016 00:08:00 Ivaylo Dimitrov wrote:
>>> The needed pipeline could be made with:
>>>
>>> media-ctl -r
>>> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2
> ...
>> On Monday 25 April 2016 09:33:18 Ivaylo Dimitrov wrote:
>>> Try with:
>>>
>>> media-ctl -r
>>> media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
> ...
>>> mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
>>
>> Hey!!! That is crazy! Who created such retard API?? In both cases you
>> are going to show video from /dev/video6 device. But in real I have two
>> independent camera devices: front and back.
> 
> Because Nokia, and because the hardware is complex, I'm afraid. First
> we need to get it to work, than we can improve v4l... 
> 
> Anyway, does anyone know where to get the media-ctl tool? It does not
> seem to be in debian 7 or debian 8...

It's part of the v4l-utils git repo:

https://git.linuxtv.org/v4l-utils.git/

Regards,

	Hans

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 14:06     ` Pavel Machek
  2016-04-25 14:09       ` Hans Verkuil
@ 2016-04-25 14:14       ` Pali Rohár
  2016-04-25 17:14         ` Pali Rohár
  1 sibling, 1 reply; 102+ messages in thread
From: Pali Rohár @ 2016-04-25 14:14 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-media, Ivaylo Dimitrov, sakari.ailus, sre

On Monday 25 April 2016 16:06:12 Pavel Machek wrote:
> Hi!
> 
> > On Monday 25 April 2016 00:08:00 Ivaylo Dimitrov wrote:
> > > The needed pipeline could be made with:
> > > 
> > > media-ctl -r
> > > media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2
> ...
> > On Monday 25 April 2016 09:33:18 Ivaylo Dimitrov wrote:
> > > Try with:
> > > 
> > > media-ctl -r
> > > media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
> ...
> > > mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
> > 
> > Hey!!! That is crazy! Who created such retard API?? In both cases you
> > are going to show video from /dev/video6 device. But in real I have two
> > independent camera devices: front and back.
> 
> Because Nokia, and because the hardware is complex, I'm afraid.

In Nokia kernel, there are just /dev/video0 and /dev/video1. When I open
first I see back camera, second front camera. No media-ctl nor any other
reconfiguration is needed. So not Nokia nor hw complexity is reason...

> First we need to get it to work, than we can improve v4l... 

Ok, I agree. But I really would like to see just two video devices and
all those route configuration in kernel...

> Anyway, does anyone know where to get the media-ctl tool?

Looks like it is part of v4l-utils package. At least in git:
https://git.linuxtv.org/v4l-utils.git/tree/utils/media-ctl

> It does not seem to be in debian 7 or debian 8...

I do not see it in debian too, but there is some version in ubuntu:
http://packages.ubuntu.com/trusty/media-ctl

So you can compile ubuntu dsc package, should work on debian.

-- 
Pali Rohár
pali.rohar@gmail.com

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

* Re: [RFC PATCH 01/24] V4L fixes
  2016-04-25 13:25     ` Sakari Ailus
@ 2016-04-25 16:32       ` Ivaylo Dimitrov
  2016-04-29  7:41         ` Sakari Ailus
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25 16:32 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Hi,

On 25.04.2016 16:25, Sakari Ailus wrote:
> Hi Ivaylo,
>
> Thanks for the set!
>
> On Mon, Apr 25, 2016 at 12:08:01AM +0300, Ivaylo Dimitrov wrote:
>> From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>
>>
>> Squashed from the following upstream commits:
>>
>> V4L: Create control class for sensor mode
>> V4L: add ad5820 focus specific custom controls
>> V4L: add V4L2_CID_TEST_PATTERN
>> V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock
>>
>> Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
>> ---
>>   include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++
>>   1 file changed, 17 insertions(+)
>>
>> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
>> index b6a357a..23011cc 100644
>> --- a/include/uapi/linux/v4l2-controls.h
>> +++ b/include/uapi/linux/v4l2-controls.h
>> @@ -62,6 +62,7 @@
>>   #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
>>   #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
>>   #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
>> +#define V4L2_CTRL_CLASS_MODE		0x00a40000	/* Sensor mode information */
>>
>>   /* User-class control IDs */
>>
>> @@ -974,4 +975,20 @@ enum v4l2_detect_md_mode {
>>   #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
>>   #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
>>
>> +/* SMIA-type sensor information */
>> +#define V4L2_CID_MODE_CLASS_BASE		(V4L2_CTRL_CLASS_MODE | 0x900)
>> +#define V4L2_CID_MODE_CLASS			(V4L2_CTRL_CLASS_MODE | 1)
>> +#define V4L2_CID_MODE_FRAME_WIDTH		(V4L2_CID_MODE_CLASS_BASE+1)
>> +#define V4L2_CID_MODE_FRAME_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+2)
>> +#define V4L2_CID_MODE_VISIBLE_WIDTH		(V4L2_CID_MODE_CLASS_BASE+3)
>> +#define V4L2_CID_MODE_VISIBLE_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+4)
>
> The interface here pre-dates the selection API. The frame width and height
> is today conveyed to the bridge driver by the smiapp driver in the scaler
> (or binner in case of the lack of the scaler) sub-device's source pad
> format.
>
> (While that's the current interface, I'm not entirely happy with it; it
> requires more sub-devices to be created for the same I2C device). I think in
> this case you'd need one more to properly control the sensor.
>

By looking at the code, it seems those are read-only, so I don't think 
it make sense to add a binner sub-device with read-only controls.

>> +#define V4L2_CID_MODE_PIXELCLOCK		(V4L2_CID_MODE_CLASS_BASE+5)
>> +#define V4L2_CID_MODE_SENSITIVITY		(V4L2_CID_MODE_CLASS_BASE+6)
>> +#define V4L2_CID_MODE_OPSYSCLOCK		(V4L2_CID_MODE_CLASS_BASE+7)
>
> While I don't remember quite what the sensitivity value was about (it could
> be e.g. binning summing / averaging), the other two values are passed to
> (and also controlled by) the user space using controls. There are
> V4L2_CID_PIXEL_RATE and V4L2_CID_LINK_FREQ or such.
>

I've already made a change so V4L2_CID_PIXEL_RATE is used in et8ek8 
driver 
(https://github.com/freemangordon/linux-n900/commit/54433e50451b4ed6cc6e3b25d149c5cacd97e438), 
but V4L2_CID_MODE_PIXELCLOCK is used in smiapp driver, which seems to 
expose its own controls. Not sure those are needed though. And if, what 
for. I hope you know better than me.

I guess V4L2_CID_MODE_OPSYSCLOCK can be easily transformed to 
V4L2_CID_LINK_FREQ in the same way as V4L2_CID_MODE_PIXELCLOCK.

>> +
>> +/* Control IDs specific to the AD5820 driver as defined by V4L2 */
>> +#define V4L2_CID_FOCUS_AD5820_BASE 		(V4L2_CTRL_CLASS_CAMERA | 0x10af)
>> +#define V4L2_CID_FOCUS_AD5820_RAMP_TIME		(V4L2_CID_FOCUS_AD5820_BASE+0)
>> +#define V4L2_CID_FOCUS_AD5820_RAMP_MODE		(V4L2_CID_FOCUS_AD5820_BASE+1)
>
> The ad5820 driver isn't in upstream at the moment. It should be investigated
> whether there is a possibility to have standard V4L2 controls for lens
> devices, or whether device specific controls should be implemented instead.
> Device specific controls are a safe choice in this case, but they should be
> in a separate patch, possibly one that would also include the lens driver
> itself.
>

Yeah, I sent the whole patch for the sake of not losing the history too 
much.

Thanks,
Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (25 preceding siblings ...)
  2016-04-25 10:40   ` Pali Rohár
@ 2016-04-25 16:58   ` Pavel Machek
  2016-04-25 17:17     ` Ivaylo Dimitrov
  2016-04-27  3:08   ` Sebastian Reichel
                     ` (2 subsequent siblings)
  29 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 16:58 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

Ok, let me try:

> The needed pipeline could be made with:
> 
> media-ctl -r
> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'

pavel@n900:/my/tui/ofone/camera$ sudo ./front.sh
MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer
Team

Linux n900 4.6.0-rc5-176733-g8b658a3-dirty
media-ctl from today's git.

> and tested with:
> 
> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
>

I can't do -vo xv ... fails for me, probably due to X
configuration. Does it work with -vo x11 for you?

For me it shows window with green interior. And complains about v4l2:
select timeouts. (I enabled these in .config):

+CONFIG_VIDEO_BUS_SWITCH=y
+CONFIG_VIDEO_SMIAREGS=y
+CONFIG_VIDEO_ET8EK8=y
+CONFIG_VIDEOBUF2_CORE=y
+CONFIG_VIDEOBUF2_MEMOPS=y
+CONFIG_VIDEOBUF2_DMA_CONTIG=y
+CONFIG_VIDEO_OMAP3=y
+CONFIG_VIDEO_SMIAPP_PLL=y
+CONFIG_VIDEO_SMIAPP=y

Any ideas?
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25  6:33     ` Ivaylo Dimitrov
@ 2016-04-25 17:09       ` Pavel Machek
  2016-04-25 17:21         ` Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 17:09 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> >>The needed pipeline could be made with:
> >
> >Would you have similar pipeline for the back camera? Autofocus and
> >5MPx makes it more interesting. I understand that different dts will
> >be needed.
> >
> 
> Try with:
> 
> media-ctl -r
> media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> 
> media-ctl -V '"et8ek8 3-003e":0 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 864x656]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 864x656]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 800x600]'
> 
> 
> mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6
> -vo xv -vf screenshot tv://

It fails with:

pavel@n900:/my/tui/ofone/camera$ sudo ./back.sh
Unable to parse link: Device or resource busy (16)
MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer
Team

...but as I'm using the original dts, it is expected...?

Would you have dts suitable for the 5MPx camera?

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 14:14       ` Pali Rohár
@ 2016-04-25 17:14         ` Pali Rohár
  0 siblings, 0 replies; 102+ messages in thread
From: Pali Rohár @ 2016-04-25 17:14 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-media, Ivaylo Dimitrov, sakari.ailus, sre

[-- Attachment #1: Type: Text/Plain, Size: 739 bytes --]

On Monday 25 April 2016 16:14:41 Pali Rohár wrote:
> > Anyway, does anyone know where to get the media-ctl tool?
> 
> Looks like it is part of v4l-utils package. At least in git:
> https://git.linuxtv.org/v4l-utils.git/tree/utils/media-ctl
> 
> > It does not seem to be in debian 7 or debian 8...
> 
> I do not see it in debian too, but there is some version in ubuntu:
> http://packages.ubuntu.com/trusty/media-ctl
> 
> So you can compile ubuntu dsc package, should work on debian.

Finally, it is also in debian, see:

https://packages.debian.org/search?suite=sid&arch=any&mode=path&searchon=contents&keywords=media-ctl
https://packages.debian.org/sid/amd64/v4l-utils/filelist

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 16:58   ` Pavel Machek
@ 2016-04-25 17:17     ` Ivaylo Dimitrov
  2016-04-25 18:40       ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25 17:17 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi,

On 25.04.2016 19:58, Pavel Machek wrote:
> Hi!
>
> Ok, let me try:
>
>> The needed pipeline could be made with:
>>
>> media-ctl -r
>> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
>> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
>> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
>> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
>> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
>> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
>> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
>> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
>
> pavel@n900:/my/tui/ofone/camera$ sudo ./front.sh
> MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer
> Team
>
> Linux n900 4.6.0-rc5-176733-g8b658a3-dirty
> media-ctl from today's git.
>
>> and tested with:
>>
>> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
>>
>
> I can't do -vo xv ... fails for me, probably due to X
> configuration. Does it work with -vo x11 for you?
>
yes, -vo x11 works under maemo.

In linux-n900 branch we have a patch that reserves memory for omapfb - 
see 
https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55

also, because of a change in 4.6, 
https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55#diff-072444ea67d2aca6b402458f50d20edeR125 
needs a change to DMA_MEMORY_IO and the if bellow needs the relevant 
change as well.

This is needed for -vo xv (and any omapfb video playback) to reliably 
work under maemo.

> For me it shows window with green interior. And complains about v4l2:
> select timeouts. (I enabled these in .config):
>
> +CONFIG_VIDEO_BUS_SWITCH=y
> +CONFIG_VIDEO_SMIAREGS=y
> +CONFIG_VIDEO_ET8EK8=y
> +CONFIG_VIDEOBUF2_CORE=y
> +CONFIG_VIDEOBUF2_MEMOPS=y
> +CONFIG_VIDEOBUF2_DMA_CONTIG=y
> +CONFIG_VIDEO_OMAP3=y
> +CONFIG_VIDEO_SMIAPP_PLL=y
> +CONFIG_VIDEO_SMIAPP=y
>
> Any ideas?
> 									Pavel
>

Try to build those as modules. Also, do you have all the needed patches 
besides those in the patchset?

See https://github.com/freemangordon/linux-n900/commits/v4.6-rc4-n900-camera

Also, is there anything related in dmesg log?

Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 17:09       ` Pavel Machek
@ 2016-04-25 17:21         ` Ivaylo Dimitrov
  2016-04-27 21:07           ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25 17:21 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media



On 25.04.2016 20:09, Pavel Machek wrote:
> Hi!
>
>>>> The needed pipeline could be made with:
>>>
>>> Would you have similar pipeline for the back camera? Autofocus and
>>> 5MPx makes it more interesting. I understand that different dts will
>>> be needed.
>>>
>>
>> Try with:
>>
>> media-ctl -r
>> media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
>> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
>> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
>> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
>>
>> media-ctl -V '"et8ek8 3-003e":0 [SGRBG10 864x656]'
>> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 864x656]'
>> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 864x656]'
>> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 864x656]'
>> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 864x656]'
>> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 800x600]'
>>
>>
>> mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6
>> -vo xv -vf screenshot tv://
>
> It fails with:
>
> pavel@n900:/my/tui/ofone/camera$ sudo ./back.sh
> Unable to parse link: Device or resource busy (16)

That shouldn't happen, there is something else wrong.

> MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer
> Team
>
> ...but as I'm using the original dts, it is expected...?
>
> Would you have dts suitable for the 5MPx camera?
>

Just change from strobe = <0>; to strobe = <1>; in isp node.

Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 17:17     ` Ivaylo Dimitrov
@ 2016-04-25 18:40       ` Pavel Machek
  2016-04-25 19:17         ` Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 18:40 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> >I can't do -vo xv ... fails for me, probably due to X
> >configuration. Does it work with -vo x11 for you?
> >
> yes, -vo x11 works under maemo.

Ok, good.

> In linux-n900 branch we have a patch that reserves memory for omapfb - see https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55
> 
> also, because of a change in 4.6, https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55#diff-072444ea67d2aca6b402458f50d20edeR125
> needs a change to DMA_MEMORY_IO and the if bellow needs the relevant change
> as well.

> This is needed for -vo xv (and any omapfb video playback) to reliably work
> under maemo.

I don't have that kind of acceleration working.

> >For me it shows window with green interior. And complains about v4l2:
> >select timeouts. (I enabled these in .config):
> >
> >+CONFIG_VIDEO_BUS_SWITCH=y
> >+CONFIG_VIDEO_SMIAREGS=y
> >+CONFIG_VIDEO_ET8EK8=y
> >+CONFIG_VIDEOBUF2_CORE=y
> >+CONFIG_VIDEOBUF2_MEMOPS=y
> >+CONFIG_VIDEOBUF2_DMA_CONTIG=y
> >+CONFIG_VIDEO_OMAP3=y
> >+CONFIG_VIDEO_SMIAPP_PLL=y
> >+CONFIG_VIDEO_SMIAPP=y
> >
> >Any ideas?
> >									Pavel
> >
> 
> Try to build those as modules. Also, do you have all the needed patches
> besides those in the patchset?
> 
> See https://github.com/freemangordon/linux-n900/commits/v4.6-rc4-n900-camera
> 
> Also, is there anything related in dmesg log?

Modules are tricky. I hate modules. I did patch with

https://lkml.org/lkml/2016/4/16/14
https://lkml.org/lkml/2016/4/16/33

+ the series. And yes, there seems to be explanation in the dmesg:

[ 6134.261993] DISPC: channel 0 xres 800 yres 480
[ 6134.262023] DISPC: pck 24000000
[ 6134.262023] DISPC: hsw 4 hfp 28 hbp 24 vsw 3 vfp 3 vbp 4
[ 6134.262023] DISPC: vsync_level 0 hsync_level 0 data_pclk_edge 1
de_level 1 sync_pclk_edge 1
[ 6134.262023] DISPC: hsync 28037Hz, vsync 57Hz
[ 6134.262054] DISPC: lck = 72000000 (1)
[ 6134.262054] DISPC: pck = 24000000 (3)
[ 6190.075103] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP resizer
[ 6192.075347] omap3isp 480bc000.isp: CCDC stop timeout!
[ 6192.075408] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP CCDC
[ 6292.293670] DISPC: dispc_runtime_put
[ 6292.293701] DISPC: dispc_save_context
[ 6292.293762] DISPC: context saved
[ 6292.294342] DSS: dss_save_context
[ 6292.294372] DSS: context saved
[ 6297.056976] DISPC: dispc_runtime_get
[ 6297.057067] DSS: dss_restore_context
[ 6297.057067] DSS: context restored
[ 6297.057159] DSS: set fck to 72000000
[ 6297.057159] DISPC: lck = 72000000 (1)
[ 6297.057159] DISPC: pck = 24000000 (3)

Let me check the github...
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 18:40       ` Pavel Machek
@ 2016-04-25 19:17         ` Ivaylo Dimitrov
  2016-04-25 20:41           ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25 19:17 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi,

On 25.04.2016 21:40, Pavel Machek wrote:
> Hi!
>
>>> I can't do -vo xv ... fails for me, probably due to X
>>> configuration. Does it work with -vo x11 for you?
>>>
>> yes, -vo x11 works under maemo.
>
> Ok, good.
>
>> In linux-n900 branch we have a patch that reserves memory for omapfb - see https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55
>>
>> also, because of a change in 4.6, https://github.com/freemangordon/linux-n900/commit/60f85dcb6a663efe687f58208861545d65210b55#diff-072444ea67d2aca6b402458f50d20edeR125
>> needs a change to DMA_MEMORY_IO and the if bellow needs the relevant change
>> as well.
>
>> This is needed for -vo xv (and any omapfb video playback) to reliably work
>> under maemo.
>
> I don't have that kind of acceleration working.
>
>>> For me it shows window with green interior. And complains about v4l2:
>>> select timeouts. (I enabled these in .config):
>>>
>>> +CONFIG_VIDEO_BUS_SWITCH=y
>>> +CONFIG_VIDEO_SMIAREGS=y
>>> +CONFIG_VIDEO_ET8EK8=y
>>> +CONFIG_VIDEOBUF2_CORE=y
>>> +CONFIG_VIDEOBUF2_MEMOPS=y
>>> +CONFIG_VIDEOBUF2_DMA_CONTIG=y
>>> +CONFIG_VIDEO_OMAP3=y
>>> +CONFIG_VIDEO_SMIAPP_PLL=y
>>> +CONFIG_VIDEO_SMIAPP=y
>>>
>>> Any ideas?
>>> 									Pavel
>>>
>>
>> Try to build those as modules. Also, do you have all the needed patches
>> besides those in the patchset?
>>
>> See https://github.com/freemangordon/linux-n900/commits/v4.6-rc4-n900-camera
>>
>> Also, is there anything related in dmesg log?
>
> Modules are tricky. I hate modules. I did patch with
>

All my testing so far was performed using modules, though it shouldn't 
make difference.

> https://lkml.org/lkml/2016/4/16/14
> https://lkml.org/lkml/2016/4/16/33
>

More stuff is needed, all those twl4030 regulator patches (already in 
linux-next) + DTS initial-mode patch (https://lkml.org/lkml/2016/4/17/78).

> + the series. And yes, there seems to be explanation in the dmesg:
>
> [ 6134.261993] DISPC: channel 0 xres 800 yres 480
> [ 6134.262023] DISPC: pck 24000000
> [ 6134.262023] DISPC: hsw 4 hfp 28 hbp 24 vsw 3 vfp 3 vbp 4
> [ 6134.262023] DISPC: vsync_level 0 hsync_level 0 data_pclk_edge 1
> de_level 1 sync_pclk_edge 1
> [ 6134.262023] DISPC: hsync 28037Hz, vsync 57Hz
> [ 6134.262054] DISPC: lck = 72000000 (1)
> [ 6134.262054] DISPC: pck = 24000000 (3)
> [ 6190.075103] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP resizer
> [ 6192.075347] omap3isp 480bc000.isp: CCDC stop timeout!
> [ 6192.075408] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP CCDC
> [ 6292.293670] DISPC: dispc_runtime_put
> [ 6292.293701] DISPC: dispc_save_context
> [ 6292.293762] DISPC: context saved
> [ 6292.294342] DSS: dss_save_context
> [ 6292.294372] DSS: context saved
> [ 6297.056976] DISPC: dispc_runtime_get
> [ 6297.057067] DSS: dss_restore_context
> [ 6297.057067] DSS: context restored
> [ 6297.057159] DSS: set fck to 72000000
> [ 6297.057159] DISPC: lck = 72000000 (1)
> [ 6297.057159] DISPC: pck = 24000000 (3)
>

That usually happens when strobe is incorrect or there is no signal from 
the camera.

> Let me check the github...

I really hope I didn't miss something when I sent the series. May I have 
your boot log?

Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 19:17         ` Ivaylo Dimitrov
@ 2016-04-25 20:41           ` Pavel Machek
  2016-04-25 20:53             ` Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 20:41 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> All my testing so far was performed using modules, though it shouldn't make
> difference.
> 
> >https://lkml.org/lkml/2016/4/16/14
> >https://lkml.org/lkml/2016/4/16/33
> >
> 
> More stuff is needed, all those twl4030 regulator patches (already in
> linux-next) + DTS initial-mode patch
> (https://lkml.org/lkml/2016/4/17/78).

Aha, that explains a lot. Dealing with -next would be tricky, I guess;
can I just pull from your camera branch?

https://github.com/freemangordon/linux-n900/tree/camera

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 20:41           ` Pavel Machek
@ 2016-04-25 20:53             ` Ivaylo Dimitrov
  2016-04-25 22:07               ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-25 20:53 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi,

On 25.04.2016 23:41, Pavel Machek wrote:
> Hi!
>
>> All my testing so far was performed using modules, though it shouldn't make
>> difference.
>>
>>> https://lkml.org/lkml/2016/4/16/14
>>> https://lkml.org/lkml/2016/4/16/33
>>>
>>
>> More stuff is needed, all those twl4030 regulator patches (already in
>> linux-next) + DTS initial-mode patch
>> (https://lkml.org/lkml/2016/4/17/78).
>
> Aha, that explains a lot. Dealing with -next would be tricky, I guess;
> can I just pull from your camera branch?
>
> https://github.com/freemangordon/linux-n900/tree/camera

I guess yes, though I am not sure all the patches there are compatible 
with userland different from maemo, so be careful. Also, the correct 
branch is v4.6-rc4-n900-camera.

Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 20:53             ` Ivaylo Dimitrov
@ 2016-04-25 22:07               ` Pavel Machek
  2016-04-26  4:21                 ` Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-25 22:07 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> >Hi!
> >
> >>All my testing so far was performed using modules, though it shouldn't make
> >>difference.
> >>
> >>>https://lkml.org/lkml/2016/4/16/14
> >>>https://lkml.org/lkml/2016/4/16/33
> >>>
> >>
> >>More stuff is needed, all those twl4030 regulator patches (already in
> >>linux-next) + DTS initial-mode patch
> >>(https://lkml.org/lkml/2016/4/17/78).
> >
> >Aha, that explains a lot. Dealing with -next would be tricky, I guess;
> >can I just pull from your camera branch?
> >
> >https://github.com/freemangordon/linux-n900/tree/camera
> 
> I guess yes, though I am not sure all the patches there are compatible with
> userland different from maemo, so be careful. Also, the correct branch is
> v4.6-rc4-n900-camera.

I tried v4.6-rc4-n900-camera, but got the same results: green mplayer
window, if I try to use front or back camera. Assuming
v4.6-rc4-n900-camera works for you, could I get your .config and list
of modules loaded during the test?

Thanks a lot,
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 22:07               ` Pavel Machek
@ 2016-04-26  4:21                 ` Ivaylo Dimitrov
  2016-04-27  8:30                   ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-26  4:21 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi,

On 26.04.2016 01:07, Pavel Machek wrote:
> Hi!
>
>>> Hi!
>>>
>>>> All my testing so far was performed using modules, though it shouldn't make
>>>> difference.
>>>>
>>>>> https://lkml.org/lkml/2016/4/16/14
>>>>> https://lkml.org/lkml/2016/4/16/33
>>>>>
>>>>
>>>> More stuff is needed, all those twl4030 regulator patches (already in
>>>> linux-next) + DTS initial-mode patch
>>>> (https://lkml.org/lkml/2016/4/17/78).
>>>
>>> Aha, that explains a lot. Dealing with -next would be tricky, I guess;
>>> can I just pull from your camera branch?
>>>
>>> https://github.com/freemangordon/linux-n900/tree/camera
>>
>> I guess yes, though I am not sure all the patches there are compatible with
>> userland different from maemo, so be careful. Also, the correct branch is
>> v4.6-rc4-n900-camera.
>
> I tried v4.6-rc4-n900-camera, but got the same results: green mplayer
> window, if I try to use front or back camera. Assuming
> v4.6-rc4-n900-camera works for you, could I get your .config and list
> of modules loaded during the test?
>

.config is 'make rx51_defconfig' from v4.6-rc4-n900-camera branch, with 
added:

CONFIG_VIDEO_BUS_SWITCH=m
CONFIG_VIDEO_SMIAREGS=m
CONFIG_VIDEO_ET8EK8=m

For some reason I have to do 'modprobe smiapp' after every reboot, 
before using cameras.

After taking a nap, a question came to my mind - what is that device 
you're using? As some early board versions use VAUX3 for cameras as well.

awk '{ printf "%s ",$1 }' /proc/modules results in:

smiapp smiapp_pll sha256_generic hmac drbg ansi_cprng ctr ccm vfat fat 
rfcomm sd_mod scsi_mod bnep bluetooth omaplfb pvrsrvkm ipv6 
bq2415x_charger uinput radio_platform_si4713 joydev cmt_speech hsi_char 
video_bus_switch arc4 wl1251_spi isp1704_charger wl1251 gpio_keys 
mac80211 smc91x mii cfg80211 omap3_isp videobuf2_v4l2 omap_wdt 
videobuf2_dma_contig omap_sham crc7 videobuf2_memops videobuf2_core 
tsc2005 tsc200x_core leds_lp5523 bq27xxx_battery_i2c si4713 adp1653 
tsl2563 bq27xxx_battery leds_lp55xx_common twl4030_wdt rtc_twl et8ek8 
v4l2_common smiaregs videodev twl4030_vibra ff_memless lis3lv02d_i2c 
lis3lv02d media input_polldev omap_ssi_port ti_soc_thermal nokia_modem 
ssi_protocol omap_ssi hsi rx51_battery

Regards,
Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (26 preceding siblings ...)
  2016-04-25 16:58   ` Pavel Machek
@ 2016-04-27  3:08   ` Sebastian Reichel
  2016-04-27  5:05     ` Ivaylo Dimitrov
  2016-06-17 16:42     ` Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working) Pavel Machek
  2016-06-24 16:21   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
  2016-08-27 13:48   ` fcam-dev support for new kernels -- " Pavel Machek
  29 siblings, 2 replies; 102+ messages in thread
From: Sebastian Reichel @ 2016-04-27  3:08 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, pali.rohar, pavel, linux-media

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

Hi,

On Mon, Apr 25, 2016 at 12:08:00AM +0300, Ivaylo Dimitrov wrote:
> Those patch series make cameras on Nokia N900 partially working.
> Some more patches are needed, but I've already sent them for
> upstreaming so they are not part of the series:
> 
> https://lkml.org/lkml/2016/4/16/14
> https://lkml.org/lkml/2016/4/16/33
> 
> As omap3isp driver supports only one endpoint on ccp2 interface,
> but cameras on N900 require different strobe settings, so far
> it is not possible to have both cameras correctly working with
> the same board DTS. DTS patch in the series has the correct
> settings for the front camera. This is a problem still to be
> solved.
> 
> The needed pipeline could be made with:
> 
> media-ctl -r
> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
> 
> and tested with:
> 
> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://

4.6-rc4 + twl regulator patch + the patches mentioned above + this
patchset (I put everything together here [0]) do _not_ work for me.
The error matches what I have seen when I was working on it: No
image data seems to be received by the ISP. For example there are
no related IRQs:

root@n900:~# cat /proc/interrupts  | grep ISP
 40:          0      INTC  24 Edge      480bd400.mmu, OMAP3 ISP

I tested with mpv and yavta (yavta --capture=8 --pause --skip 0
--format UYVY --size 656x488 /dev/video6)

[0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27  3:08   ` Sebastian Reichel
@ 2016-04-27  5:05     ` Ivaylo Dimitrov
  2016-04-27  6:57       ` Ivaylo Dimitrov
  2016-06-17 16:42     ` Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working) Pavel Machek
  1 sibling, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-27  5:05 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: sakari.ailus, pali.rohar, pavel, linux-media

Hi,

On 27.04.2016 06:08, Sebastian Reichel wrote:
> Hi,
>
> On Mon, Apr 25, 2016 at 12:08:00AM +0300, Ivaylo Dimitrov wrote:
>> Those patch series make cameras on Nokia N900 partially working.
>> Some more patches are needed, but I've already sent them for
>> upstreaming so they are not part of the series:
>>
>> https://lkml.org/lkml/2016/4/16/14
>> https://lkml.org/lkml/2016/4/16/33
>>
>> As omap3isp driver supports only one endpoint on ccp2 interface,
>> but cameras on N900 require different strobe settings, so far
>> it is not possible to have both cameras correctly working with
>> the same board DTS. DTS patch in the series has the correct
>> settings for the front camera. This is a problem still to be
>> solved.
>>
>> The needed pipeline could be made with:
>>
>> media-ctl -r
>> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
>> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
>> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
>> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
>> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
>> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
>> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
>> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
>> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
>> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
>>
>> and tested with:
>>
>> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
>
> 4.6-rc4 + twl regulator patch + the patches mentioned above + this
> patchset (I put everything together here [0]) do _not_ work for me.
> The error matches what I have seen when I was working on it: No
> image data seems to be received by the ISP. For example there are
> no related IRQs:
>
> root@n900:~# cat /proc/interrupts  | grep ISP
>   40:          0      INTC  24 Edge      480bd400.mmu, OMAP3 ISP
>
> I tested with mpv and yavta (yavta --capture=8 --pause --skip 0
> --format UYVY --size 656x488 /dev/video6)
>
> [0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
>

Ok, going to diff with my tree to see what I have missed to send in the 
patchset

Thanks

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27  5:05     ` Ivaylo Dimitrov
@ 2016-04-27  6:57       ` Ivaylo Dimitrov
  2016-04-27 16:42         ` Sebastian Reichel
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-27  6:57 UTC (permalink / raw)
  To: Sebastian Reichel, pavel; +Cc: sakari.ailus, pali.rohar, linux-media

Hi,

On 27.04.2016 08:05, Ivaylo Dimitrov wrote:
> Hi,
>
> On 27.04.2016 06:08, Sebastian Reichel wrote:
>> Hi,
>>
>> On Mon, Apr 25, 2016 at 12:08:00AM +0300, Ivaylo Dimitrov wrote:
>>> Those patch series make cameras on Nokia N900 partially working.
>>> Some more patches are needed, but I've already sent them for
>>> upstreaming so they are not part of the series:
>>>
>>> https://lkml.org/lkml/2016/4/16/14
>>> https://lkml.org/lkml/2016/4/16/33
>>>
>>> As omap3isp driver supports only one endpoint on ccp2 interface,
>>> but cameras on N900 require different strobe settings, so far
>>> it is not possible to have both cameras correctly working with
>>> the same board DTS. DTS patch in the series has the correct
>>> settings for the front camera. This is a problem still to be
>>> solved.
>>>
>>> The needed pipeline could be made with:
>>>
>>> media-ctl -r
>>> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
>>> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
>>> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
>>> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
>>> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
>>> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
>>> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488
>>> (0,0)/648x488 (0,0)/648x488]'
>>> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488
>>> (0,0)/648x488]'
>>> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
>>> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
>>> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
>>> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
>>> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
>>>
>>> and tested with:
>>>
>>> mplayer -tv
>>> driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo
>>> xv -vf screenshot tv://
>>
>> 4.6-rc4 + twl regulator patch + the patches mentioned above + this
>> patchset (I put everything together here [0]) do _not_ work for me.
>> The error matches what I have seen when I was working on it: No
>> image data seems to be received by the ISP. For example there are
>> no related IRQs:
>>
>> root@n900:~# cat /proc/interrupts  | grep ISP
>>   40:          0      INTC  24 Edge      480bd400.mmu, OMAP3 ISP
>>
>> I tested with mpv and yavta (yavta --capture=8 --pause --skip 0
>> --format UYVY --size 656x488 /dev/video6)
>>
>> [0]
>> https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
>>
>>
>
> Ok, going to diff with my tree to see what I have missed to send in the
> patchset
>

Now, that's getting weird.

I cloned n900-camera-ivo, copied rx51_defconfig from my tree, added:

CONFIG_VIDEO_SMIAREGS=m
CONFIG_VIDEO_ET8EK8=m
CONFIG_VIDEO_BUS_SWITCH=m

to it, make mrproper, built the kernel using rx51_defconfig and made 
initrd for rescueos, so to be sure that maemo5 did not influence cameras 
somehow.

Booted the device with flasher3.5:

sudo flasher-3.5 -k zImage -n ramfs -l -b"rootdelay root=/dev/ram0 
mtdoops.mtddev=log log_buf_len=1M"

ivo@ivo-H81M-S2PV:~/maemo/rescueos$ sudo ifconfig usb0 192.168.2.14
ivo@ivo-H81M-S2PV:~/maemo/rescueos$ telnet 192.168.2.15
Trying 192.168.2.15...
Connected to 192.168.2.15.
Escape character is '^]'.

rescueos login: root
Password:
~$ modprobe smiapp
~$ cd /camera/
/camera$ export LD_LIBRARY_PATH=./
/camera$ ./media-ctl -r
/camera$ ./media-ctl -l '"vs6555 binner 2-0010":1 -> 
"video-bus-switch":2 [1]'
/camera$ ./media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
/camera$ ./media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
/camera$ ./media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
/camera$ ./media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
/camera$ ./media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer 
output":0 [1]'
/camera$ ./media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 
(0,0)/648x488 (0,0)/648x488]'
/camera$ ./media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 
(0,0)/648x488 (0,0)/648x488]'
/camera$ ./media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
/camera$ ./media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
/camera$ ./media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
/camera$ ./media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
/camera$ ./media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
/camera$ ./yavta --capture=8 --pause --skip 0 --format UYVY --size 
656x488 /dev/video6
Device /dev/video6 opened.
Device `OMAP3 ISP resizer output' on `media' is a video capture device.
Video format set: UYVY (59565955) 656x488 (stride 1312) buffer size 640256
Video format: UYVY (59565955) 656x488 (stride 1312) buffer size 640256
8 buffers requested.
length: 640256 offset: 0 timestamp type: monotonic
Buffer 0 mapped at address 0xb6de5000.
length: 640256 offset: 643072 timestamp type: monotonic
Buffer 1 mapped at address 0xb6d48000.
length: 640256 offset: 1286144 timestamp type: monotonic
Buffer 2 mapped at address 0xb6cab000.
length: 640256 offset: 1929216 timestamp type: monotonic
Buffer 3 mapped at address 0xb6c0e000.
length: 640256 offset: 2572288 timestamp type: monotonic
Buffer 4 mapped at address 0xb6b71000.
length: 640256 offset: 3215360 timestamp type: monotonic
Buffer 5 mapped at address 0xb6ad4000.
length: 640256 offset: 3858432 timestamp type: monotonic
Buffer 6 mapped at address 0xb6a37000.
length: 640256 offset: 4501504 timestamp type: monotonic
Buffer 7 mapped at address 0xb699a000.
Press enter to start capture

0 (0) [-] 0 640256 bytes 211.742779 211.742932 28.518 fps
1 (1) [-] 1 640256 bytes 211.808148 211.808331 15.298 fps
2 (2) [-] 2 640256 bytes 211.873547 211.873669 15.291 fps
3 (3) [-] 3 640256 bytes 211.938946 211.939099 15.291 fps
4 (4) [-] 4 640256 bytes 212.004345 212.004498 15.291 fps
5 (5) [-] 5 640256 bytes 212.069714 212.069836 15.298 fps
6 (6) [-] 6 640256 bytes 212.135113 212.135296 15.291 fps
7 (7) [-] 7 640256 bytes 212.200512 212.200665 15.291 fps
Captured 8 frames in 0.492950 seconds (16.228812 fps, 10390594.234990 B/s).
8 buffers released.

/camera$ cat /proc/interrupts | grep ISP
  40:         30      INTC  24 Edge      480bd400.mmu, OMAP3 ISP
  41:          0      INTC  25 Edge      OMAP DISPC
/camera$ uname -a
Linux rescueos 4.6.0-rc4+ #1 PREEMPT Wed Apr 27 08:55:02 EEST 2016 
armv7l GNU/Linux

I you want to try it, zImage and initrd are on 
http://46.249.74.23/linux/camera-n900/

Please, Sebastian and Pavel, make sure you're not using some development 
devices, old board versions need VAUX3 enabled as well, and this is not 
supported in the $subject patchset. I guess you may try to make VAUX3 
always-on in board DTS if that's the case, but I've never tested that, 
my device is a production one.

Thanks,
Ivo


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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-26  4:21                 ` Ivaylo Dimitrov
@ 2016-04-27  8:30                   ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-27  8:30 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> After taking a nap, a question came to my mind - what is that device you're
> using? As some early board versions use VAUX3 for cameras as well.

I got used N900 from a friend. It had czech keyboard, originally, so I
believe it should be regular version, not early one...

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27  6:57       ` Ivaylo Dimitrov
@ 2016-04-27 16:42         ` Sebastian Reichel
  2016-04-27 16:45           ` Pavel Machek
                             ` (2 more replies)
  0 siblings, 3 replies; 102+ messages in thread
From: Sebastian Reichel @ 2016-04-27 16:42 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: pavel, sakari.ailus, pali.rohar, linux-media

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

Hi,

On Wed, Apr 27, 2016 at 09:57:51AM +0300, Ivaylo Dimitrov wrote:
> >>https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
> >
> >Ok, going to diff with my tree to see what I have missed to send in the
> >patchset
> 
> Now, that's getting weird.

[...]

> I you want to try it, zImage and initrd are on
> http://46.249.74.23/linux/camera-n900/

The zImage + initrd works with the steps you described below. I
received a completly black image, but at least there are interrupts
and yavta is happy (=> it does not hang).

For reference I configured the pipeline using media-ctl and used
this to acquire the image:

./yavta --capture=1 --format UYVY --file="/tmp/frame.uyvy" --size 656x488 /dev/video6

Then I copied the file to my notebook using the FTP server coming
with your initrd and displayed it using "display" from imagemagick:

display -size 656x488 -colorspace rgb frame.uyvy

Before analysing why there is only a black image let's start
with the no image at all problem, though.

> I cloned n900-camera-ivo, copied rx51_defconfig from my tree, added:
> 
> CONFIG_VIDEO_SMIAREGS=m
> CONFIG_VIDEO_ET8EK8=m
> CONFIG_VIDEO_BUS_SWITCH=m
> 
> to it, make mrproper, built the kernel using rx51_defconfig and made initrd
> for rescueos, so to be sure that maemo5 did not influence cameras somehow.

Ok, so there is probably a problem when some things are not built as
modules.

> I cloned n900-camera-ivo, copied rx51_defconfig from my tree, added:
> 
> CONFIG_VIDEO_SMIAREGS=m
> CONFIG_VIDEO_ET8EK8=m
> CONFIG_VIDEO_BUS_SWITCH=m
> 
> to it, make mrproper, built the kernel using rx51_defconfig and made initrd
> for rescueos, so to be sure that maemo5 did not influence cameras somehow.

I will test your kernel + your modules with my userspace, so
that I know for sure, that my userland behaves correctly.

Can you try if your config still works if you configure
CONFIG_VIDEO_OMAP3=y, but leaving the sensors configured
as modules? I will try the reverse process (using my config
and moving config options to =m).

> ~$ modprobe smiapp

modprobing smiapp resulted in a kernel message about a missing
symbol btw. I currently don't remember which one and it's no
longer in dmesg due to ISP debug messages.

> Please, Sebastian and Pavel, make sure you're not using some development
> devices, old board versions need VAUX3 enabled as well, and this is not
> supported in the $subject patchset. I guess you may try to make VAUX3
> always-on in board DTS if that's the case, but I've never tested that, my
> device is a production one.

I don't have pre-production N900s. The phone I use for development
is HW revision 2101 with Finish keyboard layout. Apart from that
I have my productive phone, which is rev 2204 with German layout.

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 16:42         ` Sebastian Reichel
@ 2016-04-27 16:45           ` Pavel Machek
  2016-04-27 16:59             ` Sebastian Reichel
  2016-04-27 17:12           ` Ивайло Димитров
  2016-04-27 20:30           ` Pavel Machek
  2 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-04-27 16:45 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

Hi!

> I don't have pre-production N900s. The phone I use for development
> is HW revision 2101 with Finish keyboard layout. Apart from that
> I have my productive phone, which is rev 2204 with German layout.

How do you check hw revision?

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 16:45           ` Pavel Machek
@ 2016-04-27 16:59             ` Sebastian Reichel
  2016-05-02  7:06               ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Sebastian Reichel @ 2016-04-27 16:59 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

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

Hi,

On Wed, Apr 27, 2016 at 06:45:29PM +0200, Pavel Machek wrote:
> > I don't have pre-production N900s. The phone I use for development
> > is HW revision 2101 with Finish keyboard layout. Apart from that
> > I have my productive phone, which is rev 2204 with German layout.
> 
> How do you check hw revision?

0xFFFF tells you the HW revision (e.g. when executed with -I, but
also during normal kernel loading operation). Apart from that there
is /proc/cpuinfo.

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 16:42         ` Sebastian Reichel
  2016-04-27 16:45           ` Pavel Machek
@ 2016-04-27 17:12           ` Ивайло Димитров
  2016-04-27 19:05             ` Pavel Machek
  2016-04-29  0:05             ` Sebastian Reichel
  2016-04-27 20:30           ` Pavel Machek
  2 siblings, 2 replies; 102+ messages in thread
From: Ивайло Димитров @ 2016-04-27 17:12 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: pavel, sakari.ailus, pali.rohar, linux-media

Hi,

On Wed Apr 27 19:42:56 2016 Sebastian Reichel <sre@kernel.org> wrote:
> Hi,
> 
> On Wed, Apr 27, 2016 at 09:57:51AM +0300, Ivaylo Dimitrov wrote:
> > > > https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
> > > 
> > > Ok, going to diff with my tree to see what I have missed to send in
> > > the patchset
> > 
> > Now, that's getting weird.
> 
> [...]
> The zImage + initrd works with the steps you described below. I

Great!

> received a completly black image, but at least there are interrupts
> and yavta is happy (=> it does not hang).
>

The black image is because by default exposure and gain are set to 0 :). 
Use yavta to set the appropriate controls. You can
also enable test patterns from there.

> 
> Can you try if your config still works if you configure
> CONFIG_VIDEO_OMAP3=y, but leaving the sensors configured
> as modules? I will try the reverse process (using my config
> and moving config options to =m).
>

Will try to find time later today.
 
> > ~$ modprobe smiapp
> 
> modprobing smiapp resulted in a kernel message about a missing
> symbol btw. I currently don't remember which one and it's no
> longer in dmesg due to ISP debug messages.
>

Never seen such missing symbols.

> > Please, Sebastian and Pavel, make sure you're not using some
> > development devices, old board versions need VAUX3 enabled as well,
> > and this is not supported in the $subject patchset. I guess you may
> > try to make VAUX3 always-on in board DTS if that's the case, but I've
> > never tested that, my device is a production one.
> 
> I don't have pre-production N900s. The phone I use for development
> is HW revision 2101 with Finish keyboard layout. Apart from that
> I have my productive phone, which is rev 2204 with German layout.
> 

The one I use for testing is 2204, but I guess it is
irrelevant now you have it finally working.

Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 17:12           ` Ивайло Димитров
@ 2016-04-27 19:05             ` Pavel Machek
  2016-04-29  0:05             ` Sebastian Reichel
  1 sibling, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-27 19:05 UTC (permalink / raw)
  To: Ивайло
	Димитров
  Cc: Sebastian Reichel, sakari.ailus, pali.rohar, linux-media

Hi!

> > On Wed, Apr 27, 2016 at 09:57:51AM +0300, Ivaylo Dimitrov wrote:
> > > > > https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
> > > > 
> > > > Ok, going to diff with my tree to see what I have missed to send in
> > > > the patchset
> > > 
> > > Now, that's getting weird.
> > 
> > [...]
> > The zImage + initrd works with the steps you described below. I
> 
> Great!
> 
> > received a completly black image, but at least there are interrupts
> > and yavta is happy (=> it does not hang).
> >
> 
> The black image is because by default exposure and gain are set to 0 :). 
> Use yavta to set the appropriate controls. You can
> also enable test patterns from there.

Uff.. .there's a lot of controls to play with ;-).

So I did this...

while true; do ./yavta --capture=1 --skip 0 --format UYVY --size
656x488 /dev/video6 --file=/tmp/delme# && cat /tmp/delme000001 >
/dev/fb0; done

...and kernel certainly did not like that. After a while:

[ 3468.118774] ---[ end trace 70aa4a6442fc6916 ]---
[ 3468.137084] Address Hole seen by CAM  at address 0
[ 3468.137084] ------------[ cut here ]------------
[ 3468.137084] WARNING: CPU: 0 PID: 4974 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.137207] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.137207] CPU: 0 PID: 4974 Comm: init Tainted: G        W
4.6.0-rc4+ #1
[ 3468.137207] Hardware name: Nokia RX-51 board
[ 3468.137237] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.137237] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.137268] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.137298] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.137298] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.137329] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.137329] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.137359] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.137359] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.137390] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.137390] [<c054da14>] (__irq_svc) from [<c0129044>]
(__do_softirq+0x5c/0x208)
[ 3468.137420] [<c0129044>] (__do_softirq) from [<c0129448>]
(irq_exit+0x80/0xe4)
[ 3468.137451] [<c0129448>] (irq_exit) from [<c01540c0>]
(__handle_domain_irq+0x88/0xa8)
[ 3468.137451] [<c01540c0>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.137481] [<c054da14>] (__irq_svc) from [<c0152d50>]
(console_unlock+0x3f4/0x4fc)
[ 3468.137481] [<c0152d50>] (console_unlock) from [<c038e2d4>]
(do_con_write.part.10+0x1d80/0x1dac)
[ 3468.137512] [<c038e2d4>] (do_con_write.part.10) from [<c038e374>]
(con_write+0x30/0x48)
[ 3468.137512] [<c038e374>] (con_write) from [<c037a014>]
(do_output_char+0x9c/0x1e4)
[ 3468.137542] [<c037a014>] (do_output_char) from [<c037b1d4>]
(n_tty_write+0x2ac/0x430)
[ 3468.137573] [<c037b1d4>] (n_tty_write) from [<c03775ec>]
(tty_write+0x1b0/0x240)
[ 3468.137573] [<c03775ec>] (tty_write) from [<c01db128>]
(__vfs_write+0x2c/0xd4)
[ 3468.137603] [<c01db128>] (__vfs_write) from [<c01dc710>]
(vfs_write+0xa0/0x18c)
[ 3468.137603] [<c01dc710>] (vfs_write) from [<c01dc9f4>]
(SyS_write+0x3c/0x78)
[ 3468.137603] [<c01dc9f4>] (SyS_write) from [<c0107160>]
(ret_fast_syscall+0x0/0x3c)
[ 3468.137634] ---[ end trace 70aa4a6442fc6917 ]---
[ 3468.144317] omap3isp 480bc000.isp: CCP2 err:8010817
[ 3468.144317] Address Hole seen by CAM  at address b00
[ 3468.144317] ------------[ cut here ]------------
[ 3468.144348] WARNING: CPU: 0 PID: 4973 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.144439] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.144439] CPU: 0 PID: 4973 Comm: yavta Tainted: G        W
4.6.0-rc4+ #1
[ 3468.144470] Hardware name: Nokia RX-51 board
[ 3468.144470] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.144500] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.144500] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.144531] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.144561] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.144561] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.144592] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.144592] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.144622] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.144622] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.144714] [<c054da14>] (__irq_svc) from [<bf20e9dc>]
(omap3isp_ccdc_busy+0xc/0x14 [omap3_isp])
[ 3468.144866] [<bf20e9dc>] (omap3isp_ccdc_busy [omap3_isp]) from
[<bf205a9c>] (isp_pipeline_wait_ccdc+0x54/0x60 [omap3_isp])
[ 3468.144989] [<bf205a9c>] (isp_pipeline_wait_ccdc [omap3_isp]) from
[<bf205058>] (isp_pipeline_wait+0x28/0x4c [omap3_isp])
[ 3468.145111] [<bf205058>] (isp_pipeline_wait [omap3_isp]) from
[<bf2056d8>] (isp_pipeline_disable+0x154/0x224 [omap3_isp])
[ 3468.145233] [<bf2056d8>] (isp_pipeline_disable [omap3_isp]) from
[<bf2065a0>] (omap3isp_pipeline_set_stream+0x14/0x2c [omap3_isp])
[ 3468.145355] [<bf2065a0>] (omap3isp_pipeline_set_stream [omap3_isp])
from [<bf2092d0>] (isp_video_streamoff+0xcc/0x124 [omap3_isp])
[ 3468.145477] [<bf2092d0>] (isp_video_streamoff [omap3_isp]) from
[<bf209354>] (isp_video_release+0x2c/0x78 [omap3_isp])
[ 3468.145690] [<bf209354>] (isp_video_release [omap3_isp]) from
[<bf00d300>] (v4l2_release+0x30/0x6c [videodev])
[ 3468.145812] [<bf00d300>] (v4l2_release [videodev]) from
[<c01dd35c>] (__fput+0xd4/0x1f0)
[ 3468.145843] [<c01dd35c>] (__fput) from [<c013e030>]
(task_work_run+0x74/0x88)
[ 3468.145843] [<c013e030>] (task_work_run) from [<c01283c8>]
(do_exit+0x3e8/0x920)
[ 3468.145874] [<c01283c8>] (do_exit) from [<c0128a50>]
(do_group_exit+0xb8/0xfc)
[ 3468.145874] [<c0128a50>] (do_group_exit) from [<c013274c>]
(get_signal+0x604/0x680)
[ 3468.145904] [<c013274c>] (get_signal) from [<c010de54>]
(do_signal+0x84/0x5d0)
[ 3468.145935] [<c010de54>] (do_signal) from [<c0109958>]
(do_work_pending+0x48/0xb8)
[ 3468.145935] [<c0109958>] (do_work_pending) from [<c01071b4>]
(slow_work_pending+0xc/0x20)
[ 3468.145935] ---[ end trace 70aa4a6442fc6918 ]---
[ 3468.167877] Address Hole seen by CAM  at address 0
[ 3468.167877] ------------[ cut here ]------------
[ 3468.167907] WARNING: CPU: 0 PID: 4973 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.167999] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.167999] CPU: 0 PID: 4973 Comm: yavta Tainted: G        W
4.6.0-rc4+ #1
[ 3468.168029] Hardware name: Nokia RX-51 board
[ 3468.168029] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.168060] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.168090] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.168090] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.168121] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.168121] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.168151] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.168182] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.168182] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.168212] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.168273] [<c054da14>] (__irq_svc) from [<bf21530c>]
(hist_busy+0xc/0x14 [omap3_isp])
[ 3468.168426] [<bf21530c>] (hist_busy [omap3_isp]) from [<bf213dc4>]
(omap3isp_stat_pcr_busy+0x10/0x14 [omap3_isp])
[ 3468.168548] [<bf213dc4>] (omap3isp_stat_pcr_busy [omap3_isp]) from
[<bf214198>] (omap3isp_stat_busy+0xc/0x2c [omap3_isp])
[ 3468.168670] [<bf214198>] (omap3isp_stat_busy [omap3_isp]) from
[<bf205a88>] (isp_pipeline_wait_ccdc+0x40/0x60 [omap3_isp])
[ 3468.168792] [<bf205a88>] (isp_pipeline_wait_ccdc [omap3_isp]) from
[<bf205058>] (isp_pipeline_wait+0x28/0x4c [omap3_isp])
[ 3468.168914] [<bf205058>] (isp_pipeline_wait [omap3_isp]) from
[<bf2056d8>] (isp_pipeline_disable+0x154/0x224 [omap3_isp])
[ 3468.169036] [<bf2056d8>] (isp_pipeline_disable [omap3_isp]) from
[<bf2065a0>] (omap3isp_pipeline_set_stream+0x14/0x2c [omap3_isp])
[ 3468.169158] [<bf2065a0>] (omap3isp_pipeline_set_stream [omap3_isp])
from [<bf2092d0>] (isp_video_streamoff+0xcc/0x124 [omap3_isp])
[ 3468.169281] [<bf2092d0>] (isp_video_streamoff [omap3_isp]) from
[<bf209354>] (isp_video_release+0x2c/0x78 [omap3_isp])
[ 3468.169494] [<bf209354>] (isp_video_release [omap3_isp]) from
[<bf00d300>] (v4l2_release+0x30/0x6c [videodev])
[ 3468.169616] [<bf00d300>] (v4l2_release [videodev]) from
[<c01dd35c>] (__fput+0xd4/0x1f0)
[ 3468.169616] [<c01dd35c>] (__fput) from [<c013e030>]
(task_work_run+0x74/0x88)
[ 3468.169647] [<c013e030>] (task_work_run) from [<c01283c8>]
(do_exit+0x3e8/0x920)
[ 3468.169647] [<c01283c8>] (do_exit) from [<c0128a50>]
(do_group_exit+0xb8/0xfc)
[ 3468.169677] [<c0128a50>] (do_group_exit) from [<c013274c>]
(get_signal+0x604/0x680)
[ 3468.169708] [<c013274c>] (get_signal) from [<c010de54>]
(do_signal+0x84/0x5d0)
[ 3468.169708] [<c010de54>] (do_signal) from [<c0109958>]
(do_work_pending+0x48/0xb8)
[ 3468.169738] [<c0109958>] (do_work_pending) from [<c01071b4>]
(slow_work_pending+0xc/0x20)
[ 3468.169738] ---[ end trace 70aa4a6442fc6919 ]---
[ 3468.188049] omap3isp 480bc000.isp: CCP2 err:8010817
[ 3468.188110] Address Hole seen by CAM  at address b00
[ 3468.188110] ------------[ cut here ]------------
[ 3468.188140] WARNING: CPU: 0 PID: 4974 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.188232] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.188232] CPU: 0 PID: 4974 Comm: init Tainted: G        W
4.6.0-rc4+ #1
[ 3468.188232] Hardware name: Nokia RX-51 board
[ 3468.188262] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.188293] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.188293] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.188323] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.188323] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.188354] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.188385] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.188385] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.188415] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.188415] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.188446] [<c054da14>] (__irq_svc) from [<c0129044>]
(__do_softirq+0x5c/0x208)
[ 3468.188446] [<c0129044>] (__do_softirq) from [<c0129448>]
(irq_exit+0x80/0xe4)
[ 3468.188476] [<c0129448>] (irq_exit) from [<c01540c0>]
(__handle_domain_irq+0x88/0xa8)
[ 3468.188476] [<c01540c0>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.188507] [<c054da14>] (__irq_svc) from [<c0152d50>]
(console_unlock+0x3f4/0x4fc)
[ 3468.188537] [<c0152d50>] (console_unlock) from [<c038e2d4>]
(do_con_write.part.10+0x1d80/0x1dac)
[ 3468.188537] [<c038e2d4>] (do_con_write.part.10) from [<c038e374>]
(con_write+0x30/0x48)
[ 3468.188568] [<c038e374>] (con_write) from [<c037a014>]
(do_output_char+0x9c/0x1e4)
[ 3468.188598] [<c037a014>] (do_output_char) from [<c037b1d4>]
(n_tty_write+0x2ac/0x430)
[ 3468.188598] [<c037b1d4>] (n_tty_write) from [<c03775ec>]
(tty_write+0x1b0/0x240)
[ 3468.188629] [<c03775ec>] (tty_write) from [<c01db128>]
(__vfs_write+0x2c/0xd4)
[ 3468.188629] [<c01db128>] (__vfs_write) from [<c01dc710>]
(vfs_write+0xa0/0x18c)
[ 3468.188659] [<c01dc710>] (vfs_write) from [<c01dc9f4>]
(SyS_write+0x3c/0x78)
[ 3468.188659] [<c01dc9f4>] (SyS_write) from [<c0107160>]
(ret_fast_syscall+0x0/0x3c)
[ 3468.188659] ---[ end trace 70aa4a6442fc691a ]---
[ 3468.200592] Address Hole seen by CAM  at address 0
[ 3468.200592] ------------[ cut here ]------------
[ 3468.200592] WARNING: CPU: 0 PID: 4973 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.200683] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.200714] CPU: 0 PID: 4973 Comm: yavta Tainted: G        W
4.6.0-rc4+ #1
[ 3468.200714] Hardware name: Nokia RX-51 board
[ 3468.200744] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.200744] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.200775] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.200775] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.200805] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.200836] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.200836] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.200866] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.200866] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.200897] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.200988] [<c054da14>] (__irq_svc) from [<bf2141b4>]
(omap3isp_stat_busy+0x28/0x2c [omap3_isp])
[ 3468.201110] [<bf2141b4>] (omap3isp_stat_busy [omap3_isp]) from
[<bf205a88>] (isp_pipeline_wait_ccdc+0x40/0x60 [omap3_isp])
[ 3468.201232] [<bf205a88>] (isp_pipeline_wait_ccdc [omap3_isp]) from
[<bf205058>] (isp_pipeline_wait+0x28/0x4c [omap3_isp])
[ 3468.201354] [<bf205058>] (isp_pipeline_wait [omap3_isp]) from
[<bf2056d8>] (isp_pipeline_disable+0x154/0x224 [omap3_isp])
[ 3468.201477] [<bf2056d8>] (isp_pipeline_disable [omap3_isp]) from
[<bf2065a0>] (omap3isp_pipeline_set_stream+0x14/0x2c [omap3_isp])
[ 3468.201599] [<bf2065a0>] (omap3isp_pipeline_set_stream [omap3_isp])
from [<bf2092d0>] (isp_video_streamoff+0xcc/0x124 [omap3_isp])
[ 3468.201721] [<bf2092d0>] (isp_video_streamoff [omap3_isp]) from
[<bf209354>] (isp_video_release+0x2c/0x78 [omap3_isp])
[ 3468.201934] [<bf209354>] (isp_video_release [omap3_isp]) from
[<bf00d300>] (v4l2_release+0x30/0x6c [videodev])
[ 3468.202056] [<bf00d300>] (v4l2_release [videodev]) from
[<c01dd35c>] (__fput+0xd4/0x1f0)
[ 3468.202056] [<c01dd35c>] (__fput) from [<c013e030>]
(task_work_run+0x74/0x88)
[ 3468.202087] [<c013e030>] (task_work_run) from [<c01283c8>]
(do_exit+0x3e8/0x920)
[ 3468.202117] [<c01283c8>] (do_exit) from [<c0128a50>]
(do_group_exit+0xb8/0xfc)
[ 3468.202117] [<c0128a50>] (do_group_exit) from [<c013274c>]
(get_signal+0x604/0x680)
[ 3468.202148] [<c013274c>] (get_signal) from [<c010de54>]
(do_signal+0x84/0x5d0)
[ 3468.202148] [<c010de54>] (do_signal) from [<c0109958>]
(do_work_pending+0x48/0xb8)
[ 3468.202178] [<c0109958>] (do_work_pending) from [<c01071b4>]
(slow_work_pending+0xc/0x20)
[ 3468.202178] ---[ end trace 70aa4a6442fc691b ]---
[ 3468.218109] omap3isp 480bc000.isp: CCP2 err:8010817
[ 3468.218109] Address Hole seen by CAM  at address b00
[ 3468.218109] ------------[ cut here ]------------
[ 3468.218139] WARNING: CPU: 0 PID: 4974 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.218231] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.218231] CPU: 0 PID: 4974 Comm: init Tainted: G        W
4.6.0-rc4+ #1
[ 3468.218231] Hardware name: Nokia RX-51 board
[ 3468.218261] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.218292] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.218292] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.218322] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.218353] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.218353] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.218383] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.218383] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.218414] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.218414] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.218444] [<c054da14>] (__irq_svc) from [<c0152d50>]
(console_unlock+0x3f4/0x4fc)
[ 3468.218475] [<c0152d50>] (console_unlock) from [<c038e2d4>]
(do_con_write.part.10+0x1d80/0x1dac)
[ 3468.218475] [<c038e2d4>] (do_con_write.part.10) from [<c038e374>]
(con_write+0x30/0x48)
[ 3468.218505] [<c038e374>] (con_write) from [<c037a014>]
(do_output_char+0x9c/0x1e4)
[ 3468.218536] [<c037a014>] (do_output_char) from [<c037b1d4>]
(n_tty_write+0x2ac/0x430)
[ 3468.218536] [<c037b1d4>] (n_tty_write) from [<c03775ec>]
(tty_write+0x1b0/0x240)
[ 3468.218566] [<c03775ec>] (tty_write) from [<c01db128>]
(__vfs_write+0x2c/0xd4)
[ 3468.218566] [<c01db128>] (__vfs_write) from [<c01dc710>]
(vfs_write+0xa0/0x18c)
[ 3468.218566] [<c01dc710>] (vfs_write) from [<c01dc9f4>]
(SyS_write+0x3c/0x78)
[ 3468.218597] [<c01dc9f4>] (SyS_write) from [<c0107160>]
(ret_fast_syscall+0x0/0x3c)
[ 3468.218597] ---[ end trace 70aa4a6442fc691c ]---
[ 3468.233276] Address Hole seen by CAM  at address 0
[ 3468.233276] ------------[ cut here ]------------
[ 3468.233306] WARNING: CPU: 0 PID: 4973 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.233398] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.233398] CPU: 0 PID: 4973 Comm: yavta Tainted: G        W
4.6.0-rc4+ #1
[ 3468.233398] Hardware name: Nokia RX-51 board
[ 3468.233428] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.233459] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.233459] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.233489] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.233520] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.233520] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.233551] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.233551] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.233581] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.233581] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.233673] [<c054da14>] (__irq_svc) from [<bf20e9dc>]
(omap3isp_ccdc_busy+0xc/0x14 [omap3_isp])
[ 3468.233795] [<bf20e9dc>] (omap3isp_ccdc_busy [omap3_isp]) from
[<bf205a9c>] (isp_pipeline_wait_ccdc+0x54/0x60 [omap3_isp])
[ 3468.233917] [<bf205a9c>] (isp_pipeline_wait_ccdc [omap3_isp]) from
[<bf205058>] (isp_pipeline_wait+0x28/0x4c [omap3_isp])
[ 3468.234039] [<bf205058>] (isp_pipeline_wait [omap3_isp]) from
[<bf2056d8>] (isp_pipeline_disable+0x154/0x224 [omap3_isp])
[ 3468.234191] [<bf2056d8>] (isp_pipeline_disable [omap3_isp]) from
[<bf2065a0>] (omap3isp_pipeline_set_stream+0x14/0x2c [omap3_isp])
[ 3468.234313] [<bf2065a0>] (omap3isp_pipeline_set_stream [omap3_isp])
from [<bf2092d0>] (isp_video_streamoff+0xcc/0x124 [omap3_isp])
[ 3468.234436] [<bf2092d0>] (isp_video_streamoff [omap3_isp]) from
[<bf209354>] (isp_video_release+0x2c/0x78 [omap3_isp])
[ 3468.234619] [<bf209354>] (isp_video_release [omap3_isp]) from
[<bf00d300>] (v4l2_release+0x30/0x6c [videodev])
[ 3468.234771] [<bf00d300>] (v4l2_release [videodev]) from
[<c01dd35c>] (__fput+0xd4/0x1f0)
[ 3468.234771] [<c01dd35c>] (__fput) from [<c013e030>]
(task_work_run+0x74/0x88)
[ 3468.234802] [<c013e030>] (task_work_run) from [<c01283c8>]
(do_exit+0x3e8/0x920)
[ 3468.234802] [<c01283c8>] (do_exit) from [<c0128a50>]
(do_group_exit+0xb8/0xfc)
[ 3468.234832] [<c0128a50>] (do_group_exit) from [<c013274c>]
(get_signal+0x604/0x680)
[ 3468.234832] [<c013274c>] (get_signal) from [<c010de54>]
(do_signal+0x84/0x5d0)
[ 3468.234863] [<c010de54>] (do_signal) from [<c0109958>]
(do_work_pending+0x48/0xb8)
[ 3468.234893] [<c0109958>] (do_work_pending) from [<c01071b4>]
(slow_work_pending+0xc/0x20)
[ 3468.234893] ---[ end trace 70aa4a6442fc691d ]---
[ 3468.238891] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP CCDC
[ 3468.238922] Address Hole seen by CAM  at address b00
[ 3468.238922] ------------[ cut here ]------------
[ 3468.238952] WARNING: CPU: 0 PID: 4973 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xdc/0x124
[ 3468.239044] Modules linked in: smiapp smiapp_pll ipv6
isp1704_charger omap3_isp videobuf2_v4l2 videobuf2_dma_contig
videobuf2_memops videobuf2_core et8ek8 smiaregs lis3lv02d_i2c
lis3lv02d input_polldev ti_soc_thermal arc4 wl1251_spi wl1251 crc7
mac80211 cfg80211 omap_ssi hsi bq2415x_charger si4713
bq27xxx_battery_i2c bq27xxx_battery leds_lp5523 leds_lp55xx_common
adp1653 v4l2_common tsl2563 smc91x mii rtc_twl twl4030_vibra
ff_memless twl4030_wdt tsc2005 tsc200x_core omap_sham omap_wdt
gpio_keys rx51_battery video_bus_switch videodev media
[ 3468.239044] CPU: 0 PID: 4973 Comm: yavta Tainted: G        W
4.6.0-rc4+ #1
[ 3468.239044] Hardware name: Nokia RX-51 board
[ 3468.239074] [<c010bc18>] (unwind_backtrace) from [<c0109f38>]
(show_stack+0x10/0x14)
[ 3468.239105] [<c0109f38>] (show_stack) from [<c01262dc>]
(__warn+0xcc/0xf8)
[ 3468.239105] [<c01262dc>] (__warn) from [<c0126324>]
(warn_slowpath_null+0x1c/0x20)
[ 3468.239135] [<c0126324>] (warn_slowpath_null) from [<c0331c6c>]
(omap3_l3_app_irq+0xdc/0x124)
[ 3468.239166] [<c0331c6c>] (omap3_l3_app_irq) from [<c01545bc>]
(handle_irq_event_percpu+0x34/0x138)
[ 3468.239166] [<c01545bc>] (handle_irq_event_percpu) from
[<c015471c>] (handle_irq_event+0x5c/0x88)
[ 3468.239196] [<c015471c>] (handle_irq_event) from [<c015768c>]
(handle_level_irq+0xcc/0x130)
[ 3468.239196] [<c015768c>] (handle_level_irq) from [<c0153f9c>]
(generic_handle_irq+0x18/0x28)
[ 3468.239227] [<c0153f9c>] (generic_handle_irq) from [<c01540bc>]
(__handle_domain_irq+0x84/0xa8)
[ 3468.239227] [<c01540bc>] (__handle_domain_irq) from [<c054da14>]
(__irq_svc+0x54/0x90)
[ 3468.239257] [<c054da14>] (__irq_svc) from [<c0372430>]
(regulator_disable+0x8/0x5c)
[ 3468.239349] [<c0372430>] (regulator_disable) from [<bf20a120>]
(ccp2_if_enable+0xf4/0xfc [omap3_isp])
[ 3468.239471] [<bf20a120>] (ccp2_if_enable [omap3_isp]) from
[<bf20a948>] (ccp2_s_stream+0x44c/0x4b0 [omap3_isp])
[ 3468.239593] [<bf20a948>] (ccp2_s_stream [omap3_isp]) from
[<bf205688>] (isp_pipeline_disable+0x104/0x224 [omap3_isp])
[ 3468.239746] [<bf205688>] (isp_pipeline_disable [omap3_isp]) from
[<bf2065a0>] (omap3isp_pipeline_set_stream+0x14/0x2c [omap3_isp])
[ 3468.239868] [<bf2065a0>] (omap3isp_pipeline_set_stream [omap3_isp])
from [<bf2092d0>] (isp_video_streamoff+0xcc/0x124 [omap3_isp])
[ 3468.239990] [<bf2092d0>] (isp_video_streamoff [omap3_isp]) from
[<bf209354>] (isp_video_release+0x2c/0x78 [omap3_isp])
[ 3468.240173] [<bf209354>] (isp_video_release [omap3_isp]) from
[<bf00d300>] (v4l2_release+0x30/0x6c [videodev])
[ 3468.240295] [<bf00d300>] (v4l2_release [videodev]) from
[<c01dd35c>] (__fput+0xd4/0x1f0)
[ 3468.240325] [<c01dd35c>] (__fput) from [<c013e030>]
(task_work_run+0x74/0x88)
[ 3468.240325] [<c013e030>] (task_work_run) from [<c01283c8>]
(do_exit+0x3e8/0x920)
[ 3468.240356] [<c01283c8>] (do_exit) from [<c0128a50>]
(do_group_exit+0xb8/0xfc)
[ 3468.240356] [<c0128a50>] (do_group_exit) from [<c013274c>]
(get_signal+0x604/0x680)
[ 3468.240386] [<c013274c>] (get_signal) from [<c010de54>]
(do_signal+0x84/0x5d0)
[ 3468.240417] [<c010de54>] (do_signal) from [<c0109958>]
(do_work_pending+0x48/0xb8)
[ 3468.240417] [<c0109958>] (do_work_pending) from [<c01071b4>]
(slow_work_pending+0xc/0x20)
[ 3468.240417] ---[ end trace 70aa4a6442fc691e ]---

But it seems to produce _something_ so I guess its a improvement :-).

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 16:42         ` Sebastian Reichel
  2016-04-27 16:45           ` Pavel Machek
  2016-04-27 17:12           ` Ивайло Димитров
@ 2016-04-27 20:30           ` Pavel Machek
  2 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-27 20:30 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

Hi!

> On Wed, Apr 27, 2016 at 09:57:51AM +0300, Ivaylo Dimitrov wrote:
> > >>https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n900-camera-ivo
> > >
> > >Ok, going to diff with my tree to see what I have missed to send in the
> > >patchset
> > 
> > Now, that's getting weird.
> 
> [...]
> 
> > I you want to try it, zImage and initrd are on
> > http://46.249.74.23/linux/camera-n900/
> 
> The zImage + initrd works with the steps you described below. I
> received a completly black image, but at least there are interrupts
> and yavta is happy (=> it does not hang).

Ok, thanks for all the help. I switched from =y to =m, and it started
to work.

sudo insmod videobuf2-core.ko
sudo insmod videobuf2-v4l2.ko
sudo insmod videobuf2-memops.ko
sudo insmod video-bus-switch.ko
sudo insmod smiaregs.ko
sudo insmod smiapp-pll.ko
sudo insmod smiapp.ko
sudo insmod /my/modules/videobuf2-dma-contig.ko
sudo insmod /my/modules/omap3-isp.ko

So far I tested front camera only, and used a rather bright light to
get something... but that's a start :-).

Ok, and these seem to get some image that is dark, but not completely
dark:

YA=/my/tui/yavta/yavta
sudo $YA --set-control '0x009e0903 240'  /dev/v4l-subdev8
sudo $YA --set-control '0x00980911 485'  /dev/v4l-subdev8

Thanks!
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 17:21         ` Ivaylo Dimitrov
@ 2016-04-27 21:07           ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-27 21:07 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> >>Try with:
> >>
> >>media-ctl -r
> >>media-ctl -l '"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]'
> >>media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> >>media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> >>media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> >>media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> >>media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> >>
> >>media-ctl -V '"et8ek8 3-003e":0 [SGRBG10 864x656]'
> >>media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 864x656]'
> >>media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 864x656]'
> >>media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 864x656]'
> >>media-ctl -V '"OMAP3 ISP preview":1 [UYVY 864x656]'
> >>media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 800x600]'
> >>
> >>
> >>mplayer -tv driver=v4l2:width=800:height=600:outfmt=uyvy:device=/dev/video6
> >>-vo xv -vf screenshot tv://
> >
> >It fails with:
> >
> >pavel@n900:/my/tui/ofone/camera$ sudo ./back.sh
> >Unable to parse link: Device or resource busy (16)
> 
> That shouldn't happen, there is something else wrong.
> 
> >MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer
> >Team
> >
> >...but as I'm using the original dts, it is expected...?
> >
> >Would you have dts suitable for the 5MPx camera?
> 
> Just change from strobe = <0>; to strobe = <1>; in isp node.

Thanks, that got the trick. (With  drivers being compiled as modules).

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-25 14:09       ` Hans Verkuil
@ 2016-04-27 21:09         ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-27 21:09 UTC (permalink / raw)
  To: Hans Verkuil, Pali Rohár
  Cc: linux-media, Ivaylo Dimitrov, sakari.ailus, sre

Hi!

> It's part of the v4l-utils git repo:
> 
> https://git.linuxtv.org/v4l-utils.git/
...

> > > Anyway, does anyone know where to get the media-ctl tool?
> > 
> > Looks like it is part of v4l-utils package. At least in git:
> > https://git.linuxtv.org/v4l-utils.git/tree/utils/media-ctl
> > 
> > > It does not seem to be in debian 7 or debian 8...
> > 
> > I do not see it in debian too, but there is some version in ubuntu:
> > http://packages.ubuntu.com/trusty/media-ctl
> > 
> > So you can compile ubuntu dsc package, should work on debian.
> 
> Finally, it is also in debian, see:
> 
> https://packages.debian.org/search?suite=sid&arch=any&mode=path&searchon=contents&keywords=media-ctl
> https://packages.debian.org/sid/amd64/v4l-utils/filelist

Thanks for the pointers. It seems that new debian contains media-ctl,
but I'm using older one, so I compiled it from source. Could not find
yavta, either, but that was very easy to pull from git and compile.

Best regards,
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 17:12           ` Ивайло Димитров
  2016-04-27 19:05             ` Pavel Machek
@ 2016-04-29  0:05             ` Sebastian Reichel
  2016-04-29 17:45               ` Sebastian Reichel
  1 sibling, 1 reply; 102+ messages in thread
From: Sebastian Reichel @ 2016-04-29  0:05 UTC (permalink / raw)
  To: Ивайло
	Димитров
  Cc: pavel, sakari.ailus, pali.rohar, linux-media

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

Hi Ivo,

On Wed, Apr 27, 2016 at 08:12:50PM +0300, Ивайло Димитров wrote:
> > The zImage + initrd works with the steps you described below.
> 
> Great!

I also got it working with the previously referenced branch with the
following built as modules:

CONFIG_VIDEOBUF2_CORE=m
CONFIG_VIDEOBUF2_MEMOPS=m
CONFIG_VIDEOBUF2_DMA_CONTIG=m
CONFIG_VIDEO_OMAP3=m
CONFIG_VIDEO_BUS_SWITCH=m
CONFIG_VIDEO_SMIAPP_PLL=m
CONFIG_VIDEO_SMIAPP=m
CONFIG_VIDEO_SMIAREGS=m
CONFIG_VIDEO_ET8EK8=m

> > I received a completly black image, but at least there are interrupts
> > and yavta is happy (=> it does not hang).
> 
> The black image is because by default exposure and gain are set to 0 :). 
> Use yavta to set the appropriate controls. You can
> also enable test patterns from there.

The test patterns look ok for me, so it's definitively a sensor
configuration problem :)

> > Can you try if your config still works if you configure
> > CONFIG_VIDEO_OMAP3=y, but leaving the sensors configured
> > as modules? I will try the reverse process (using my config
> > and moving config options to =m).
> >
> 
> Will try to find time later today.
>  
> > > ~$ modprobe smiapp
> > 
> > modprobing smiapp resulted in a kernel message about a missing
> > symbol btw. I currently don't remember which one and it's no
> > longer in dmesg due to ISP debug messages.
> 
> Never seen such missing symbols.

It did not appear with my kernel built either.

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [RFC PATCH 01/24] V4L fixes
  2016-04-25 16:32       ` Ivaylo Dimitrov
@ 2016-04-29  7:41         ` Sakari Ailus
  0 siblings, 0 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-04-29  7:41 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

On Mon, Apr 25, 2016 at 07:32:50PM +0300, Ivaylo Dimitrov wrote:
> Hi,
> 
> On 25.04.2016 16:25, Sakari Ailus wrote:
> >Hi Ivaylo,
> >
> >Thanks for the set!
> >
> >On Mon, Apr 25, 2016 at 12:08:01AM +0300, Ivaylo Dimitrov wrote:
> >>From: "Tuukka.O Toivonen" <tuukka.o.toivonen@nokia.com>
> >>
> >>Squashed from the following upstream commits:
> >>
> >>V4L: Create control class for sensor mode
> >>V4L: add ad5820 focus specific custom controls
> >>V4L: add V4L2_CID_TEST_PATTERN
> >>V4L: Add V4L2_CID_MODE_OPSYSCLOCK for reading output system clock
> >>
> >>Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> >>Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> >>---
> >>  include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++
> >>  1 file changed, 17 insertions(+)
> >>
> >>diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> >>index b6a357a..23011cc 100644
> >>--- a/include/uapi/linux/v4l2-controls.h
> >>+++ b/include/uapi/linux/v4l2-controls.h
> >>@@ -62,6 +62,7 @@
> >>  #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
> >>  #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
> >>  #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
> >>+#define V4L2_CTRL_CLASS_MODE		0x00a40000	/* Sensor mode information */
> >>
> >>  /* User-class control IDs */
> >>
> >>@@ -974,4 +975,20 @@ enum v4l2_detect_md_mode {
> >>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
> >>  #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
> >>
> >>+/* SMIA-type sensor information */
> >>+#define V4L2_CID_MODE_CLASS_BASE		(V4L2_CTRL_CLASS_MODE | 0x900)
> >>+#define V4L2_CID_MODE_CLASS			(V4L2_CTRL_CLASS_MODE | 1)
> >>+#define V4L2_CID_MODE_FRAME_WIDTH		(V4L2_CID_MODE_CLASS_BASE+1)
> >>+#define V4L2_CID_MODE_FRAME_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+2)
> >>+#define V4L2_CID_MODE_VISIBLE_WIDTH		(V4L2_CID_MODE_CLASS_BASE+3)
> >>+#define V4L2_CID_MODE_VISIBLE_HEIGHT		(V4L2_CID_MODE_CLASS_BASE+4)
> >
> >The interface here pre-dates the selection API. The frame width and height
> >is today conveyed to the bridge driver by the smiapp driver in the scaler
> >(or binner in case of the lack of the scaler) sub-device's source pad
> >format.
> >
> >(While that's the current interface, I'm not entirely happy with it; it
> >requires more sub-devices to be created for the same I2C device). I think in
> >this case you'd need one more to properly control the sensor.
> >
> 
> By looking at the code, it seems those are read-only, so I don't think it
> make sense to add a binner sub-device with read-only controls.

Well, I can't disagree with that. However, I think a number of other drivers
would benefit from doing this properly --- the V4L2 selection API which
defines a strict order for the processing steps does not suit to this use
case very well. You could use the crop selection rectangle for now, albeit
that doesn't fully express what the sensor does, or use private controls. We
could then later replace this with something better.

> 
> >>+#define V4L2_CID_MODE_PIXELCLOCK		(V4L2_CID_MODE_CLASS_BASE+5)
> >>+#define V4L2_CID_MODE_SENSITIVITY		(V4L2_CID_MODE_CLASS_BASE+6)
> >>+#define V4L2_CID_MODE_OPSYSCLOCK		(V4L2_CID_MODE_CLASS_BASE+7)
> >
> >While I don't remember quite what the sensitivity value was about (it could
> >be e.g. binning summing / averaging), the other two values are passed to
> >(and also controlled by) the user space using controls. There are
> >V4L2_CID_PIXEL_RATE and V4L2_CID_LINK_FREQ or such.
> >
> 
> I've already made a change so V4L2_CID_PIXEL_RATE is used in et8ek8 driver (https://github.com/freemangordon/linux-n900/commit/54433e50451b4ed6cc6e3b25d149c5cacd97e438),
> but V4L2_CID_MODE_PIXELCLOCK is used in smiapp driver, which seems to expose
> its own controls. Not sure those are needed though. And if, what for. I hope
> you know better than me.

It's needed to tell the sensor timings to the user space. The camera control
algorithms need that information.

> 
> I guess V4L2_CID_MODE_OPSYSCLOCK can be easily transformed to
> V4L2_CID_LINK_FREQ in the same way as V4L2_CID_MODE_PIXELCLOCK.

Yes.

> 
> >>+
> >>+/* Control IDs specific to the AD5820 driver as defined by V4L2 */
> >>+#define V4L2_CID_FOCUS_AD5820_BASE 		(V4L2_CTRL_CLASS_CAMERA | 0x10af)
> >>+#define V4L2_CID_FOCUS_AD5820_RAMP_TIME		(V4L2_CID_FOCUS_AD5820_BASE+0)
> >>+#define V4L2_CID_FOCUS_AD5820_RAMP_MODE		(V4L2_CID_FOCUS_AD5820_BASE+1)
> >
> >The ad5820 driver isn't in upstream at the moment. It should be investigated
> >whether there is a possibility to have standard V4L2 controls for lens
> >devices, or whether device specific controls should be implemented instead.
> >Device specific controls are a safe choice in this case, but they should be
> >in a separate patch, possibly one that would also include the lens driver
> >itself.
> >
> 
> Yeah, I sent the whole patch for the sake of not losing the history too
> much.

There's still work to be done but I'm very happy to see that you and a few
others are contributing. :-)

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi
  2016-04-24 21:08   ` [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi Ivaylo Dimitrov
@ 2016-04-29 13:22     ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-29 13:22 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On Mon 2016-04-25 00:08:07, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> The function to parse CSI2 bus parameters was called
> v4l2_of_parse_csi_bus(), rename it as v4l2_of_parse_csi2_bus() in
> anticipation of CSI1/CCP2 support.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>

Acked-by: Pavel Machek <pavel@ucw.cz>

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type
  2016-04-24 21:08   ` [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type Ivaylo Dimitrov
@ 2016-04-29 13:27     ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-29 13:27 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On Mon 2016-04-25 00:08:09, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> CCP2, or CSI-1, is an older single data lane serial bus.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>

Acked-by: Pavel Machek <pavel@ucw.cz>

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property
  2016-04-24 21:08   ` [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property Ivaylo Dimitrov
@ 2016-04-29 13:28     ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-29 13:28 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On Mon 2016-04-25 00:08:11, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> In the vast majority of cases the bus type is known to the driver(s) since
> a receiver or transmitter can only support a single one. There are cases
> however where different options are possible.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>

Acked-by: Pavel	 Machek <pavel@ucw.cz>

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt
  2016-04-24 21:08   ` [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt Ivaylo Dimitrov
@ 2016-04-29 13:39     ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-04-29 13:39 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On Mon 2016-04-25 00:08:12, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> Document the CSI1/CCP2 properties strobe_clk_inv and strobe_clock
> properties. The former tells whether the strobe/clock signal is inverted,
> while the latter signifies the clock or strobe mode.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
> ---
>  Documentation/devicetree/bindings/media/video-interfaces.txt | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
> index f5b61bd..f0523f7 100644
> --- a/Documentation/devicetree/bindings/media/video-interfaces.txt
> +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
> @@ -114,9 +114,10 @@ Optional endpoint properties
>    lane and followed by the data lanes in the same order as in data-lanes.
>    Valid values are 0 (normal) and 1 (inverted). The length of the array
>    should be the combined length of data-lanes and clock-lanes properties.
> -  If the lane-polarities property is omitted, the value must be interpreted
> -  as 0 (normal). This property is valid for serial busses only.
> -
> +- clock-inv: Clock or strobe signal inversion.
> +  Possible values: 0 -- not inverted; 1 -- inverted

I'd do "clock-inverted". And probably dt people need to be cc-ed on
this one.

> +- strobe: Whether the clock signal is used as clock or strobe. Used
> +  with CCP2, for instance.

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-29  0:05             ` Sebastian Reichel
@ 2016-04-29 17:45               ` Sebastian Reichel
  2016-04-29 18:44                 ` Ivaylo Dimitrov
  2016-05-01  9:03                 ` Pavel Machek
  0 siblings, 2 replies; 102+ messages in thread
From: Sebastian Reichel @ 2016-04-29 17:45 UTC (permalink / raw)
  To: Ивайло
	Димитров
  Cc: pavel, sakari.ailus, pali.rohar, linux-media

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

Hi,

On Fri, Apr 29, 2016 at 02:05:52AM +0200, Sebastian Reichel wrote:
> On Wed, Apr 27, 2016 at 08:12:50PM +0300, Ивайло Димитров wrote:
> > > The zImage + initrd works with the steps you described below.
> > 
> > Great!
> 
> I also got it working with the previously referenced branch with the
> following built as modules:
> 
> CONFIG_VIDEOBUF2_CORE=m
> CONFIG_VIDEOBUF2_MEMOPS=m
> CONFIG_VIDEOBUF2_DMA_CONTIG=m
> CONFIG_VIDEO_OMAP3=m
> CONFIG_VIDEO_BUS_SWITCH=m
> CONFIG_VIDEO_SMIAPP_PLL=m
> CONFIG_VIDEO_SMIAPP=m
> CONFIG_VIDEO_SMIAREGS=m
> CONFIG_VIDEO_ET8EK8=m

Ok, I found the problem. CONFIG_VIDEO_OMAP3=y does not work,
due to missing -EPROBE_DEFER handling for vdds_csib. I added
it and just got a test image with builtin CONFIG_VIDEO_OMAP3.
The below patch fixes the problem.

commit 9d8333b29207de3a9b6ac99db2dfd91e2f8c0216
Author: Sebastian Reichel <sre@kernel.org>
Date:   Fri Apr 29 19:23:02 2016 +0200

    omap3isp: handle -EPROBE_DEFER for vdds_csib
    
    omap3isp may be initialized before the regulator's driver has been
    loaded resulting in vdds_csib=NULL. Fix this by handling -EPROBE_DEFER
    for vdds_csib.
    
    Signed-Off-By: Sebastian Reichel <sre@kernel.org>

diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 833eed411886..2d1463a72d6a 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -1167,6 +1167,8 @@ int omap3isp_ccp2_init(struct isp_device *isp)
 	if (isp->revision == ISP_REVISION_2_0) {
 		ccp2->vdds_csib = devm_regulator_get(isp->dev, "vdds_csib");
 		if (IS_ERR(ccp2->vdds_csib)) {
+			if (PTR_ERR(ccp2->vdds_csib) == -EPROBE_DEFER)
+				return -EPROBE_DEFER;
 			dev_dbg(isp->dev,
 				"Could not get regulator vdds_csib\n");
 			ccp2->vdds_csib = NULL;

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-29 17:45               ` Sebastian Reichel
@ 2016-04-29 18:44                 ` Ivaylo Dimitrov
  2016-05-01 10:37                   ` Sakari Ailus
  2016-05-01  9:03                 ` Pavel Machek
  1 sibling, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-04-29 18:44 UTC (permalink / raw)
  To: Sebastian Reichel, sakari.ailus; +Cc: pavel, pali.rohar, linux-media

Hi,

On 29.04.2016 20:45, Sebastian Reichel wrote:
> Hi,
>
> On Fri, Apr 29, 2016 at 02:05:52AM +0200, Sebastian Reichel wrote:
>> On Wed, Apr 27, 2016 at 08:12:50PM +0300, Ивайло Димитров wrote:
>>>> The zImage + initrd works with the steps you described below.
>>>
>>> Great!
>>
>> I also got it working with the previously referenced branch with the
>> following built as modules:
>>
>> CONFIG_VIDEOBUF2_CORE=m
>> CONFIG_VIDEOBUF2_MEMOPS=m
>> CONFIG_VIDEOBUF2_DMA_CONTIG=m
>> CONFIG_VIDEO_OMAP3=m
>> CONFIG_VIDEO_BUS_SWITCH=m
>> CONFIG_VIDEO_SMIAPP_PLL=m
>> CONFIG_VIDEO_SMIAPP=m
>> CONFIG_VIDEO_SMIAREGS=m
>> CONFIG_VIDEO_ET8EK8=m
>
> Ok, I found the problem. CONFIG_VIDEO_OMAP3=y does not work,
> due to missing -EPROBE_DEFER handling for vdds_csib. I added
> it and just got a test image with builtin CONFIG_VIDEO_OMAP3.
> The below patch fixes the problem.
>

Cool :)

vdd-csiphy1/2 will need the same handling, but lets have what is done so 
far rolling, those can be fixed later on.

> commit 9d8333b29207de3a9b6ac99db2dfd91e2f8c0216
> Author: Sebastian Reichel <sre@kernel.org>
> Date:   Fri Apr 29 19:23:02 2016 +0200
>
>      omap3isp: handle -EPROBE_DEFER for vdds_csib
>
>      omap3isp may be initialized before the regulator's driver has been
>      loaded resulting in vdds_csib=NULL. Fix this by handling -EPROBE_DEFER
>      for vdds_csib.
>
>      Signed-Off-By: Sebastian Reichel <sre@kernel.org>
>
> diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
> index 833eed411886..2d1463a72d6a 100644
> --- a/drivers/media/platform/omap3isp/ispccp2.c
> +++ b/drivers/media/platform/omap3isp/ispccp2.c
> @@ -1167,6 +1167,8 @@ int omap3isp_ccp2_init(struct isp_device *isp)
>   	if (isp->revision == ISP_REVISION_2_0) {
>   		ccp2->vdds_csib = devm_regulator_get(isp->dev, "vdds_csib");
>   		if (IS_ERR(ccp2->vdds_csib)) {
> +			if (PTR_ERR(ccp2->vdds_csib) == -EPROBE_DEFER)
> +				return -EPROBE_DEFER;
>   			dev_dbg(isp->dev,
>   				"Could not get regulator vdds_csib\n");
>   			ccp2->vdds_csib = NULL;
>

Sakari, how we're going to proceed, it seems there are a couple of 
patches in the series which can be directly upstreamed, how's that gonna 
happen? IOW - I don't know how this RFC stuff works, are there any docs 
I can use to educate myself?

Thanks,
Ivo

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-29 17:45               ` Sebastian Reichel
  2016-04-29 18:44                 ` Ivaylo Dimitrov
@ 2016-05-01  9:03                 ` Pavel Machek
  1 sibling, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-05-01  9:03 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Ивайло
	Димитров,
	sakari.ailus, pali.rohar, linux-media

On Fri 2016-04-29 19:45:59, Sebastian Reichel wrote:

> Ok, I found the problem. CONFIG_VIDEO_OMAP3=y does not work,
> due to missing -EPROBE_DEFER handling for vdds_csib. I added
> it and just got a test image with builtin CONFIG_VIDEO_OMAP3.
> The below patch fixes the problem.
> 
> commit 9d8333b29207de3a9b6ac99db2dfd91e2f8c0216
> Author: Sebastian Reichel <sre@kernel.org>
> Date:   Fri Apr 29 19:23:02 2016 +0200
> 
>     omap3isp: handle -EPROBE_DEFER for vdds_csib
>     
>     omap3isp may be initialized before the regulator's driver has been
>     loaded resulting in vdds_csib=NULL. Fix this by handling -EPROBE_DEFER
>     for vdds_csib.
>     
>     Signed-Off-By: Sebastian Reichel <sre@kernel.org>

Tested-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Pavel Machek <pavel@ucw.cz>

...and... thanks :-).
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-29 18:44                 ` Ivaylo Dimitrov
@ 2016-05-01 10:37                   ` Sakari Ailus
  0 siblings, 0 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 10:37 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: Sebastian Reichel, pavel, pali.rohar, linux-media

Hi Ivaylo,

On Fri, Apr 29, 2016 at 09:44:25PM +0300, Ivaylo Dimitrov wrote:
> Hi,
> 
> On 29.04.2016 20:45, Sebastian Reichel wrote:
> >Hi,
> >
> >On Fri, Apr 29, 2016 at 02:05:52AM +0200, Sebastian Reichel wrote:
> >>On Wed, Apr 27, 2016 at 08:12:50PM +0300, Ивайло Димитров wrote:
> >>>>The zImage + initrd works with the steps you described below.
> >>>
> >>>Great!
> >>
> >>I also got it working with the previously referenced branch with the
> >>following built as modules:
> >>
> >>CONFIG_VIDEOBUF2_CORE=m
> >>CONFIG_VIDEOBUF2_MEMOPS=m
> >>CONFIG_VIDEOBUF2_DMA_CONTIG=m
> >>CONFIG_VIDEO_OMAP3=m
> >>CONFIG_VIDEO_BUS_SWITCH=m
> >>CONFIG_VIDEO_SMIAPP_PLL=m
> >>CONFIG_VIDEO_SMIAPP=m
> >>CONFIG_VIDEO_SMIAREGS=m
> >>CONFIG_VIDEO_ET8EK8=m
> >
> >Ok, I found the problem. CONFIG_VIDEO_OMAP3=y does not work,
> >due to missing -EPROBE_DEFER handling for vdds_csib. I added
> >it and just got a test image with builtin CONFIG_VIDEO_OMAP3.
> >The below patch fixes the problem.
> >
> 
> Cool :)
> 
> vdd-csiphy1/2 will need the same handling, but lets have what is done so far
> rolling, those can be fixed later on.
> 
> >commit 9d8333b29207de3a9b6ac99db2dfd91e2f8c0216
> >Author: Sebastian Reichel <sre@kernel.org>
> >Date:   Fri Apr 29 19:23:02 2016 +0200
> >
> >     omap3isp: handle -EPROBE_DEFER for vdds_csib
> >
> >     omap3isp may be initialized before the regulator's driver has been
> >     loaded resulting in vdds_csib=NULL. Fix this by handling -EPROBE_DEFER
> >     for vdds_csib.
> >
> >     Signed-Off-By: Sebastian Reichel <sre@kernel.org>
> >
> >diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
> >index 833eed411886..2d1463a72d6a 100644
> >--- a/drivers/media/platform/omap3isp/ispccp2.c
> >+++ b/drivers/media/platform/omap3isp/ispccp2.c
> >@@ -1167,6 +1167,8 @@ int omap3isp_ccp2_init(struct isp_device *isp)
> >  	if (isp->revision == ISP_REVISION_2_0) {
> >  		ccp2->vdds_csib = devm_regulator_get(isp->dev, "vdds_csib");
> >  		if (IS_ERR(ccp2->vdds_csib)) {
> >+			if (PTR_ERR(ccp2->vdds_csib) == -EPROBE_DEFER)
> >+				return -EPROBE_DEFER;
> >  			dev_dbg(isp->dev,
> >  				"Could not get regulator vdds_csib\n");
> >  			ccp2->vdds_csib = NULL;
> >
> 
> Sakari, how we're going to proceed, it seems there are a couple of patches
> in the series which can be directly upstreamed, how's that gonna happen? IOW
> - I don't know how this RFC stuff works, are there any docs I can use to
> educate myself?

If there are just a few, then they could be picked up individually, but
otherwise I'd propose to split up the set into two, those that can go to
upstream now and those that are still understood to require some work.

I'll try to spend some time in the near future for reviewing the entire set.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-04-24 21:08   ` [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver Ivaylo Dimitrov
@ 2016-05-01 10:44     ` Sakari Ailus
  2016-05-01 12:31       ` Ivaylo Dimitrov
  2016-05-01 12:50       ` Ivaylo Dimitrov
  0 siblings, 2 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 10:44 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

On Mon, Apr 25, 2016 at 12:08:03AM +0300, Ivaylo Dimitrov wrote:
> add driver
> 
> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
> ---
>  drivers/media/i2c/smia/Kconfig  |    8 +
>  drivers/media/i2c/smia/Makefile |    1 +
>  drivers/media/i2c/smia/et8ek8.c | 1788 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1797 insertions(+)
>  create mode 100644 drivers/media/i2c/smia/et8ek8.c
> 
> diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
> index d9be497..13ca043 100644
> --- a/drivers/media/i2c/smia/Kconfig
> +++ b/drivers/media/i2c/smia/Kconfig
> @@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
>  
>  	  Also a few helper functions are provided to work with binary
>  	  register lists.
> +
> +config VIDEO_ET8EK8
> +	tristate "ET8EK8 camera sensor support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select VIDEO_SMIAREGS
> +	---help---
> +	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
> +	  It is used for example in Nokia N900 (RX-51).
> diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
> index cff67bc..56cf15e 100644
> --- a/drivers/media/i2c/smia/Makefile
> +++ b/drivers/media/i2c/smia/Makefile
> @@ -1 +1,2 @@
>  obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
> +obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
> diff --git a/drivers/media/i2c/smia/et8ek8.c b/drivers/media/i2c/smia/et8ek8.c
> new file mode 100644
> index 0000000..46c112d
> --- /dev/null
> +++ b/drivers/media/i2c/smia/et8ek8.c
> @@ -0,0 +1,1788 @@
> +/*
> + * drivers/media/video/et8ek8.c
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@nokia.com>
> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> + *
> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
> + *
> + * This driver is based on the Micron MT9T012 camera imager driver
> + * (C) Texas Instruments.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#define DEBUG
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +#include <linux/v4l2-mediabus.h>
> +
> +#include <media/media-entity.h>
> +#include <media/smiaregs.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define ET8EK8_NAME		"et8ek8"
> +#define ET8EK8_XCLK_HZ		9600000
> +#define ET8EK8_PRIV_MEM_SIZE	128
> +
> +#define CTRL_GAIN		0
> +#define CTRL_EXPOSURE		1
> +#define CTRL_TEST_PATTERN	2
> +
> +#define CID_TO_CTRL(id)		((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
> +				 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
> +				 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
> +				 -EINVAL)
> +
> +struct et8ek8_sensor {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pad;
> +	struct v4l2_mbus_framefmt format;
> +	struct gpio_desc *reset;
> +	struct regulator *vana;
> +	struct clk *ext_clk;
> +
> +	u16 version;
> +
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *pixel_rate;
> +	struct smia_reglist *current_reglist;
> +
> +	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
> +
> +	struct mutex power_lock;
> +	int power_count;
> +};
> +
> +#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
> +
> +enum et8ek8_versions {
> +	ET8EK8_REV_1 = 0x0001,
> +	ET8EK8_REV_2,
> +};
> +
> +/*
> + * This table describes what should be written to the sensor register
> + * for each gain value. The gain(index in the table) is in terms of
> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
> + * the *analog gain, [1] in the digital gain
> + *
> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
> + */
> +static struct et8ek8_gain {
> +	u16 analog;
> +	u16 digital;
> +} const et8ek8_gain_table[] = {
> +	{ 32,    0},  /* x1 */
> +	{ 34,    0},
> +	{ 37,    0},
> +	{ 39,    0},
> +	{ 42,    0},
> +	{ 45,    0},
> +	{ 49,    0},
> +	{ 52,    0},
> +	{ 56,    0},
> +	{ 60,    0},
> +	{ 64,    0},  /* x2 */
> +	{ 69,    0},
> +	{ 74,    0},
> +	{ 79,    0},
> +	{ 84,    0},
> +	{ 91,    0},
> +	{ 97,    0},
> +	{104,    0},
> +	{111,    0},
> +	{119,    0},
> +	{128,    0},  /* x4 */
> +	{137,    0},
> +	{147,    0},
> +	{158,    0},
> +	{169,    0},
> +	{181,    0},
> +	{194,    0},
> +	{208,    0},
> +	{223,    0},
> +	{239,    0},
> +	{256,    0},  /* x8 */
> +	{256,   73},
> +	{256,  152},
> +	{256,  236},
> +	{256,  327},
> +	{256,  424},
> +	{256,  528},
> +	{256,  639},
> +	{256,  758},
> +	{256,  886},
> +	{256, 1023},  /* x16 */
> +};
> +
> +/* Register definitions */
> +#define REG_REVISION_NUMBER_L	0x1200
> +#define REG_REVISION_NUMBER_H	0x1201
> +
> +#define PRIV_MEM_START_REG	0x0008
> +#define PRIV_MEM_WIN_SIZE	8
> +
> +#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
> +
> +#define USE_CRC			1
> +
> +/*
> + *
> + * Stingray sensor mode settings for Scooby
> + *
> + *
> + */
> +

It'd be nice to get rid of the register lists, however considering where the
sensor is used it's unlikely going to find its way elsewhere, so the gain
might not be worth the effort.

I'd still integrate the functionality in the smia register list library to
the driver. That sort of functionality ideally should not be needed at all
but sadly, the documentation of some sensors is too bad to write proper
drivers. :-(

(SMIA is an ill-conceived name for this library btw., it's got nothing to do
with SMIA as such. I'd call it et8ek8_reglist for example. Perhaps the
library only would be a better choice.)

> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
> +static struct smia_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {	/* 1 */
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 640 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_POWERON,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 2016,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 1207
> +		},
> +		.max_exp = 2012,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x126C, 0xCC },	/* Need to set firstly */
> +		{ SMIA_REG_8BIT, 0x1269, 0x00 },	/* Strobe and Data of CCP2 delay are minimized. */
> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },	/* Refined value of Min H_COUNT  */
> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },	/* Frequency of SPCK setting (SPCK=MRCK) */
> +		{ SMIA_REG_8BIT, 0x1241, 0x94 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1242, 0x02 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x124B, 0x00 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1255, 0xFF },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1256, 0x9F },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1258, 0x00 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* From parallel out to serial out */
> +		{ SMIA_REG_8BIT, 0x125E, 0xC0 },	/* From w/ embeded data to w/o embeded data */
> +		{ SMIA_REG_8BIT, 0x1263, 0x98 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1268, 0xC6 },	/* CCP2 out is from STOP to ACTIVE */
> +		{ SMIA_REG_8BIT, 0x1434, 0x00 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1163, 0x44 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1166, 0x29 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1140, 0x02 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1011, 0x24 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1151, 0x80 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1152, 0x23 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1014, 0x05 },	/* Initial setting( for improvement2 of lower frequency noise ) */
> +		{ SMIA_REG_8BIT, 0x1033, 0x06 },
> +		{ SMIA_REG_8BIT, 0x1034, 0x79 },
> +		{ SMIA_REG_8BIT, 0x1423, 0x3F },
> +		{ SMIA_REG_8BIT, 0x1424, 0x3F },
> +		{ SMIA_REG_8BIT, 0x1426, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1439, 0x00 },	/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
> +		{ SMIA_REG_8BIT, 0x161F, 0x60 },	/* Switch of blemish correction (0d:disable / 1d:enable) */
> +		{ SMIA_REG_8BIT, 0x1634, 0x00 },	/* Switch of auto noise correction (0d:disable / 1d:enable) */
> +		{ SMIA_REG_8BIT, 0x1646, 0x00 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1648, 0x00 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x113E, 0x01 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x113F, 0x22 },	/* Initial setting */
> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
> +static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {	/* 2 */
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 560 MHz
> + * VCO        = 560 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 128 (3072)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 175
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 6
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3072,
> +		.height = 2016,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 1292
> +		},
> +		.max_exp = 2012,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x57 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x06 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0x80 },	/* <-changed to v14 7E->80 */
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
> +static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {	/* 3 */
> +/* (without the +1)
> + * SPCK       = 96.5333333333333 MHz
> + * CCP2       = 579.2 MHz
> + * VCO        = 579.2 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 133 (3192)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 181
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 5
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3192,
> +		.height = 1008,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 96533333,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 3000
> +		},
> +		.max_exp = 1004,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x5A },	/*        */
> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },	/*        */
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode4_SVGA_864x656_29.88fps */
> +static struct smia_reglist mode4_svga_864x656_29_88fps = {	/* 4 */
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 320 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 166 (3984)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3984,
> +		.height = 672,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 864,
> +		.window_height = 656,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2988
> +		},
> +		.max_exp = 668,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x62 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x62 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0xA6 },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode5_VGA_648x492_29.93fps */
> +static struct smia_reglist mode5_vga_648x492_29_93fps = {	/* 5 */
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 320 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 221 (5304)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 5304,
> +		.height = 504,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 648,
> +		.window_height = 492,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2993
> +		},
> +		.max_exp = 500,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode2_16VGA_2592x1968_3.99fps */
> +static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {	/* 6 */
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 640 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 254 (6096)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 6096,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 399
> +		},
> +		.max_exp = 6092,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0xFE },
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode_648x492_5fps */
> +static struct smia_reglist mode_648x492_5fps = {	/* 7 */
> +/* (without the +1)
> + * SPCK       = 13.3333333333333 MHz
> + * CCP2       = 53.3333333333333 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 221 (5304)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 5
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 5304,
> +		.height = 504,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 648,
> +		.window_height = 492,
> +		.pixel_clock = 13333333,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 499
> +		},
> +		.max_exp = 500,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x57 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode3_4VGA_1296x984_5fps */
> +static struct smia_reglist mode3_4vga_1296x984_5fps = {	/* 8 */
> +/* (without the +1)
> + * SPCK       = 49.4 MHz
> + * CCP2       = 395.2 MHz
> + * VCO        = 790.4 MHz
> + * VCOUNT     = 250 (6000)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 247
> + * VCO_DIV    = 1
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 3000,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 49400000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 501
> +		},
> +		.max_exp = 2996,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x7B },
> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> +		{ SMIA_REG_8BIT, 0x123A, 0x17 },
> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },
> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> +		{ SMIA_REG_8BIT, 0x1222, 0xFA },
> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
> +static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {	/* 9 */
> +/* (without the +1)
> + * SPCK       = 84.2666666666667 MHz
> + * CCP2       = 505.6 MHz
> + * VCO        = 505.6 MHz
> + * VCOUNT     = 88 (2112)
> + * HCOUNT     = 133 (3192)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 158
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 5
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = SMIA_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3192,
> +		.height = 1056,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 84266667,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2500
> +		},
> +		.max_exp = 1052,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ SMIA_REG_8BIT, 0x1239, 0x4F },	/*        */
> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },	/*        */
> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1222, 0x58 },	/*        */
> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },	/*        */
> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/*        */
> +		{ SMIA_REG_TERM, 0, 0}
> +	}
> +};
> +
> +static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
> +	.magic   = SMIA_MAGIC,
> +	.version = "V14 03-June-2008",
> +	.reglist = {
> +		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
> +		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
> +		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
> +		{ .ptr = &mode4_svga_864x656_29_88fps },
> +		{ .ptr = &mode5_vga_648x492_29_93fps },
> +		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
> +		{ .ptr = &mode_648x492_5fps },
> +		{ .ptr = &mode3_4vga_1296x984_5fps },
> +		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
> +		{ .ptr = 0 }
> +	}
> +};
> +
> +/*
> + * Return time of one row in microseconds, .8 fixed point format.
> + * If the sensor is not set to any mode, return zero.
> + */
> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
> +{
> +	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
> +	unsigned int rt;	/* Row time in .8 fixed point */
> +
> +	if (!sensor->current_reglist)
> +		return 0;
> +
> +	clock = sensor->current_reglist->mode.pixel_clock;
> +	clock = (clock + (1 << 9)) >> 10;
> +	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
> +	rt = (rt + (clock >> 1)) / clock;
> +
> +	return rt;
> +}
> +
> +/*
> + * Convert exposure time `us' to rows. Modify `us' to make it to
> + * correspond to the actual exposure time.
> + */
> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
> +{
> +	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
> +	unsigned int rt;	/* Row time in .8 fixed point */
> +
> +	/* Assume that the maximum exposure time is at most ~8 s,
> +	 * and the maximum width (with blanking) ~8000 pixels.
> +	 * The formula here is in principle as simple as
> +	 *    rows = exptime / 1e6 / width * pixel_clock
> +	 * but to get accurate results while coping with value ranges,
> +	 * have to do some fixed point math.
> +	 */
> +
> +	rt = et8ek8_get_row_time(sensor);
> +	rows = ((*us << 8) + (rt >> 1)) / rt;
> +
> +	if (rows > sensor->current_reglist->mode.max_exp)
> +		rows = sensor->current_reglist->mode.max_exp;
> +
> +	/* Set the exposure time to the rounded value */
> +	*us = (rt * rows + (1 << 7)) >> 8;
> +
> +	return rows;
> +}
> +
> +/*
> + * Convert exposure time in rows to microseconds
> + */
> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
> +{
> +	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
> +}
> +
> +/* Called to change the V4L2 gain control value. This function
> + * rounds and clamps the given value and updates the V4L2 control value.
> + * If power is on, also updates the sensor analog and digital gains.
> + * gain is in 0.1 EV (exposure value) units.
> + */
> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	struct et8ek8_gain new;
> +	int r;
> +
> +	new = et8ek8_gain_table[gain];
> +
> +	/* FIXME: optimise I2C writes! */
> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> +				0x124a, new.analog >> 8);
> +	if (r)
> +		return r;
> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> +				0x1249, new.analog & 0xff);
> +	if (r)
> +		return r;
> +
> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> +				0x124d, new.digital >> 8);
> +	if (r)
> +		return r;
> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> +				0x124c, new.digital & 0xff);
> +
> +	return r;
> +}
> +
> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
> +
> +	/* Values for normal mode */
> +	cbh_mode = 0;
> +	cbv_mode = 0;
> +	tp_mode  = 0;
> +	din_sw   = 0x00;
> +	r1420    = 0xF0;
> +
> +	if (mode != 0) {
> +		/* Test pattern mode */
> +		if (mode < 5) {
> +			cbh_mode = 1;
> +			cbv_mode = 1;
> +			tp_mode  = mode + 3;
> +		} else {
> +			cbh_mode = 0;
> +			cbv_mode = 0;
> +			tp_mode  = mode - 4 + 3;
> +		}
> +		din_sw   = 0x01;
> +		r1420    = 0xE0;
> +	}
> +
> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode << 4);
> +	if (rval)
> +		goto out;
> +
> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121, cbh_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124, cbv_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
> +	if (rval)
> +		goto out;
> +
> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
> +	if (rval)
> +		goto out;
> +
> +out:
> +	return rval;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 controls
> + */
> +
> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct et8ek8_sensor *sensor =
> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> +	const struct smia_mode *mode = &sensor->current_reglist->mode;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_MODE_FRAME_WIDTH:
> +		ctrl->cur.val = mode->width;
> +		break;
> +	case V4L2_CID_MODE_FRAME_HEIGHT:
> +		ctrl->cur.val = mode->height;
> +		break;
> +	case V4L2_CID_MODE_VISIBLE_WIDTH:
> +		ctrl->cur.val = mode->window_width;
> +		break;
> +	case V4L2_CID_MODE_VISIBLE_HEIGHT:
> +		ctrl->cur.val = mode->window_height;
> +		break;
> +	case V4L2_CID_MODE_PIXELCLOCK:
> +		ctrl->cur.val = mode->pixel_clock;

Please use V4L2_CID_PIXEL_RATE instead. It's a 64-bit control.

> +		break;
> +	case V4L2_CID_MODE_SENSITIVITY:
> +		ctrl->cur.val = mode->sensitivity;
> +		break;
> +	case V4L2_CID_MODE_OPSYSCLOCK:
> +		ctrl->cur.val = mode->opsys_clock;
> +		break;

V4L2_CID_LINK_FREQ.

> +	}
> +
> +	return 0;
> +}
> +
> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct et8ek8_sensor *sensor =
> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	int uninitialized_var(rows);
> +
> +	if (ctrl->id == V4L2_CID_EXPOSURE)
> +		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_GAIN:
> +		return et8ek8_set_gain(sensor, ctrl->val);
> +
> +	case V4L2_CID_EXPOSURE:
> +		return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
> +					  swab16(rows));
> +
> +	case V4L2_CID_TEST_PATTERN:
> +		return et8ek8_set_test_pattern(sensor, ctrl->val);
> +
> +	case V4L2_CID_PIXEL_RATE:
> +		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
> +	.g_volatile_ctrl = et8ek8_get_ctrl,
> +	.s_ctrl = et8ek8_set_ctrl,
> +};
> +
> +static const char *et8ek8_test_pattern_menu[] = {
> +	"Normal",
> +	"Vertical colorbar",
> +	"Horizontal colorbar",
> +	"Scale",
> +	"Ramp",
> +	"Small vertical colorbar",
> +	"Small horizontal colorbar",
> +	"Small scale",
> +	"Small ramp",
> +};
> +
> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_TEST_PATTERN,
> +		.type		= V4L2_CTRL_TYPE_MENU,
> +		.name		= "Test pattern mode",
> +		.min		= 0,
> +		.max		= ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
> +		.step		= 0,
> +		.def		= 0,
> +		.flags		= 0,
> +		.qmenu		= et8ek8_test_pattern_menu,
> +	},
> +	{
> +		.id		= V4L2_CID_MODE_CLASS,
> +		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
> +		.name		= "SMIA-type sensor information",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				| V4L2_CTRL_FLAG_WRITE_ONLY,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_FRAME_WIDTH,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Frame width",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_FRAME_HEIGHT,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Frame height",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_VISIBLE_WIDTH,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Visible width",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_VISIBLE_HEIGHT,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Visible height",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_PIXELCLOCK,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Pixel clock [Hz]",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_SENSITIVITY,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Sensivity",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= V4L2_CID_MODE_OPSYSCLOCK,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Output pixel clock [Hz]",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> +				  | V4L2_CTRL_FLAG_VOLATILE,

Many of the above controls are or standard controls should be used, please
use the native V4L2 control framework functions to create the controls
instead of the custom one. You get control names for free, for instance.

> +	},
> +};
> +
> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
> +{
> +	unsigned int i;
> +	u32 min, max;
> +
> +	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
> +			       ARRAY_SIZE(et8ek8_ctrls) + 2);
> +
> +	/* V4L2_CID_GAIN */
> +	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
> +			  1, 0);
> +
> +	/* V4L2_CID_EXPOSURE */
> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
> +	max = et8ek8_exposure_rows_to_us(sensor,
> +				sensor->current_reglist->mode.max_exp);
> +	sensor->exposure =
> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +				  V4L2_CID_EXPOSURE, min, max, min, max);
> +	sensor->pixel_rate =
> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
> +
> +	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
> +	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
> +		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
> +				     NULL);
> +
> +	if (sensor->ctrl_handler.error)
> +		return sensor->ctrl_handler.error;
> +
> +	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
> +	return 0;
> +}
> +
> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_ctrl *ctrl = sensor->exposure;
> +	u32 min, max;
> +
> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
> +	max = et8ek8_exposure_rows_to_us(sensor,
> +					 sensor->current_reglist->mode.max_exp);
> +
> +	v4l2_ctrl_lock(ctrl);
> +	ctrl->minimum = min;
> +	ctrl->maximum = max;
> +	ctrl->step = min;
> +	ctrl->default_value = max;
> +	ctrl->val = max;
> +	ctrl->cur.val = max;
> +	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
> +				 sensor->current_reglist->mode.ext_clock);
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +
> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_subdev *subdev = &sensor->subdev;
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	int rval;
> +
> +	rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
> +	if (rval)
> +		goto fail;
> +
> +	/* Controls set while the power to the sensor is turned off are saved
> +	 * but not applied to the hardware. Now that we're about to start
> +	 * streaming apply all the current values to the hardware.
> +	 */
> +	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
> +	if (rval)
> +		goto fail;
> +
> +	return 0;
> +
> +fail:
> +	dev_err(&client->dev, "sensor configuration failed\n");
> +	return rval;
> +}
> +
> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +
> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
> +}
> +
> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +
> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
> +}
> +
> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	int ret;
> +
> +	if (!streaming)
> +		return et8ek8_stream_off(sensor);
> +
> +	ret = et8ek8_configure(sensor);
> +	if (ret < 0)
> +		return ret;
> +
> +	return et8ek8_stream_on(sensor);
> +}
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
> +{
> +	int rval;
> +
> +	gpiod_set_value(sensor->reset, 0);
> +	udelay(1);
> +
> +	clk_disable_unprepare(sensor->ext_clk);
> +
> +	rval = regulator_disable(sensor->vana);
> +	return rval;
> +}
> +
> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_subdev *subdev = &sensor->subdev;
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	unsigned int hz = ET8EK8_XCLK_HZ;
> +	int val, rval;
> +
> +	rval = regulator_enable(sensor->vana);
> +	if (rval) {
> +		dev_err(&client->dev, "failed to enable vana regulator\n");
> +		return rval;
> +	}
> +
> +	if (sensor->current_reglist)
> +		hz = sensor->current_reglist->mode.ext_clock;
> +
> +	rval = clk_set_rate(sensor->ext_clk, hz);
> +	if (rval < 0) {
> +		dev_err(&client->dev,
> +			"unable to set extclk clock freq to %u\n", hz);
> +		goto out;
> +	}
> +	rval = clk_prepare_enable(sensor->ext_clk);
> +	if (rval < 0) {
> +		dev_err(&client->dev, "failed to enable extclk\n");
> +		goto out;
> +	}
> +
> +	if (rval)
> +		goto out;
> +
> +	udelay(10);			/* I wish this is a good value */
> +
> +	gpiod_set_value(sensor->reset, 1);
> +
> +	msleep(5000*1000/hz+1);				/* Wait 5000 cycles */
> +
> +	rval = smia_i2c_reglist_find_write(client,
> +					   &et8ek8_smia_meta_reglist,
> +					   SMIA_REGLIST_POWERON);
> +	if (rval)
> +		goto out;
> +
> +#ifdef USE_CRC
> +	rval = smia_i2c_read_reg(client,
> +				 SMIA_REG_8BIT, 0x1263, &val);
> +	if (rval)
> +		goto out;
> +#if USE_CRC
> +	val |= (1<<4);
> +#else
> +	val &= ~(1<<4);
> +#endif
> +	rval = smia_i2c_write_reg(client,
> +				  SMIA_REG_8BIT, 0x1263, val);
> +	if (rval)
> +		goto out;
> +#endif
> +
> +out:
> +	if (rval)
> +		et8ek8_power_off(sensor);
> +
> +	return rval;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev video operations
> + */
> +
> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist, code);
> +}
> +
> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist, fse);
> +}
> +
> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_interval_enum *fie)
> +{
> +	return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist, fie);
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
> +			struct v4l2_subdev_pad_config *cfg,
> +			unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &sensor->format;
> +	default: 
> +		return NULL;
> +	}
> +}
> +
> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct v4l2_mbus_framefmt *format;
> + 
> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct v4l2_mbus_framefmt *format;
> +        struct smia_reglist *reglist;
> +
> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
> +					     &fmt->format);
> +	smia_reglist_to_mbus(reglist, &fmt->format);
> +	*format = fmt->format;
> +
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +		sensor->current_reglist = reglist;
> +		et8ek8_update_controls(sensor);
> +	}
> +
> +	return 0;
> +}
> +
> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	memset(fi, 0, sizeof(*fi));
> +	fi->interval = sensor->current_reglist->mode.timeperframe;
> +
> +	return 0;
> +}
> +
> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct smia_reglist *reglist;
> +
> +	reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
> +					      sensor->current_reglist,
> +					      &fi->interval);
> +
> +	if (!reglist)
> +		return -EINVAL;
> +
> +	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
> +		return -EINVAL;
> +
> +	sensor->current_reglist = reglist;
> +	et8ek8_update_controls(sensor);
> +
> +	return 0;
> +}
> +
> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
> +	unsigned int offset = 0;
> +	u8 *ptr  = sensor->priv_mem;
> +	int rval = 0;
> +
> +	/* Read the EEPROM window-by-window, each window 8 bytes */
> +	do {
> +		u8 buffer[PRIV_MEM_WIN_SIZE];
> +		struct i2c_msg msg;
> +		int bytes, i;
> +		int ofs;
> +
> +		/* Set the current window */
> +		rval = smia_i2c_write_reg(client,
> +					  SMIA_REG_8BIT,
> +					  0x0001,
> +					  0xe0 | (offset >> 3));
> +		if (rval < 0)
> +			goto out;
> +
> +		/* Wait for status bit */
> +		for (i = 0; i < 1000; ++i) {
> +			u32 status;
> +			rval = smia_i2c_read_reg(client,
> +						 SMIA_REG_8BIT,
> +						 0x0003,
> +						 &status);
> +			if (rval < 0)
> +				goto out;
> +			if ((status & 0x08) == 0)
> +				break;
> +			msleep(1);
> +		};
> +
> +		if (i == 1000) {
> +			rval = -EIO;
> +			goto out;
> +		}
> +
> +		/* Read window, 8 bytes at once, and copy to user space */
> +		ofs = offset & 0x07;	/* Offset within this window */
> +		bytes = length + ofs > 8 ? 8-ofs : length;
> +		msg.addr = client->addr;
> +		msg.flags = 0;
> +		msg.len = 2;
> +		msg.buf = buffer;
> +		ofs += PRIV_MEM_START_REG;
> +		buffer[0] = (u8)(ofs >> 8);
> +		buffer[1] = (u8)(ofs & 0xFF);
> +		rval = i2c_transfer(client->adapter, &msg, 1);
> +		if (rval < 0)
> +			goto out;
> +		mdelay(ET8EK8_I2C_DELAY);
> +		msg.addr = client->addr;
> +		msg.len = bytes;
> +		msg.flags = I2C_M_RD;
> +		msg.buf = buffer;
> +		memset(buffer, 0, sizeof(buffer));
> +		rval = i2c_transfer(client->adapter, &msg, 1);
> +		if (rval < 0)
> +			goto out;
> +		rval = 0;
> +		memcpy(ptr, buffer, bytes);
> +
> +		length -= bytes;
> +		offset += bytes;
> +		ptr    += bytes;
> +	} while (length > 0);
> +
> +out:
> +	return rval;
> +}
> +
> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	int rval, rev_l, rev_h;
> +
> +	rval = et8ek8_power_on(sensor);
> +	if (rval) {
> +		dev_err(&client->dev, "could not power on\n");
> +		return rval;
> +	}
> +
> +	if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
> +			      REG_REVISION_NUMBER_L, &rev_l) != 0
> +	    || smia_i2c_read_reg(client, SMIA_REG_8BIT,
> +				 REG_REVISION_NUMBER_H, &rev_h) != 0) {
> +		dev_err(&client->dev,
> +			"no et8ek8 sensor detected\n");
> +		rval = -ENODEV;
> +		goto out_poweroff;
> +	}
> +	sensor->version = (rev_h << 8) + rev_l;
> +	if (sensor->version != ET8EK8_REV_1
> +	    && sensor->version != ET8EK8_REV_2)
> +		dev_info(&client->dev,
> +			 "unknown version 0x%x detected, "
> +			 "continuing anyway\n", sensor->version);
> +
> +	rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
> +	if (rval) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, import failed\n",
> +			ET8EK8_NAME);
> +		goto out_poweroff;
> +	}
> +
> +	sensor->current_reglist =
> +		smia_reglist_find_type(&et8ek8_smia_meta_reglist,
> +				       SMIA_REGLIST_MODE);
> +	if (!sensor->current_reglist) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, no mode found\n",
> +			ET8EK8_NAME);
> +		rval = -ENODEV;
> +		goto out_poweroff;
> +	}
> +
> +	smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
> +
> +	rval = smia_i2c_reglist_find_write(client,
> +					   &et8ek8_smia_meta_reglist,
> +					   SMIA_REGLIST_POWERON);
> +	if (rval) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, no POWERON mode found\n",
> +			ET8EK8_NAME);
> +		goto out_poweroff;
> +	}
> +	rval = et8ek8_stream_on(sensor);	/* Needed to be able to read EEPROM */
> +	if (rval)
> +		goto out_poweroff;
> +	rval = et8ek8_g_priv_mem(subdev);
> +	if (rval)
> +		dev_warn(&client->dev,
> +			"can not read OTP (EEPROM) memory from sensor\n");
> +	rval = et8ek8_stream_off(sensor);
> +	if (rval)
> +		goto out_poweroff;
> +
> +	rval = et8ek8_power_off(sensor);
> +	if (rval)
> +		goto out_poweroff;
> +
> +	return 0;
> +
> +out_poweroff:
> +	et8ek8_power_off(sensor);
> +
> +	return rval;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * sysfs attributes
> + */
> +static ssize_t
> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
> +		     char *buf)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
> +#error PAGE_SIZE too small!
> +#endif
> +
> +	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
> +
> +	return ET8EK8_PRIV_MEM_SIZE;
> +}
> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev core operations
> + */
> +
> +static int
> +et8ek8_registered(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	struct v4l2_mbus_framefmt *format;
> +	int rval;
> +
> +	dev_dbg(&client->dev, "registered!");
> +
> +	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
> +		dev_err(&client->dev, "could not register sysfs entry\n");
> +		return -EBUSY;
> +	}
> +
> +	rval = et8ek8_dev_init(subdev);
> +	if (rval)
> +		return rval;
> +
> +	rval = et8ek8_init_controls(sensor);
> +	if (rval) {
> +		dev_err(&client->dev, "controls initialization failed\n");
> +		return rval;
> +	}
> +
> +	format = __et8ek8_get_pad_format(sensor, NULL, 0,
> +					 V4L2_SUBDEV_FORMAT_ACTIVE);
> +	return 0;
> +}
> +
> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
> +{
> +	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
> +}
> +
> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	int ret = 0;
> +
> +	mutex_lock(&sensor->power_lock);
> +
> +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
> +	 * update the power state.
> +	 */
> +	if (sensor->power_count == !on) {
> +		ret = __et8ek8_set_power(sensor, !!on);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	/* Update the power count. */
> +	sensor->power_count += on ? 1 : -1;
> +	WARN_ON(sensor->power_count < 0);
> +
> +done:
> +	mutex_unlock(&sensor->power_lock);
> +	return ret;
> +}
> +
> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	struct smia_reglist *reglist;
> +
> +	reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
> +					 SMIA_REGLIST_MODE);
> +	format = __et8ek8_get_pad_format(sensor, fh->pad, 0, V4L2_SUBDEV_FORMAT_TRY);
> +	smia_reglist_to_mbus(reglist, format);
> +
> +	return et8ek8_set_power(sd, true);
> +}
> +
> +static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return et8ek8_set_power(sd, false);
> +}
> +
> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
> +	.s_stream = et8ek8_s_stream,
> +	.g_frame_interval = et8ek8_get_frame_interval,
> +	.s_frame_interval = et8ek8_set_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
> +	.s_power = et8ek8_set_power,
> +};
> +
> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
> +	.enum_mbus_code = et8ek8_enum_mbus_code,
> +        .enum_frame_size = et8ek8_enum_frame_size,
> +        .enum_frame_interval = et8ek8_enum_frame_ival,
> +	.get_fmt = et8ek8_get_pad_format,
> +	.set_fmt = et8ek8_set_pad_format,
> +};
> +
> +static const struct v4l2_subdev_ops et8ek8_ops = {
> +	.core = &et8ek8_core_ops,
> +	.video = &et8ek8_video_ops,
> +	.pad = &et8ek8_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
> +	.registered = et8ek8_registered,
> +	.open = et8ek8_open,
> +	.close = et8ek8_close,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * I2C driver
> + */
> +#ifdef CONFIG_PM
> +
> +static int et8ek8_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (!sensor->power_count)
> +		return 0;
> +
> +	return __et8ek8_set_power(sensor, false);
> +}
> +
> +static int et8ek8_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (!sensor->power_count)
> +		return 0;
> +
> +	return __et8ek8_set_power(sensor, true);
> +}
> +
> +static struct dev_pm_ops et8ek8_pm_ops = {
> +	.suspend	= et8ek8_suspend,
> +	.resume		= et8ek8_resume,
> +};
> +
> +#else
> +
> +#define et8ek8_pm_ops	NULL
> +
> +#endif /* CONFIG_PM */
> +
> +static int et8ek8_probe(struct i2c_client *client,
> +			const struct i2c_device_id *devid)
> +{
> +	struct et8ek8_sensor *sensor;
> +	int ret;
> +
> +	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
> +	if (!sensor)
> +		return -ENOMEM;
> +
> +	sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(sensor->reset)) {
> +		dev_dbg(&client->dev, "could not request reset gpio\n");
> +		return PTR_ERR(sensor->reset);;
> +	}
> +
> +	sensor->vana = devm_regulator_get(&client->dev, "vana");
> +	if (IS_ERR(sensor->vana)) {
> +		dev_err(&client->dev, "could not get regulator for vana\n");
> +		return PTR_ERR(sensor->vana);
> +	}
> +
> +	sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
> +	if (IS_ERR(sensor->ext_clk)) {
> +		dev_err(&client->dev, "could not get clock\n");
> +		return PTR_ERR(sensor->ext_clk);
> +	}
> +
> +	mutex_init(&sensor->power_lock);
> +
> +	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
> +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	sensor->subdev.internal_ops = &et8ek8_internal_ops;
> +
> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "media entity init failed!\n");
> +		return ret;
> +	}
> +
> +	ret = v4l2_async_register_subdev(&sensor->subdev);
> +	if (ret < 0) {
> +		media_entity_cleanup(&sensor->subdev.entity);
> +		return ret;
> +	}
> +
> +	dev_dbg(&client->dev, "initialized!\n");
> +
> +	return 0;
> +}
> +
> +static int __exit et8ek8_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (sensor->power_count) {
> +		gpiod_set_value(sensor->reset, 0);
> +		clk_disable_unprepare(sensor->ext_clk);
> +		sensor->power_count = 0;
> +	}
> +
> +	v4l2_device_unregister_subdev(&sensor->subdev);
> +	device_remove_file(&client->dev, &dev_attr_priv_mem);
> +	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
> +	media_entity_cleanup(&sensor->subdev.entity);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id et8ek8_of_table[] = {
> +	{ .compatible = "toshiba,et8ek8" },
> +	{ },
> +};
> +
> +static const struct i2c_device_id et8ek8_id_table[] = {
> +	{ ET8EK8_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
> +
> +static struct i2c_driver et8ek8_i2c_driver = {
> +	.driver		= {
> +		.name	= ET8EK8_NAME,
> +		.pm	= &et8ek8_pm_ops,
> +		.of_match_table	= et8ek8_of_table,
> +	},
> +	.probe		= et8ek8_probe,
> +	.remove		= __exit_p(et8ek8_remove),
> +	.id_table	= et8ek8_id_table,
> +};
> +
> +module_i2c_driver(et8ek8_i2c_driver);
> +
> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");

s/nokia.com/iki.fi/ please.

> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
> +MODULE_LICENSE("GPL");

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check
  2016-04-24 21:08   ` [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check Ivaylo Dimitrov
@ 2016-05-01 10:45     ` Sakari Ailus
  2016-05-03 18:25       ` Ivaylo Dimitrov
  2016-05-24  9:09       ` Pali Rohár
  2016-05-24 10:17     ` Pavel Machek
  1 sibling, 2 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 10:45 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

On Mon, Apr 25, 2016 at 12:08:04AM +0300, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> Required added multiplier (and divisor) calculation did not take into
> account the existing divisor when checking the values against the minimum
> divisor. Do just that.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
> ---
>  drivers/media/i2c/smiapp-pll.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
> index e3348db..5ad1edb 100644
> --- a/drivers/media/i2c/smiapp-pll.c
> +++ b/drivers/media/i2c/smiapp-pll.c
> @@ -227,7 +227,8 @@ static int __smiapp_pll_calculate(
>  
>  	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
>  	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
> -	more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
> +	more_mul_factor = lcm(more_mul_factor,
> +			      DIV_ROUND_UP(op_limits->min_sys_clk_div, div));
>  	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
>  		more_mul_factor);
>  	i = roundup(more_mul_min, more_mul_factor);

I remember writing the patch, but I don't remember what for, or whether it
was really needed. Does the secondary sensor work without this one?

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 06/24] smiapp: Add quirk control support
  2016-04-24 21:08   ` [RFC PATCH 06/24] smiapp: Add quirk control support Ivaylo Dimitrov
@ 2016-05-01 10:46     ` Sakari Ailus
  2016-05-03 18:32       ` Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 10:46 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media, Sakari Ailus

On Mon, Apr 25, 2016 at 12:08:06AM +0300, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> 
> Quirk controls can be set up in the init quirk.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Do you need quirk controls for something at the moment? I guess not at least
with the secondary sensor?

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 17/24] smiapp: add CCP2 support
  2016-04-24 21:08   ` [RFC PATCH 17/24] smiapp: add CCP2 support Ivaylo Dimitrov
@ 2016-05-01 10:57     ` Sakari Ailus
  0 siblings, 0 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 10:57 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

On Mon, Apr 25, 2016 at 12:08:17AM +0300, Ivaylo Dimitrov wrote:
> From: Sebastian Reichel <sre@kernel.org>
> 
> Add support for CCP2 connected SMIA sensors as found
> on the Nokia N900.
> 
> Signed-off-by: Sebastian Reichel <sre@kernel.org>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-05-01 10:44     ` Sakari Ailus
@ 2016-05-01 12:31       ` Ivaylo Dimitrov
  2016-05-01 12:32         ` Ivaylo Dimitrov
  2016-05-01 12:50       ` Ivaylo Dimitrov
  1 sibling, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-01 12:31 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Hi,

On  1.05.2016 13:44, Sakari Ailus wrote:
> Hi Ivaylo,
>
> On Mon, Apr 25, 2016 at 12:08:03AM +0300, Ivaylo Dimitrov wrote:
>> add driver
>>
>> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
>> ---
>>   drivers/media/i2c/smia/Kconfig  |    8 +
>>   drivers/media/i2c/smia/Makefile |    1 +
>>   drivers/media/i2c/smia/et8ek8.c | 1788 +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 1797 insertions(+)
>>   create mode 100644 drivers/media/i2c/smia/et8ek8.c
>>
>> diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
>> index d9be497..13ca043 100644
>> --- a/drivers/media/i2c/smia/Kconfig
>> +++ b/drivers/media/i2c/smia/Kconfig
>> @@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
>>
>>   	  Also a few helper functions are provided to work with binary
>>   	  register lists.
>> +
>> +config VIDEO_ET8EK8
>> +	tristate "ET8EK8 camera sensor support"
>> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	select VIDEO_SMIAREGS
>> +	---help---
>> +	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
>> +	  It is used for example in Nokia N900 (RX-51).
>> diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
>> index cff67bc..56cf15e 100644
>> --- a/drivers/media/i2c/smia/Makefile
>> +++ b/drivers/media/i2c/smia/Makefile
>> @@ -1 +1,2 @@
>>   obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
>> +obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
>> diff --git a/drivers/media/i2c/smia/et8ek8.c b/drivers/media/i2c/smia/et8ek8.c
>> new file mode 100644
>> index 0000000..46c112d
>> --- /dev/null
>> +++ b/drivers/media/i2c/smia/et8ek8.c
>> @@ -0,0 +1,1788 @@
>> +/*
>> + * drivers/media/video/et8ek8.c
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@nokia.com>
>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>> + *
>> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
>> + *
>> + * This driver is based on the Micron MT9T012 camera imager driver
>> + * (C) Texas Instruments.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#define DEBUG
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <linux/version.h>
>> +#include <linux/v4l2-mediabus.h>
>> +
>> +#include <media/media-entity.h>
>> +#include <media/smiaregs.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define ET8EK8_NAME		"et8ek8"
>> +#define ET8EK8_XCLK_HZ		9600000
>> +#define ET8EK8_PRIV_MEM_SIZE	128
>> +
>> +#define CTRL_GAIN		0
>> +#define CTRL_EXPOSURE		1
>> +#define CTRL_TEST_PATTERN	2
>> +
>> +#define CID_TO_CTRL(id)		((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
>> +				 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
>> +				 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
>> +				 -EINVAL)
>> +
>> +struct et8ek8_sensor {
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pad;
>> +	struct v4l2_mbus_framefmt format;
>> +	struct gpio_desc *reset;
>> +	struct regulator *vana;
>> +	struct clk *ext_clk;
>> +
>> +	u16 version;
>> +
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +	struct v4l2_ctrl *exposure;
>> +	struct v4l2_ctrl *pixel_rate;
>> +	struct smia_reglist *current_reglist;
>> +
>> +	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
>> +
>> +	struct mutex power_lock;
>> +	int power_count;
>> +};
>> +
>> +#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
>> +
>> +enum et8ek8_versions {
>> +	ET8EK8_REV_1 = 0x0001,
>> +	ET8EK8_REV_2,
>> +};
>> +
>> +/*
>> + * This table describes what should be written to the sensor register
>> + * for each gain value. The gain(index in the table) is in terms of
>> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
>> + * the *analog gain, [1] in the digital gain
>> + *
>> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
>> + */
>> +static struct et8ek8_gain {
>> +	u16 analog;
>> +	u16 digital;
>> +} const et8ek8_gain_table[] = {
>> +	{ 32,    0},  /* x1 */
>> +	{ 34,    0},
>> +	{ 37,    0},
>> +	{ 39,    0},
>> +	{ 42,    0},
>> +	{ 45,    0},
>> +	{ 49,    0},
>> +	{ 52,    0},
>> +	{ 56,    0},
>> +	{ 60,    0},
>> +	{ 64,    0},  /* x2 */
>> +	{ 69,    0},
>> +	{ 74,    0},
>> +	{ 79,    0},
>> +	{ 84,    0},
>> +	{ 91,    0},
>> +	{ 97,    0},
>> +	{104,    0},
>> +	{111,    0},
>> +	{119,    0},
>> +	{128,    0},  /* x4 */
>> +	{137,    0},
>> +	{147,    0},
>> +	{158,    0},
>> +	{169,    0},
>> +	{181,    0},
>> +	{194,    0},
>> +	{208,    0},
>> +	{223,    0},
>> +	{239,    0},
>> +	{256,    0},  /* x8 */
>> +	{256,   73},
>> +	{256,  152},
>> +	{256,  236},
>> +	{256,  327},
>> +	{256,  424},
>> +	{256,  528},
>> +	{256,  639},
>> +	{256,  758},
>> +	{256,  886},
>> +	{256, 1023},  /* x16 */
>> +};
>> +
>> +/* Register definitions */
>> +#define REG_REVISION_NUMBER_L	0x1200
>> +#define REG_REVISION_NUMBER_H	0x1201
>> +
>> +#define PRIV_MEM_START_REG	0x0008
>> +#define PRIV_MEM_WIN_SIZE	8
>> +
>> +#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
>> +
>> +#define USE_CRC			1
>> +
>> +/*
>> + *
>> + * Stingray sensor mode settings for Scooby
>> + *
>> + *
>> + */
>> +
>
> It'd be nice to get rid of the register lists, however considering where the
> sensor is used it's unlikely going to find its way elsewhere, so the gain
> might not be worth the effort.
>
> I'd still integrate the functionality in the smia register list library to
> the driver. That sort of functionality ideally should not be needed at all
> but sadly, the documentation of some sensors is too bad to write proper
> drivers. :-(
>
> (SMIA is an ill-conceived name for this library btw., it's got nothing to do
> with SMIA as such. I'd call it et8ek8_reglist for example. Perhaps the
> library only would be a better choice.)
>

ok.

>> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
>> +static struct smia_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {	/* 1 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_POWERON,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1207
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x126C, 0xCC },	/* Need to set firstly */
>> +		{ SMIA_REG_8BIT, 0x1269, 0x00 },	/* Strobe and Data of CCP2 delay are minimized. */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },	/* Refined value of Min H_COUNT  */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },	/* Frequency of SPCK setting (SPCK=MRCK) */
>> +		{ SMIA_REG_8BIT, 0x1241, 0x94 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1242, 0x02 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x124B, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1255, 0xFF },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1256, 0x9F },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1258, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* From parallel out to serial out */
>> +		{ SMIA_REG_8BIT, 0x125E, 0xC0 },	/* From w/ embeded data to w/o embeded data */
>> +		{ SMIA_REG_8BIT, 0x1263, 0x98 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1268, 0xC6 },	/* CCP2 out is from STOP to ACTIVE */
>> +		{ SMIA_REG_8BIT, 0x1434, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1163, 0x44 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1166, 0x29 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1140, 0x02 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1011, 0x24 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1151, 0x80 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1152, 0x23 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1014, 0x05 },	/* Initial setting( for improvement2 of lower frequency noise ) */
>> +		{ SMIA_REG_8BIT, 0x1033, 0x06 },
>> +		{ SMIA_REG_8BIT, 0x1034, 0x79 },
>> +		{ SMIA_REG_8BIT, 0x1423, 0x3F },
>> +		{ SMIA_REG_8BIT, 0x1424, 0x3F },
>> +		{ SMIA_REG_8BIT, 0x1426, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1439, 0x00 },	/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x161F, 0x60 },	/* Switch of blemish correction (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x1634, 0x00 },	/* Switch of auto noise correction (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x1646, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1648, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x113E, 0x01 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x113F, 0x22 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
>> +static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {	/* 2 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 560 MHz
>> + * VCO        = 560 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 128 (3072)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 175
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 6
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3072,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1292
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x57 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x06 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x80 },	/* <-changed to v14 7E->80 */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
>> +static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {	/* 3 */
>> +/* (without the +1)
>> + * SPCK       = 96.5333333333333 MHz
>> + * CCP2       = 579.2 MHz
>> + * VCO        = 579.2 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 181
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1008,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 96533333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 3000
>> +		},
>> +		.max_exp = 1004,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x5A },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode4_SVGA_864x656_29.88fps */
>> +static struct smia_reglist mode4_svga_864x656_29_88fps = {	/* 4 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 166 (3984)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3984,
>> +		.height = 672,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 864,
>> +		.window_height = 656,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2988
>> +		},
>> +		.max_exp = 668,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x62 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x62 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xA6 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode5_VGA_648x492_29.93fps */
>> +static struct smia_reglist mode5_vga_648x492_29_93fps = {	/* 5 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2993
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode2_16VGA_2592x1968_3.99fps */
>> +static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {	/* 6 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 254 (6096)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 6096,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 399
>> +		},
>> +		.max_exp = 6092,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0xFE },
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_648x492_5fps */
>> +static struct smia_reglist mode_648x492_5fps = {	/* 7 */
>> +/* (without the +1)
>> + * SPCK       = 13.3333333333333 MHz
>> + * CCP2       = 53.3333333333333 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 5
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 13333333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 499
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x57 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_5fps */
>> +static struct smia_reglist mode3_4vga_1296x984_5fps = {	/* 8 */
>> +/* (without the +1)
>> + * SPCK       = 49.4 MHz
>> + * CCP2       = 395.2 MHz
>> + * VCO        = 790.4 MHz
>> + * VCOUNT     = 250 (6000)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 247
>> + * VCO_DIV    = 1
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 3000,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 49400000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 501
>> +		},
>> +		.max_exp = 2996,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x7B },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x17 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0xFA },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
>> +static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {	/* 9 */
>> +/* (without the +1)
>> + * SPCK       = 84.2666666666667 MHz
>> + * CCP2       = 505.6 MHz
>> + * VCO        = 505.6 MHz
>> + * VCOUNT     = 88 (2112)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 158
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1056,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 84266667,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2500
>> +		},
>> +		.max_exp = 1052,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x4F },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1222, 0x58 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/*        */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
>> +	.magic   = SMIA_MAGIC,
>> +	.version = "V14 03-June-2008",
>> +	.reglist = {
>> +		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
>> +		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
>> +		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
>> +		{ .ptr = &mode4_svga_864x656_29_88fps },
>> +		{ .ptr = &mode5_vga_648x492_29_93fps },
>> +		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
>> +		{ .ptr = &mode_648x492_5fps },
>> +		{ .ptr = &mode3_4vga_1296x984_5fps },
>> +		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
>> +		{ .ptr = 0 }
>> +	}
>> +};
>> +
>> +/*
>> + * Return time of one row in microseconds, .8 fixed point format.
>> + * If the sensor is not set to any mode, return zero.
>> + */
>> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	if (!sensor->current_reglist)
>> +		return 0;
>> +
>> +	clock = sensor->current_reglist->mode.pixel_clock;
>> +	clock = (clock + (1 << 9)) >> 10;
>> +	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
>> +	rt = (rt + (clock >> 1)) / clock;
>> +
>> +	return rt;
>> +}
>> +
>> +/*
>> + * Convert exposure time `us' to rows. Modify `us' to make it to
>> + * correspond to the actual exposure time.
>> + */
>> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
>> +{
>> +	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	/* Assume that the maximum exposure time is at most ~8 s,
>> +	 * and the maximum width (with blanking) ~8000 pixels.
>> +	 * The formula here is in principle as simple as
>> +	 *    rows = exptime / 1e6 / width * pixel_clock
>> +	 * but to get accurate results while coping with value ranges,
>> +	 * have to do some fixed point math.
>> +	 */
>> +
>> +	rt = et8ek8_get_row_time(sensor);
>> +	rows = ((*us << 8) + (rt >> 1)) / rt;
>> +
>> +	if (rows > sensor->current_reglist->mode.max_exp)
>> +		rows = sensor->current_reglist->mode.max_exp;
>> +
>> +	/* Set the exposure time to the rounded value */
>> +	*us = (rt * rows + (1 << 7)) >> 8;
>> +
>> +	return rows;
>> +}
>> +
>> +/*
>> + * Convert exposure time in rows to microseconds
>> + */
>> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
>> +{
>> +	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
>> +}
>> +
>> +/* Called to change the V4L2 gain control value. This function
>> + * rounds and clamps the given value and updates the V4L2 control value.
>> + * If power is on, also updates the sensor analog and digital gains.
>> + * gain is in 0.1 EV (exposure value) units.
>> + */
>> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	struct et8ek8_gain new;
>> +	int r;
>> +
>> +	new = et8ek8_gain_table[gain];
>> +
>> +	/* FIXME: optimise I2C writes! */
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124a, new.analog >> 8);
>> +	if (r)
>> +		return r;
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x1249, new.analog & 0xff);
>> +	if (r)
>> +		return r;
>> +
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124d, new.digital >> 8);
>> +	if (r)
>> +		return r;
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124c, new.digital & 0xff);
>> +
>> +	return r;
>> +}
>> +
>> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
>> +
>> +	/* Values for normal mode */
>> +	cbh_mode = 0;
>> +	cbv_mode = 0;
>> +	tp_mode  = 0;
>> +	din_sw   = 0x00;
>> +	r1420    = 0xF0;
>> +
>> +	if (mode != 0) {
>> +		/* Test pattern mode */
>> +		if (mode < 5) {
>> +			cbh_mode = 1;
>> +			cbv_mode = 1;
>> +			tp_mode  = mode + 3;
>> +		} else {
>> +			cbh_mode = 0;
>> +			cbv_mode = 0;
>> +			tp_mode  = mode - 4 + 3;
>> +		}
>> +		din_sw   = 0x01;
>> +		r1420    = 0xE0;
>> +	}
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode << 4);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121, cbh_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124, cbv_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
>> +	if (rval)
>> +		goto out;
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 controls
>> + */
>> +
>> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	const struct smia_mode *mode = &sensor->current_reglist->mode;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_MODE_FRAME_WIDTH:
>> +		ctrl->cur.val = mode->width;
>> +		break;
>> +	case V4L2_CID_MODE_FRAME_HEIGHT:
>> +		ctrl->cur.val = mode->height;
>> +		break;
>> +	case V4L2_CID_MODE_VISIBLE_WIDTH:
>> +		ctrl->cur.val = mode->window_width;
>> +		break;
>> +	case V4L2_CID_MODE_VISIBLE_HEIGHT:
>> +		ctrl->cur.val = mode->window_height;
>> +		break;
>> +	case V4L2_CID_MODE_PIXELCLOCK:
>> +		ctrl->cur.val = mode->pixel_clock;
>
> Please use V4L2_CID_PIXEL_RATE instead. It's a 64-bit control.
>

Already done, see
>> +		break;
>> +	case V4L2_CID_MODE_SENSITIVITY:
>> +		ctrl->cur.val = mode->sensitivity;
>> +		break;
>> +	case V4L2_CID_MODE_OPSYSCLOCK:
>> +		ctrl->cur.val = mode->opsys_clock;
>> +		break;
>
> V4L2_CID_LINK_FREQ.
>
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int uninitialized_var(rows);
>> +
>> +	if (ctrl->id == V4L2_CID_EXPOSURE)
>> +		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_GAIN:
>> +		return et8ek8_set_gain(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_EXPOSURE:
>> +		return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
>> +					  swab16(rows));
>> +
>> +	case V4L2_CID_TEST_PATTERN:
>> +		return et8ek8_set_test_pattern(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_PIXEL_RATE:
>> +		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
>> +		return 0;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
>> +	.g_volatile_ctrl = et8ek8_get_ctrl,
>> +	.s_ctrl = et8ek8_set_ctrl,
>> +};
>> +
>> +static const char *et8ek8_test_pattern_menu[] = {
>> +	"Normal",
>> +	"Vertical colorbar",
>> +	"Horizontal colorbar",
>> +	"Scale",
>> +	"Ramp",
>> +	"Small vertical colorbar",
>> +	"Small horizontal colorbar",
>> +	"Small scale",
>> +	"Small ramp",
>> +};
>> +
>> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_TEST_PATTERN,
>> +		.type		= V4L2_CTRL_TYPE_MENU,
>> +		.name		= "Test pattern mode",
>> +		.min		= 0,
>> +		.max		= ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
>> +		.step		= 0,
>> +		.def		= 0,
>> +		.flags		= 0,
>> +		.qmenu		= et8ek8_test_pattern_menu,
>> +	},
>> +	{
>> +		.id		= V4L2_CID_MODE_CLASS,
>> +		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
>> +		.name		= "SMIA-type sensor information",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				| V4L2_CTRL_FLAG_WRITE_ONLY,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_FRAME_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_FRAME_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_VISIBLE_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_VISIBLE_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_PIXELCLOCK,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Pixel clock [Hz]",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_SENSITIVITY,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Sensivity",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_OPSYSCLOCK,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Output pixel clock [Hz]",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>
> Many of the above controls are or standard controls should be used, please
> use the native V4L2 control framework functions to create the controls
> instead of the custom one. You get control names for free, for instance.
>
>> +	},
>> +};
>> +
>> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int i;
>> +	u32 min, max;
>> +
>> +	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
>> +			       ARRAY_SIZE(et8ek8_ctrls) + 2);
>> +
>> +	/* V4L2_CID_GAIN */
>> +	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
>> +			  1, 0);
>> +
>> +	/* V4L2_CID_EXPOSURE */
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor,
>> +				sensor->current_reglist->mode.max_exp);
>> +	sensor->exposure =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +				  V4L2_CID_EXPOSURE, min, max, min, max);
>> +	sensor->pixel_rate =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
>> +
>> +	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
>> +	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
>> +		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
>> +				     NULL);
>> +
>> +	if (sensor->ctrl_handler.error)
>> +		return sensor->ctrl_handler.error;
>> +
>> +	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
>> +	return 0;
>> +}
>> +
>> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_ctrl *ctrl = sensor->exposure;
>> +	u32 min, max;
>> +
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor,
>> +					 sensor->current_reglist->mode.max_exp);
>> +
>> +	v4l2_ctrl_lock(ctrl);
>> +	ctrl->minimum = min;
>> +	ctrl->maximum = max;
>> +	ctrl->step = min;
>> +	ctrl->default_value = max;
>> +	ctrl->val = max;
>> +	ctrl->cur.val = max;
>> +	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
>> +				 sensor->current_reglist->mode.ext_clock);
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +
>> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval;
>> +
>> +	rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	/* Controls set while the power to the sensor is turned off are saved
>> +	 * but not applied to the hardware. Now that we're about to start
>> +	 * streaming apply all the current values to the hardware.
>> +	 */
>> +	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	return 0;
>> +
>> +fail:
>> +	dev_err(&client->dev, "sensor configuration failed\n");
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
>> +}
>> +
>> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
>> +}
>> +
>> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret;
>> +
>> +	if (!streaming)
>> +		return et8ek8_stream_off(sensor);
>> +
>> +	ret = et8ek8_configure(sensor);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return et8ek8_stream_on(sensor);
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev operations
>> + */
>> +
>> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
>> +{
>> +	int rval;
>> +
>> +	gpiod_set_value(sensor->reset, 0);
>> +	udelay(1);
>> +
>> +	clk_disable_unprepare(sensor->ext_clk);
>> +
>> +	rval = regulator_disable(sensor->vana);
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int hz = ET8EK8_XCLK_HZ;
>> +	int val, rval;
>> +
>> +	rval = regulator_enable(sensor->vana);
>> +	if (rval) {
>> +		dev_err(&client->dev, "failed to enable vana regulator\n");
>> +		return rval;
>> +	}
>> +
>> +	if (sensor->current_reglist)
>> +		hz = sensor->current_reglist->mode.ext_clock;
>> +
>> +	rval = clk_set_rate(sensor->ext_clk, hz);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev,
>> +			"unable to set extclk clock freq to %u\n", hz);
>> +		goto out;
>> +	}
>> +	rval = clk_prepare_enable(sensor->ext_clk);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev, "failed to enable extclk\n");
>> +		goto out;
>> +	}
>> +
>> +	if (rval)
>> +		goto out;
>> +
>> +	udelay(10);			/* I wish this is a good value */
>> +
>> +	gpiod_set_value(sensor->reset, 1);
>> +
>> +	msleep(5000*1000/hz+1);				/* Wait 5000 cycles */
>> +
>> +	rval = smia_i2c_reglist_find_write(client,
>> +					   &et8ek8_smia_meta_reglist,
>> +					   SMIA_REGLIST_POWERON);
>> +	if (rval)
>> +		goto out;
>> +
>> +#ifdef USE_CRC
>> +	rval = smia_i2c_read_reg(client,
>> +				 SMIA_REG_8BIT, 0x1263, &val);
>> +	if (rval)
>> +		goto out;
>> +#if USE_CRC
>> +	val |= (1<<4);
>> +#else
>> +	val &= ~(1<<4);
>> +#endif
>> +	rval = smia_i2c_write_reg(client,
>> +				  SMIA_REG_8BIT, 0x1263, val);
>> +	if (rval)
>> +		goto out;
>> +#endif
>> +
>> +out:
>> +	if (rval)
>> +		et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev video operations
>> + */
>> +
>> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist, code);
>> +}
>> +
>> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist, fse);
>> +}
>> +
>> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_interval_enum *fie)
>> +{
>> +	return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist, fie);
>> +}
>> +
>> +static struct v4l2_mbus_framefmt *
>> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
>> +			struct v4l2_subdev_pad_config *cfg,
>> +			unsigned int pad, enum v4l2_subdev_format_whence which)
>> +{
>> +	switch (which) {
>> +	case V4L2_SUBDEV_FORMAT_TRY:
>> +		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
>> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
>> +		return &sensor->format;
>> +	default:
>> +		return NULL;
>> +	}
>> +}
>> +
>> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	fmt->format = *format;
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +        struct smia_reglist *reglist;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
>> +					     &fmt->format);
>> +	smia_reglist_to_mbus(reglist, &fmt->format);
>> +	*format = fmt->format;
>> +
>> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> +		sensor->current_reglist = reglist;
>> +		et8ek8_update_controls(sensor);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	memset(fi, 0, sizeof(*fi));
>> +	fi->interval = sensor->current_reglist->mode.timeperframe;
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct smia_reglist *reglist;
>> +
>> +	reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
>> +					      sensor->current_reglist,
>> +					      &fi->interval);
>> +
>> +	if (!reglist)
>> +		return -EINVAL;
>> +
>> +	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
>> +		return -EINVAL;
>> +
>> +	sensor->current_reglist = reglist;
>> +	et8ek8_update_controls(sensor);
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
>> +	unsigned int offset = 0;
>> +	u8 *ptr  = sensor->priv_mem;
>> +	int rval = 0;
>> +
>> +	/* Read the EEPROM window-by-window, each window 8 bytes */
>> +	do {
>> +		u8 buffer[PRIV_MEM_WIN_SIZE];
>> +		struct i2c_msg msg;
>> +		int bytes, i;
>> +		int ofs;
>> +
>> +		/* Set the current window */
>> +		rval = smia_i2c_write_reg(client,
>> +					  SMIA_REG_8BIT,
>> +					  0x0001,
>> +					  0xe0 | (offset >> 3));
>> +		if (rval < 0)
>> +			goto out;
>> +
>> +		/* Wait for status bit */
>> +		for (i = 0; i < 1000; ++i) {
>> +			u32 status;
>> +			rval = smia_i2c_read_reg(client,
>> +						 SMIA_REG_8BIT,
>> +						 0x0003,
>> +						 &status);
>> +			if (rval < 0)
>> +				goto out;
>> +			if ((status & 0x08) == 0)
>> +				break;
>> +			msleep(1);
>> +		};
>> +
>> +		if (i == 1000) {
>> +			rval = -EIO;
>> +			goto out;
>> +		}
>> +
>> +		/* Read window, 8 bytes at once, and copy to user space */
>> +		ofs = offset & 0x07;	/* Offset within this window */
>> +		bytes = length + ofs > 8 ? 8-ofs : length;
>> +		msg.addr = client->addr;
>> +		msg.flags = 0;
>> +		msg.len = 2;
>> +		msg.buf = buffer;
>> +		ofs += PRIV_MEM_START_REG;
>> +		buffer[0] = (u8)(ofs >> 8);
>> +		buffer[1] = (u8)(ofs & 0xFF);
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		mdelay(ET8EK8_I2C_DELAY);
>> +		msg.addr = client->addr;
>> +		msg.len = bytes;
>> +		msg.flags = I2C_M_RD;
>> +		msg.buf = buffer;
>> +		memset(buffer, 0, sizeof(buffer));
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		rval = 0;
>> +		memcpy(ptr, buffer, bytes);
>> +
>> +		length -= bytes;
>> +		offset += bytes;
>> +		ptr    += bytes;
>> +	} while (length > 0);
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval, rev_l, rev_h;
>> +
>> +	rval = et8ek8_power_on(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "could not power on\n");
>> +		return rval;
>> +	}
>> +
>> +	if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
>> +			      REG_REVISION_NUMBER_L, &rev_l) != 0
>> +	    || smia_i2c_read_reg(client, SMIA_REG_8BIT,
>> +				 REG_REVISION_NUMBER_H, &rev_h) != 0) {
>> +		dev_err(&client->dev,
>> +			"no et8ek8 sensor detected\n");
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +	sensor->version = (rev_h << 8) + rev_l;
>> +	if (sensor->version != ET8EK8_REV_1
>> +	    && sensor->version != ET8EK8_REV_2)
>> +		dev_info(&client->dev,
>> +			 "unknown version 0x%x detected, "
>> +			 "continuing anyway\n", sensor->version);
>> +
>> +	rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, import failed\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +
>> +	sensor->current_reglist =
>> +		smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>> +				       SMIA_REGLIST_MODE);
>> +	if (!sensor->current_reglist) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no mode found\n",
>> +			ET8EK8_NAME);
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +
>> +	smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
>> +
>> +	rval = smia_i2c_reglist_find_write(client,
>> +					   &et8ek8_smia_meta_reglist,
>> +					   SMIA_REGLIST_POWERON);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no POWERON mode found\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +	rval = et8ek8_stream_on(sensor);	/* Needed to be able to read EEPROM */
>> +	if (rval)
>> +		goto out_poweroff;
>> +	rval = et8ek8_g_priv_mem(subdev);
>> +	if (rval)
>> +		dev_warn(&client->dev,
>> +			"can not read OTP (EEPROM) memory from sensor\n");
>> +	rval = et8ek8_stream_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	rval = et8ek8_power_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	return 0;
>> +
>> +out_poweroff:
>> +	et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * sysfs attributes
>> + */
>> +static ssize_t
>> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
>> +		     char *buf)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
>> +#error PAGE_SIZE too small!
>> +#endif
>> +
>> +	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
>> +
>> +	return ET8EK8_PRIV_MEM_SIZE;
>> +}
>> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev core operations
>> + */
>> +
>> +static int
>> +et8ek8_registered(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +	int rval;
>> +
>> +	dev_dbg(&client->dev, "registered!");
>> +
>> +	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
>> +		dev_err(&client->dev, "could not register sysfs entry\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	rval = et8ek8_dev_init(subdev);
>> +	if (rval)
>> +		return rval;
>> +
>> +	rval = et8ek8_init_controls(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "controls initialization failed\n");
>> +		return rval;
>> +	}
>> +
>> +	format = __et8ek8_get_pad_format(sensor, NULL, 0,
>> +					 V4L2_SUBDEV_FORMAT_ACTIVE);
>> +	return 0;
>> +}
>> +
>> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
>> +{
>> +	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
>> +}
>> +
>> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret = 0;
>> +
>> +	mutex_lock(&sensor->power_lock);
>> +
>> +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> +	 * update the power state.
>> +	 */
>> +	if (sensor->power_count == !on) {
>> +		ret = __et8ek8_set_power(sensor, !!on);
>> +		if (ret < 0)
>> +			goto done;
>> +	}
>> +
>> +	/* Update the power count. */
>> +	sensor->power_count += on ? 1 : -1;
>> +	WARN_ON(sensor->power_count < 0);
>> +
>> +done:
>> +	mutex_unlock(&sensor->power_lock);
>> +	return ret;
>> +}
>> +
>> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
>> +	struct v4l2_mbus_framefmt *format;
>> +	struct smia_reglist *reglist;
>> +
>> +	reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>> +					 SMIA_REGLIST_MODE);
>> +	format = __et8ek8_get_pad_format(sensor, fh->pad, 0, V4L2_SUBDEV_FORMAT_TRY);
>> +	smia_reglist_to_mbus(reglist, format);
>> +
>> +	return et8ek8_set_power(sd, true);
>> +}
>> +
>> +static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	return et8ek8_set_power(sd, false);
>> +}
>> +
>> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
>> +	.s_stream = et8ek8_s_stream,
>> +	.g_frame_interval = et8ek8_get_frame_interval,
>> +	.s_frame_interval = et8ek8_set_frame_interval,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
>> +	.s_power = et8ek8_set_power,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
>> +	.enum_mbus_code = et8ek8_enum_mbus_code,
>> +        .enum_frame_size = et8ek8_enum_frame_size,
>> +        .enum_frame_interval = et8ek8_enum_frame_ival,
>> +	.get_fmt = et8ek8_get_pad_format,
>> +	.set_fmt = et8ek8_set_pad_format,
>> +};
>> +
>> +static const struct v4l2_subdev_ops et8ek8_ops = {
>> +	.core = &et8ek8_core_ops,
>> +	.video = &et8ek8_video_ops,
>> +	.pad = &et8ek8_pad_ops,
>> +};
>> +
>> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
>> +	.registered = et8ek8_registered,
>> +	.open = et8ek8_open,
>> +	.close = et8ek8_close,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * I2C driver
>> + */
>> +#ifdef CONFIG_PM
>> +
>> +static int et8ek8_suspend(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, false);
>> +}
>> +
>> +static int et8ek8_resume(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, true);
>> +}
>> +
>> +static struct dev_pm_ops et8ek8_pm_ops = {
>> +	.suspend	= et8ek8_suspend,
>> +	.resume		= et8ek8_resume,
>> +};
>> +
>> +#else
>> +
>> +#define et8ek8_pm_ops	NULL
>> +
>> +#endif /* CONFIG_PM */
>> +
>> +static int et8ek8_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *devid)
>> +{
>> +	struct et8ek8_sensor *sensor;
>> +	int ret;
>> +
>> +	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
>> +	if (!sensor)
>> +		return -ENOMEM;
>> +
>> +	sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(sensor->reset)) {
>> +		dev_dbg(&client->dev, "could not request reset gpio\n");
>> +		return PTR_ERR(sensor->reset);;
>> +	}
>> +
>> +	sensor->vana = devm_regulator_get(&client->dev, "vana");
>> +	if (IS_ERR(sensor->vana)) {
>> +		dev_err(&client->dev, "could not get regulator for vana\n");
>> +		return PTR_ERR(sensor->vana);
>> +	}
>> +
>> +	sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
>> +	if (IS_ERR(sensor->ext_clk)) {
>> +		dev_err(&client->dev, "could not get clock\n");
>> +		return PTR_ERR(sensor->ext_clk);
>> +	}
>> +
>> +	mutex_init(&sensor->power_lock);
>> +
>> +	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
>> +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	sensor->subdev.internal_ops = &et8ek8_internal_ops;
>> +
>> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "media entity init failed!\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = v4l2_async_register_subdev(&sensor->subdev);
>> +	if (ret < 0) {
>> +		media_entity_cleanup(&sensor->subdev.entity);
>> +		return ret;
>> +	}
>> +
>> +	dev_dbg(&client->dev, "initialized!\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static int __exit et8ek8_remove(struct i2c_client *client)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (sensor->power_count) {
>> +		gpiod_set_value(sensor->reset, 0);
>> +		clk_disable_unprepare(sensor->ext_clk);
>> +		sensor->power_count = 0;
>> +	}
>> +
>> +	v4l2_device_unregister_subdev(&sensor->subdev);
>> +	device_remove_file(&client->dev, &dev_attr_priv_mem);
>> +	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
>> +	media_entity_cleanup(&sensor->subdev.entity);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id et8ek8_of_table[] = {
>> +	{ .compatible = "toshiba,et8ek8" },
>> +	{ },
>> +};
>> +
>> +static const struct i2c_device_id et8ek8_id_table[] = {
>> +	{ ET8EK8_NAME, 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
>> +
>> +static struct i2c_driver et8ek8_i2c_driver = {
>> +	.driver		= {
>> +		.name	= ET8EK8_NAME,
>> +		.pm	= &et8ek8_pm_ops,
>> +		.of_match_table	= et8ek8_of_table,
>> +	},
>> +	.probe		= et8ek8_probe,
>> +	.remove		= __exit_p(et8ek8_remove),
>> +	.id_table	= et8ek8_id_table,
>> +};
>> +
>> +module_i2c_driver(et8ek8_i2c_driver);
>> +
>> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
>
> s/nokia.com/iki.fi/ please.
>
>> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
>> +MODULE_LICENSE("GPL");
>

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

* Re: [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-05-01 12:31       ` Ivaylo Dimitrov
@ 2016-05-01 12:32         ` Ivaylo Dimitrov
  0 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-01 12:32 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Ignore that mail, I pushed send by mistake

On  1.05.2016 15:31, Ivaylo Dimitrov wrote:
> Hi,
>
> On  1.05.2016 13:44, Sakari Ailus wrote:
>> Hi Ivaylo,
>>
>> On Mon, Apr 25, 2016 at 12:08:03AM +0300, Ivaylo Dimitrov wrote:
>>> add driver
>>>
>>> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
>>> ---
>>>   drivers/media/i2c/smia/Kconfig  |    8 +
>>>   drivers/media/i2c/smia/Makefile |    1 +
>>>   drivers/media/i2c/smia/et8ek8.c | 1788
>>> +++++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 1797 insertions(+)
>>>   create mode 100644 drivers/media/i2c/smia/et8ek8.c
>>>
>>> diff --git a/drivers/media/i2c/smia/Kconfig
>>> b/drivers/media/i2c/smia/Kconfig
>>> index d9be497..13ca043 100644
>>> --- a/drivers/media/i2c/smia/Kconfig
>>> +++ b/drivers/media/i2c/smia/Kconfig
>>> @@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
>>>
>>>         Also a few helper functions are provided to work with binary
>>>         register lists.
>>> +
>>> +config VIDEO_ET8EK8
>>> +    tristate "ET8EK8 camera sensor support"
>>> +    depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>>> +    select VIDEO_SMIAREGS
>>> +    ---help---
>>> +      This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
>>> +      It is used for example in Nokia N900 (RX-51).
>>> diff --git a/drivers/media/i2c/smia/Makefile
>>> b/drivers/media/i2c/smia/Makefile
>>> index cff67bc..56cf15e 100644
>>> --- a/drivers/media/i2c/smia/Makefile
>>> +++ b/drivers/media/i2c/smia/Makefile
>>> @@ -1 +1,2 @@
>>>   obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
>>> +obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
>>> diff --git a/drivers/media/i2c/smia/et8ek8.c
>>> b/drivers/media/i2c/smia/et8ek8.c
>>> new file mode 100644
>>> index 0000000..46c112d
>>> --- /dev/null
>>> +++ b/drivers/media/i2c/smia/et8ek8.c
>>> @@ -0,0 +1,1788 @@
>>> +/*
>>> + * drivers/media/video/et8ek8.c
>>> + *
>>> + * Copyright (C) 2008 Nokia Corporation
>>> + *
>>> + * Contact: Sakari Ailus <sakari.ailus@nokia.com>
>>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>>> + *
>>> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
>>> + *
>>> + * This driver is based on the Micron MT9T012 camera imager driver
>>> + * (C) Texas Instruments.
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * version 2 as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#define DEBUG
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/gpio/consumer.h>
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/version.h>
>>> +#include <linux/v4l2-mediabus.h>
>>> +
>>> +#include <media/media-entity.h>
>>> +#include <media/smiaregs.h>
>>> +#include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-device.h>
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +#define ET8EK8_NAME        "et8ek8"
>>> +#define ET8EK8_XCLK_HZ        9600000
>>> +#define ET8EK8_PRIV_MEM_SIZE    128
>>> +
>>> +#define CTRL_GAIN        0
>>> +#define CTRL_EXPOSURE        1
>>> +#define CTRL_TEST_PATTERN    2
>>> +
>>> +#define CID_TO_CTRL(id)        ((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
>>> +                 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
>>> +                 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
>>> +                 -EINVAL)
>>> +
>>> +struct et8ek8_sensor {
>>> +    struct v4l2_subdev subdev;
>>> +    struct media_pad pad;
>>> +    struct v4l2_mbus_framefmt format;
>>> +    struct gpio_desc *reset;
>>> +    struct regulator *vana;
>>> +    struct clk *ext_clk;
>>> +
>>> +    u16 version;
>>> +
>>> +    struct v4l2_ctrl_handler ctrl_handler;
>>> +    struct v4l2_ctrl *exposure;
>>> +    struct v4l2_ctrl *pixel_rate;
>>> +    struct smia_reglist *current_reglist;
>>> +
>>> +    u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
>>> +
>>> +    struct mutex power_lock;
>>> +    int power_count;
>>> +};
>>> +
>>> +#define to_et8ek8_sensor(sd)    container_of(sd, struct
>>> et8ek8_sensor, subdev)
>>> +
>>> +enum et8ek8_versions {
>>> +    ET8EK8_REV_1 = 0x0001,
>>> +    ET8EK8_REV_2,
>>> +};
>>> +
>>> +/*
>>> + * This table describes what should be written to the sensor register
>>> + * for each gain value. The gain(index in the table) is in terms of
>>> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
>>> + * the *analog gain, [1] in the digital gain
>>> + *
>>> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
>>> + */
>>> +static struct et8ek8_gain {
>>> +    u16 analog;
>>> +    u16 digital;
>>> +} const et8ek8_gain_table[] = {
>>> +    { 32,    0},  /* x1 */
>>> +    { 34,    0},
>>> +    { 37,    0},
>>> +    { 39,    0},
>>> +    { 42,    0},
>>> +    { 45,    0},
>>> +    { 49,    0},
>>> +    { 52,    0},
>>> +    { 56,    0},
>>> +    { 60,    0},
>>> +    { 64,    0},  /* x2 */
>>> +    { 69,    0},
>>> +    { 74,    0},
>>> +    { 79,    0},
>>> +    { 84,    0},
>>> +    { 91,    0},
>>> +    { 97,    0},
>>> +    {104,    0},
>>> +    {111,    0},
>>> +    {119,    0},
>>> +    {128,    0},  /* x4 */
>>> +    {137,    0},
>>> +    {147,    0},
>>> +    {158,    0},
>>> +    {169,    0},
>>> +    {181,    0},
>>> +    {194,    0},
>>> +    {208,    0},
>>> +    {223,    0},
>>> +    {239,    0},
>>> +    {256,    0},  /* x8 */
>>> +    {256,   73},
>>> +    {256,  152},
>>> +    {256,  236},
>>> +    {256,  327},
>>> +    {256,  424},
>>> +    {256,  528},
>>> +    {256,  639},
>>> +    {256,  758},
>>> +    {256,  886},
>>> +    {256, 1023},  /* x16 */
>>> +};
>>> +
>>> +/* Register definitions */
>>> +#define REG_REVISION_NUMBER_L    0x1200
>>> +#define REG_REVISION_NUMBER_H    0x1201
>>> +
>>> +#define PRIV_MEM_START_REG    0x0008
>>> +#define PRIV_MEM_WIN_SIZE    8
>>> +
>>> +#define ET8EK8_I2C_DELAY    3    /* msec delay b/w accesses */
>>> +
>>> +#define USE_CRC            1
>>> +
>>> +/*
>>> + *
>>> + * Stingray sensor mode settings for Scooby
>>> + *
>>> + *
>>> + */
>>> +
>>
>> It'd be nice to get rid of the register lists, however considering
>> where the
>> sensor is used it's unlikely going to find its way elsewhere, so the gain
>> might not be worth the effort.
>>
>> I'd still integrate the functionality in the smia register list
>> library to
>> the driver. That sort of functionality ideally should not be needed at
>> all
>> but sadly, the documentation of some sensors is too bad to write proper
>> drivers. :-(
>>
>> (SMIA is an ill-conceived name for this library btw., it's got nothing
>> to do
>> with SMIA as such. I'd call it et8ek8_reglist for example. Perhaps the
>> library only would be a better choice.)
>>
>
> ok.
>
>>> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
>>> +static struct smia_reglist
>>> mode1_poweron_mode2_16vga_2592x1968_12_07fps = {    /* 1 */
>>> +/* (without the +1)
>>> + * SPCK       = 80 MHz
>>> + * CCP2       = 640 MHz
>>> + * VCO        = 640 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 137 (3288)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 200
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_POWERON,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3288,
>>> +        .height = 2016,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 2592,
>>> +        .window_height = 1968,
>>> +        .pixel_clock = 80000000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 1207
>>> +        },
>>> +        .max_exp = 2012,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x126C, 0xCC },    /* Need to set firstly */
>>> +        { SMIA_REG_8BIT, 0x1269, 0x00 },    /* Strobe and Data of
>>> CCP2 delay are minimized. */
>>> +        { SMIA_REG_8BIT, 0x1220, 0x89 },    /* Refined value of Min
>>> H_COUNT  */
>>> +        { SMIA_REG_8BIT, 0x123A, 0x07 },    /* Frequency of SPCK
>>> setting (SPCK=MRCK) */
>>> +        { SMIA_REG_8BIT, 0x1241, 0x94 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1242, 0x02 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x124B, 0x00 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1255, 0xFF },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1256, 0x9F },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1258, 0x00 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* From parallel out to
>>> serial out */
>>> +        { SMIA_REG_8BIT, 0x125E, 0xC0 },    /* From w/ embeded data
>>> to w/o embeded data */
>>> +        { SMIA_REG_8BIT, 0x1263, 0x98 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1268, 0xC6 },    /* CCP2 out is from STOP
>>> to ACTIVE */
>>> +        { SMIA_REG_8BIT, 0x1434, 0x00 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1163, 0x44 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1166, 0x29 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1140, 0x02 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1011, 0x24 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1151, 0x80 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1152, 0x23 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1014, 0x05 },    /* Initial setting( for
>>> improvement2 of lower frequency noise ) */
>>> +        { SMIA_REG_8BIT, 0x1033, 0x06 },
>>> +        { SMIA_REG_8BIT, 0x1034, 0x79 },
>>> +        { SMIA_REG_8BIT, 0x1423, 0x3F },
>>> +        { SMIA_REG_8BIT, 0x1424, 0x3F },
>>> +        { SMIA_REG_8BIT, 0x1426, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1439, 0x00 },    /* Switch of
>>> Preset-White-balance (0d:disable / 1d:enable) */
>>> +        { SMIA_REG_8BIT, 0x161F, 0x60 },    /* Switch of blemish
>>> correction (0d:disable / 1d:enable) */
>>> +        { SMIA_REG_8BIT, 0x1634, 0x00 },    /* Switch of auto noise
>>> correction (0d:disable / 1d:enable) */
>>> +        { SMIA_REG_8BIT, 0x1646, 0x00 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1648, 0x00 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x113E, 0x01 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x113F, 0x22 },    /* Initial setting */
>>> +        { SMIA_REG_8BIT, 0x1239, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x07 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0x89 },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
>>> +static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 =
>>> {    /* 2 */
>>> +/* (without the +1)
>>> + * SPCK       = 80 MHz
>>> + * CCP2       = 560 MHz
>>> + * VCO        = 560 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 128 (3072)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 175
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 6
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3072,
>>> +        .height = 2016,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 2592,
>>> +        .window_height = 1968,
>>> +        .pixel_clock = 80000000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 1292
>>> +        },
>>> +        .max_exp = 2012,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x57 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x82 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x06 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0x80 },    /* <-changed to v14
>>> 7E->80 */
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x83 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
>>> +static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 =
>>> {    /* 3 */
>>> +/* (without the +1)
>>> + * SPCK       = 96.5333333333333 MHz
>>> + * CCP2       = 579.2 MHz
>>> + * VCO        = 579.2 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 133 (3192)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 181
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 5
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3192,
>>> +        .height = 1008,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 1296,
>>> +        .window_height = 984,
>>> +        .pixel_clock = 96533333,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 3000
>>> +        },
>>> +        .max_exp = 1004,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x5A },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1238, 0x82 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x123A, 0x05 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x121B, 0x63 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1220, 0x85 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x121D, 0x63 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x83 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode4_SVGA_864x656_29.88fps */
>>> +static struct smia_reglist mode4_svga_864x656_29_88fps = {    /* 4 */
>>> +/* (without the +1)
>>> + * SPCK       = 80 MHz
>>> + * CCP2       = 320 MHz
>>> + * VCO        = 640 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 166 (3984)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 200
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 1
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3984,
>>> +        .height = 672,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 864,
>>> +        .window_height = 656,
>>> +        .pixel_clock = 80000000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 2988
>>> +        },
>>> +        .max_exp = 668,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x71 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x07 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x62 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x62 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0xA6 },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode5_VGA_648x492_29.93fps */
>>> +static struct smia_reglist mode5_vga_648x492_29_93fps = {    /* 5 */
>>> +/* (without the +1)
>>> + * SPCK       = 80 MHz
>>> + * CCP2       = 320 MHz
>>> + * VCO        = 640 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 221 (5304)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 200
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 1
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 5304,
>>> +        .height = 504,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 648,
>>> +        .window_height = 492,
>>> +        .pixel_clock = 80000000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 2993
>>> +        },
>>> +        .max_exp = 500,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x71 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x07 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x61 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x61 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0xDD },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode2_16VGA_2592x1968_3.99fps */
>>> +static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {    /* 6 */
>>> +/* (without the +1)
>>> + * SPCK       = 80 MHz
>>> + * CCP2       = 640 MHz
>>> + * VCO        = 640 MHz
>>> + * VCOUNT     = 254 (6096)
>>> + * HCOUNT     = 137 (3288)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 200
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3288,
>>> +        .height = 6096,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 2592,
>>> +        .window_height = 1968,
>>> +        .pixel_clock = 80000000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 399
>>> +        },
>>> +        .max_exp = 6092,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x07 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0x89 },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0xFE },
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode_648x492_5fps */
>>> +static struct smia_reglist mode_648x492_5fps = {    /* 7 */
>>> +/* (without the +1)
>>> + * SPCK       = 13.3333333333333 MHz
>>> + * CCP2       = 53.3333333333333 MHz
>>> + * VCO        = 640 MHz
>>> + * VCOUNT     = 84 (2016)
>>> + * HCOUNT     = 221 (5304)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 200
>>> + * VCO_DIV    = 5
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 1
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 5304,
>>> +        .height = 504,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 648,
>>> +        .window_height = 492,
>>> +        .pixel_clock = 13333333,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 499
>>> +        },
>>> +        .max_exp = 500,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x64 },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x71 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x57 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x61 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x61 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0xDD },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0x54 },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode3_4VGA_1296x984_5fps */
>>> +static struct smia_reglist mode3_4vga_1296x984_5fps = {    /* 8 */
>>> +/* (without the +1)
>>> + * SPCK       = 49.4 MHz
>>> + * CCP2       = 395.2 MHz
>>> + * VCO        = 790.4 MHz
>>> + * VCOUNT     = 250 (6000)
>>> + * HCOUNT     = 137 (3288)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 247
>>> + * VCO_DIV    = 1
>>> + * SPCK_DIV   = 7
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3288,
>>> +        .height = 3000,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 1296,
>>> +        .window_height = 984,
>>> +        .pixel_clock = 49400000,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 501
>>> +        },
>>> +        .max_exp = 2996,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x7B },
>>> +        { SMIA_REG_8BIT, 0x1238, 0x82 },
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },
>>> +        { SMIA_REG_8BIT, 0x123A, 0x17 },
>>> +        { SMIA_REG_8BIT, 0x121B, 0x63 },
>>> +        { SMIA_REG_8BIT, 0x121D, 0x63 },
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1220, 0x89 },
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },
>>> +        { SMIA_REG_8BIT, 0x1222, 0xFA },
>>> +        { SMIA_REG_8BIT, 0x125D, 0x88 },    /* CCP_LVDS_MODE/  */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
>>> +static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {
>>> /* 9 */
>>> +/* (without the +1)
>>> + * SPCK       = 84.2666666666667 MHz
>>> + * CCP2       = 505.6 MHz
>>> + * VCO        = 505.6 MHz
>>> + * VCOUNT     = 88 (2112)
>>> + * HCOUNT     = 133 (3192)
>>> + * CKREF_DIV  = 2
>>> + * CKVAR_DIV  = 158
>>> + * VCO_DIV    = 0
>>> + * SPCK_DIV   = 5
>>> + * MRCK_DIV   = 7
>>> + * LVDSCK_DIV = 0
>>> + */
>>> +    .type = SMIA_REGLIST_MODE,
>>> +    .mode = {
>>> +        .sensor_width = 2592,
>>> +        .sensor_height = 1968,
>>> +        .sensor_window_origin_x = 0,
>>> +        .sensor_window_origin_y = 0,
>>> +        .sensor_window_width = 2592,
>>> +        .sensor_window_height = 1968,
>>> +        .width = 3192,
>>> +        .height = 1056,
>>> +        .window_origin_x = 0,
>>> +        .window_origin_y = 0,
>>> +        .window_width = 1296,
>>> +        .window_height = 984,
>>> +        .pixel_clock = 84266667,
>>> +        .ext_clock = 9600000,
>>> +        .timeperframe = {
>>> +            .numerator = 100,
>>> +            .denominator = 2500
>>> +        },
>>> +        .max_exp = 1052,
>>> +        /* .max_gain = 0, */
>>> +        .pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>>> +        .sensitivity = 65536
>>> +    },
>>> +    .regs = {
>>> +        { SMIA_REG_8BIT, 0x1239, 0x4F },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1238, 0x02 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x123B, 0x70 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x123A, 0x05 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x121B, 0x63 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1220, 0x85 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1221, 0x00 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1222, 0x58 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x1223, 0x00 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x121D, 0x63 },    /*        */
>>> +        { SMIA_REG_8BIT, 0x125D, 0x83 },    /*        */
>>> +        { SMIA_REG_TERM, 0, 0}
>>> +    }
>>> +};
>>> +
>>> +static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
>>> +    .magic   = SMIA_MAGIC,
>>> +    .version = "V14 03-June-2008",
>>> +    .reglist = {
>>> +        { .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
>>> +        { .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
>>> +        { .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
>>> +        { .ptr = &mode4_svga_864x656_29_88fps },
>>> +        { .ptr = &mode5_vga_648x492_29_93fps },
>>> +        { .ptr = &mode2_16vga_2592x1968_3_99fps },
>>> +        { .ptr = &mode_648x492_5fps },
>>> +        { .ptr = &mode3_4vga_1296x984_5fps },
>>> +        { .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
>>> +        { .ptr = 0 }
>>> +    }
>>> +};
>>> +
>>> +/*
>>> + * Return time of one row in microseconds, .8 fixed point format.
>>> + * If the sensor is not set to any mode, return zero.
>>> + */
>>> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
>>> +{
>>> +    unsigned int clock;    /* Pixel clock in Hz>>10 fixed point */
>>> +    unsigned int rt;    /* Row time in .8 fixed point */
>>> +
>>> +    if (!sensor->current_reglist)
>>> +        return 0;
>>> +
>>> +    clock = sensor->current_reglist->mode.pixel_clock;
>>> +    clock = (clock + (1 << 9)) >> 10;
>>> +    rt = sensor->current_reglist->mode.width * (1000000 >> 2);
>>> +    rt = (rt + (clock >> 1)) / clock;
>>> +
>>> +    return rt;
>>> +}
>>> +
>>> +/*
>>> + * Convert exposure time `us' to rows. Modify `us' to make it to
>>> + * correspond to the actual exposure time.
>>> + */
>>> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor,
>>> u32 *us)
>>> +{
>>> +    unsigned int rows;    /* Exposure value as written to HW (ie.
>>> rows) */
>>> +    unsigned int rt;    /* Row time in .8 fixed point */
>>> +
>>> +    /* Assume that the maximum exposure time is at most ~8 s,
>>> +     * and the maximum width (with blanking) ~8000 pixels.
>>> +     * The formula here is in principle as simple as
>>> +     *    rows = exptime / 1e6 / width * pixel_clock
>>> +     * but to get accurate results while coping with value ranges,
>>> +     * have to do some fixed point math.
>>> +     */
>>> +
>>> +    rt = et8ek8_get_row_time(sensor);
>>> +    rows = ((*us << 8) + (rt >> 1)) / rt;
>>> +
>>> +    if (rows > sensor->current_reglist->mode.max_exp)
>>> +        rows = sensor->current_reglist->mode.max_exp;
>>> +
>>> +    /* Set the exposure time to the rounded value */
>>> +    *us = (rt * rows + (1 << 7)) >> 8;
>>> +
>>> +    return rows;
>>> +}
>>> +
>>> +/*
>>> + * Convert exposure time in rows to microseconds
>>> + */
>>> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor,
>>> int rows)
>>> +{
>>> +    return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
>>> +}
>>> +
>>> +/* Called to change the V4L2 gain control value. This function
>>> + * rounds and clamps the given value and updates the V4L2 control
>>> value.
>>> + * If power is on, also updates the sensor analog and digital gains.
>>> + * gain is in 0.1 EV (exposure value) units.
>>> + */
>>> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
>>> +{
>>> +    struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>>> +    struct et8ek8_gain new;
>>> +    int r;
>>> +
>>> +    new = et8ek8_gain_table[gain];
>>> +
>>> +    /* FIXME: optimise I2C writes! */
>>> +    r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>>> +                0x124a, new.analog >> 8);
>>> +    if (r)
>>> +        return r;
>>> +    r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>>> +                0x1249, new.analog & 0xff);
>>> +    if (r)
>>> +        return r;
>>> +
>>> +    r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>>> +                0x124d, new.digital >> 8);
>>> +    if (r)
>>> +        return r;
>>> +    r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>>> +                0x124c, new.digital & 0xff);
>>> +
>>> +    return r;
>>> +}
>>> +
>>> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32
>>> mode)
>>> +{
>>> +    struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>>> +    int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
>>> +
>>> +    /* Values for normal mode */
>>> +    cbh_mode = 0;
>>> +    cbv_mode = 0;
>>> +    tp_mode  = 0;
>>> +    din_sw   = 0x00;
>>> +    r1420    = 0xF0;
>>> +
>>> +    if (mode != 0) {
>>> +        /* Test pattern mode */
>>> +        if (mode < 5) {
>>> +            cbh_mode = 1;
>>> +            cbv_mode = 1;
>>> +            tp_mode  = mode + 3;
>>> +        } else {
>>> +            cbh_mode = 0;
>>> +            cbv_mode = 0;
>>> +            tp_mode  = mode - 4 + 3;
>>> +        }
>>> +        din_sw   = 0x01;
>>> +        r1420    = 0xE0;
>>> +    }
>>> +
>>> +    rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode
>>> << 4);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +    rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121,
>>> cbh_mode << 7);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +    rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124,
>>> cbv_mode << 7);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +    rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +    rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +out:
>>> +    return rval;
>>> +}
>>> +
>>> +/*
>>> -----------------------------------------------------------------------------
>>>
>>> + * V4L2 controls
>>> + */
>>> +
>>> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
>>> +{
>>> +    struct et8ek8_sensor *sensor =
>>> +        container_of(ctrl->handler, struct et8ek8_sensor,
>>> ctrl_handler);
>>> +    const struct smia_mode *mode = &sensor->current_reglist->mode;
>>> +
>>> +    switch (ctrl->id) {
>>> +    case V4L2_CID_MODE_FRAME_WIDTH:
>>> +        ctrl->cur.val = mode->width;
>>> +        break;
>>> +    case V4L2_CID_MODE_FRAME_HEIGHT:
>>> +        ctrl->cur.val = mode->height;
>>> +        break;
>>> +    case V4L2_CID_MODE_VISIBLE_WIDTH:
>>> +        ctrl->cur.val = mode->window_width;
>>> +        break;
>>> +    case V4L2_CID_MODE_VISIBLE_HEIGHT:
>>> +        ctrl->cur.val = mode->window_height;
>>> +        break;
>>> +    case V4L2_CID_MODE_PIXELCLOCK:
>>> +        ctrl->cur.val = mode->pixel_clock;
>>
>> Please use V4L2_CID_PIXEL_RATE instead. It's a 64-bit control.
>>
>
> Already done, see
>>> +        break;
>>> +    case V4L2_CID_MODE_SENSITIVITY:
>>> +        ctrl->cur.val = mode->sensitivity;
>>> +        break;
>>> +    case V4L2_CID_MODE_OPSYSCLOCK:
>>> +        ctrl->cur.val = mode->opsys_clock;
>>> +        break;
>>
>> V4L2_CID_LINK_FREQ.
>>
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
>>> +{
>>> +    struct et8ek8_sensor *sensor =
>>> +        container_of(ctrl->handler, struct et8ek8_sensor,
>>> ctrl_handler);
>>> +    struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>>> +    int uninitialized_var(rows);
>>> +
>>> +    if (ctrl->id == V4L2_CID_EXPOSURE)
>>> +        rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
>>> +
>>> +    switch (ctrl->id) {
>>> +    case V4L2_CID_GAIN:
>>> +        return et8ek8_set_gain(sensor, ctrl->val);
>>> +
>>> +    case V4L2_CID_EXPOSURE:
>>> +        return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
>>> +                      swab16(rows));
>>> +
>>> +    case V4L2_CID_TEST_PATTERN:
>>> +        return et8ek8_set_test_pattern(sensor, ctrl->val);
>>> +
>>> +    case V4L2_CID_PIXEL_RATE:
>>> +        /* For v4l2_ctrl_s_ctrl_int64() used internally. */
>>> +        return 0;
>>> +
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +}
>>> +
>>> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
>>> +    .g_volatile_ctrl = et8ek8_get_ctrl,
>>> +    .s_ctrl = et8ek8_set_ctrl,
>>> +};
>>> +
>>> +static const char *et8ek8_test_pattern_menu[] = {
>>> +    "Normal",
>>> +    "Vertical colorbar",
>>> +    "Horizontal colorbar",
>>> +    "Scale",
>>> +    "Ramp",
>>> +    "Small vertical colorbar",
>>> +    "Small horizontal colorbar",
>>> +    "Small scale",
>>> +    "Small ramp",
>>> +};
>>> +
>>> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_TEST_PATTERN,
>>> +        .type        = V4L2_CTRL_TYPE_MENU,
>>> +        .name        = "Test pattern mode",
>>> +        .min        = 0,
>>> +        .max        = ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
>>> +        .step        = 0,
>>> +        .def        = 0,
>>> +        .flags        = 0,
>>> +        .qmenu        = et8ek8_test_pattern_menu,
>>> +    },
>>> +    {
>>> +        .id        = V4L2_CID_MODE_CLASS,
>>> +        .type        = V4L2_CTRL_TYPE_CTRL_CLASS,
>>> +        .name        = "SMIA-type sensor information",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                | V4L2_CTRL_FLAG_WRITE_ONLY,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_FRAME_WIDTH,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Frame width",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_FRAME_HEIGHT,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Frame height",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_VISIBLE_WIDTH,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Visible width",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_VISIBLE_HEIGHT,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Visible height",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_PIXELCLOCK,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Pixel clock [Hz]",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_SENSITIVITY,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Sensivity",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>> +    },
>>> +    {
>>> +        .ops        = &et8ek8_ctrl_ops,
>>> +        .id        = V4L2_CID_MODE_OPSYSCLOCK,
>>> +        .type        = V4L2_CTRL_TYPE_INTEGER,
>>> +        .name        = "Output pixel clock [Hz]",
>>> +        .min        = 0,
>>> +        .max        = 0,
>>> +        .step        = 1,
>>> +        .def        = 0,
>>> +        .flags        = V4L2_CTRL_FLAG_READ_ONLY
>>> +                  | V4L2_CTRL_FLAG_VOLATILE,
>>
>> Many of the above controls are or standard controls should be used,
>> please
>> use the native V4L2 control framework functions to create the controls
>> instead of the custom one. You get control names for free, for instance.
>>
>>> +    },
>>> +};
>>> +
>>> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
>>> +{
>>> +    unsigned int i;
>>> +    u32 min, max;
>>> +
>>> +    v4l2_ctrl_handler_init(&sensor->ctrl_handler,
>>> +                   ARRAY_SIZE(et8ek8_ctrls) + 2);
>>> +
>>> +    /* V4L2_CID_GAIN */
>>> +    v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>>> +              V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
>>> +              1, 0);
>>> +
>>> +    /* V4L2_CID_EXPOSURE */
>>> +    min = et8ek8_exposure_rows_to_us(sensor, 1);
>>> +    max = et8ek8_exposure_rows_to_us(sensor,
>>> +                sensor->current_reglist->mode.max_exp);
>>> +    sensor->exposure =
>>> +        v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>>> +                  V4L2_CID_EXPOSURE, min, max, min, max);
>>> +    sensor->pixel_rate =
>>> +        v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>>> +        V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
>>> +
>>> +    /* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
>>> +    for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
>>> +        v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
>>> +                     NULL);
>>> +
>>> +    if (sensor->ctrl_handler.error)
>>> +        return sensor->ctrl_handler.error;
>>> +
>>> +    sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
>>> +    return 0;
>>> +}
>>> +
>>> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
>>> +{
>>> +    struct v4l2_ctrl *ctrl = sensor->exposure;
>>> +    u32 min, max;
>>> +
>>> +    min = et8ek8_exposure_rows_to_us(sensor, 1);
>>> +    max = et8ek8_exposure_rows_to_us(sensor,
>>> +                     sensor->current_reglist->mode.max_exp);
>>> +
>>> +    v4l2_ctrl_lock(ctrl);
>>> +    ctrl->minimum = min;
>>> +    ctrl->maximum = max;
>>> +    ctrl->step = min;
>>> +    ctrl->default_value = max;
>>> +    ctrl->val = max;
>>> +    ctrl->cur.val = max;
>>> +    __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
>>> +                 sensor->current_reglist->mode.ext_clock);
>>> +    v4l2_ctrl_unlock(ctrl);
>>> +}
>>> +
>>> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
>>> +{
>>> +    struct v4l2_subdev *subdev = &sensor->subdev;
>>> +    struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>> +    int rval;
>>> +
>>> +    rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
>>> +    if (rval)
>>> +        goto fail;
>>> +
>>> +    /* Controls set while the power to the sensor is turned off are
>>> saved
>>> +     * but not applied to the hardware. Now that we're about to start
>>> +     * streaming apply all the current values to the hardware.
>>> +     */
>>> +    rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
>>> +    if (rval)
>>> +        goto fail;
>>> +
>>> +    return 0;
>>> +
>>> +fail:
>>> +    dev_err(&client->dev, "sensor configuration failed\n");
>>> +    return rval;
>>> +}
>>> +
>>> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
>>> +{
>>> +    struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>>> +
>>> +    return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
>>> +}
>>> +
>>> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
>>> +{
>>> +    struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>>> +
>>> +    return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
>>> +}
>>> +
>>> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    int ret;
>>> +
>>> +    if (!streaming)
>>> +        return et8ek8_stream_off(sensor);
>>> +
>>> +    ret = et8ek8_configure(sensor);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return et8ek8_stream_on(sensor);
>>> +}
>>> +
>>> +/*
>>> --------------------------------------------------------------------------
>>>
>>> + * V4L2 subdev operations
>>> + */
>>> +
>>> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
>>> +{
>>> +    int rval;
>>> +
>>> +    gpiod_set_value(sensor->reset, 0);
>>> +    udelay(1);
>>> +
>>> +    clk_disable_unprepare(sensor->ext_clk);
>>> +
>>> +    rval = regulator_disable(sensor->vana);
>>> +    return rval;
>>> +}
>>> +
>>> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
>>> +{
>>> +    struct v4l2_subdev *subdev = &sensor->subdev;
>>> +    struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>> +    unsigned int hz = ET8EK8_XCLK_HZ;
>>> +    int val, rval;
>>> +
>>> +    rval = regulator_enable(sensor->vana);
>>> +    if (rval) {
>>> +        dev_err(&client->dev, "failed to enable vana regulator\n");
>>> +        return rval;
>>> +    }
>>> +
>>> +    if (sensor->current_reglist)
>>> +        hz = sensor->current_reglist->mode.ext_clock;
>>> +
>>> +    rval = clk_set_rate(sensor->ext_clk, hz);
>>> +    if (rval < 0) {
>>> +        dev_err(&client->dev,
>>> +            "unable to set extclk clock freq to %u\n", hz);
>>> +        goto out;
>>> +    }
>>> +    rval = clk_prepare_enable(sensor->ext_clk);
>>> +    if (rval < 0) {
>>> +        dev_err(&client->dev, "failed to enable extclk\n");
>>> +        goto out;
>>> +    }
>>> +
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +    udelay(10);            /* I wish this is a good value */
>>> +
>>> +    gpiod_set_value(sensor->reset, 1);
>>> +
>>> +    msleep(5000*1000/hz+1);                /* Wait 5000 cycles */
>>> +
>>> +    rval = smia_i2c_reglist_find_write(client,
>>> +                       &et8ek8_smia_meta_reglist,
>>> +                       SMIA_REGLIST_POWERON);
>>> +    if (rval)
>>> +        goto out;
>>> +
>>> +#ifdef USE_CRC
>>> +    rval = smia_i2c_read_reg(client,
>>> +                 SMIA_REG_8BIT, 0x1263, &val);
>>> +    if (rval)
>>> +        goto out;
>>> +#if USE_CRC
>>> +    val |= (1<<4);
>>> +#else
>>> +    val &= ~(1<<4);
>>> +#endif
>>> +    rval = smia_i2c_write_reg(client,
>>> +                  SMIA_REG_8BIT, 0x1263, val);
>>> +    if (rval)
>>> +        goto out;
>>> +#endif
>>> +
>>> +out:
>>> +    if (rval)
>>> +        et8ek8_power_off(sensor);
>>> +
>>> +    return rval;
>>> +}
>>> +
>>> +/*
>>> --------------------------------------------------------------------------
>>>
>>> + * V4L2 subdev video operations
>>> + */
>>> +
>>> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
>>> +                 struct v4l2_subdev_pad_config *cfg,
>>> +                 struct v4l2_subdev_mbus_code_enum *code)
>>> +{
>>> +    return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist,
>>> code);
>>> +}
>>> +
>>> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
>>> +                  struct v4l2_subdev_pad_config *cfg,
>>> +                  struct v4l2_subdev_frame_size_enum *fse)
>>> +{
>>> +    return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist,
>>> fse);
>>> +}
>>> +
>>> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
>>> +                  struct v4l2_subdev_pad_config *cfg,
>>> +                  struct v4l2_subdev_frame_interval_enum *fie)
>>> +{
>>> +    return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist,
>>> fie);
>>> +}
>>> +
>>> +static struct v4l2_mbus_framefmt *
>>> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
>>> +            struct v4l2_subdev_pad_config *cfg,
>>> +            unsigned int pad, enum v4l2_subdev_format_whence which)
>>> +{
>>> +    switch (which) {
>>> +    case V4L2_SUBDEV_FORMAT_TRY:
>>> +        return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
>>> +    case V4L2_SUBDEV_FORMAT_ACTIVE:
>>> +        return &sensor->format;
>>> +    default:
>>> +        return NULL;
>>> +    }
>>> +}
>>> +
>>> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
>>> +                 struct v4l2_subdev_pad_config *cfg,
>>> +                 struct v4l2_subdev_format *fmt)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct v4l2_mbus_framefmt *format;
>>> +
>>> +    format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad,
>>> fmt->which);
>>> +    if (format == NULL)
>>> +        return -EINVAL;
>>> +
>>> +    fmt->format = *format;
>>> +    return 0;
>>> +}
>>> +
>>> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
>>> +                 struct v4l2_subdev_pad_config *cfg,
>>> +                 struct v4l2_subdev_format *fmt)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct v4l2_mbus_framefmt *format;
>>> +        struct smia_reglist *reglist;
>>> +
>>> +    format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad,
>>> fmt->which);
>>> +    if (format == NULL)
>>> +        return -EINVAL;
>>> +
>>> +    reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
>>> +                         &fmt->format);
>>> +    smia_reglist_to_mbus(reglist, &fmt->format);
>>> +    *format = fmt->format;
>>> +
>>> +    if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>>> +        sensor->current_reglist = reglist;
>>> +        et8ek8_update_controls(sensor);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
>>> +                     struct v4l2_subdev_frame_interval *fi)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +
>>> +    memset(fi, 0, sizeof(*fi));
>>> +    fi->interval = sensor->current_reglist->mode.timeperframe;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
>>> +                     struct v4l2_subdev_frame_interval *fi)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct smia_reglist *reglist;
>>> +
>>> +    reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
>>> +                          sensor->current_reglist,
>>> +                          &fi->interval);
>>> +
>>> +    if (!reglist)
>>> +        return -EINVAL;
>>> +
>>> +    if (sensor->current_reglist->mode.ext_clock !=
>>> reglist->mode.ext_clock)
>>> +        return -EINVAL;
>>> +
>>> +    sensor->current_reglist = reglist;
>>> +    et8ek8_update_controls(sensor);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>> +    unsigned int length = ET8EK8_PRIV_MEM_SIZE;
>>> +    unsigned int offset = 0;
>>> +    u8 *ptr  = sensor->priv_mem;
>>> +    int rval = 0;
>>> +
>>> +    /* Read the EEPROM window-by-window, each window 8 bytes */
>>> +    do {
>>> +        u8 buffer[PRIV_MEM_WIN_SIZE];
>>> +        struct i2c_msg msg;
>>> +        int bytes, i;
>>> +        int ofs;
>>> +
>>> +        /* Set the current window */
>>> +        rval = smia_i2c_write_reg(client,
>>> +                      SMIA_REG_8BIT,
>>> +                      0x0001,
>>> +                      0xe0 | (offset >> 3));
>>> +        if (rval < 0)
>>> +            goto out;
>>> +
>>> +        /* Wait for status bit */
>>> +        for (i = 0; i < 1000; ++i) {
>>> +            u32 status;
>>> +            rval = smia_i2c_read_reg(client,
>>> +                         SMIA_REG_8BIT,
>>> +                         0x0003,
>>> +                         &status);
>>> +            if (rval < 0)
>>> +                goto out;
>>> +            if ((status & 0x08) == 0)
>>> +                break;
>>> +            msleep(1);
>>> +        };
>>> +
>>> +        if (i == 1000) {
>>> +            rval = -EIO;
>>> +            goto out;
>>> +        }
>>> +
>>> +        /* Read window, 8 bytes at once, and copy to user space */
>>> +        ofs = offset & 0x07;    /* Offset within this window */
>>> +        bytes = length + ofs > 8 ? 8-ofs : length;
>>> +        msg.addr = client->addr;
>>> +        msg.flags = 0;
>>> +        msg.len = 2;
>>> +        msg.buf = buffer;
>>> +        ofs += PRIV_MEM_START_REG;
>>> +        buffer[0] = (u8)(ofs >> 8);
>>> +        buffer[1] = (u8)(ofs & 0xFF);
>>> +        rval = i2c_transfer(client->adapter, &msg, 1);
>>> +        if (rval < 0)
>>> +            goto out;
>>> +        mdelay(ET8EK8_I2C_DELAY);
>>> +        msg.addr = client->addr;
>>> +        msg.len = bytes;
>>> +        msg.flags = I2C_M_RD;
>>> +        msg.buf = buffer;
>>> +        memset(buffer, 0, sizeof(buffer));
>>> +        rval = i2c_transfer(client->adapter, &msg, 1);
>>> +        if (rval < 0)
>>> +            goto out;
>>> +        rval = 0;
>>> +        memcpy(ptr, buffer, bytes);
>>> +
>>> +        length -= bytes;
>>> +        offset += bytes;
>>> +        ptr    += bytes;
>>> +    } while (length > 0);
>>> +
>>> +out:
>>> +    return rval;
>>> +}
>>> +
>>> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>> +    int rval, rev_l, rev_h;
>>> +
>>> +    rval = et8ek8_power_on(sensor);
>>> +    if (rval) {
>>> +        dev_err(&client->dev, "could not power on\n");
>>> +        return rval;
>>> +    }
>>> +
>>> +    if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
>>> +                  REG_REVISION_NUMBER_L, &rev_l) != 0
>>> +        || smia_i2c_read_reg(client, SMIA_REG_8BIT,
>>> +                 REG_REVISION_NUMBER_H, &rev_h) != 0) {
>>> +        dev_err(&client->dev,
>>> +            "no et8ek8 sensor detected\n");
>>> +        rval = -ENODEV;
>>> +        goto out_poweroff;
>>> +    }
>>> +    sensor->version = (rev_h << 8) + rev_l;
>>> +    if (sensor->version != ET8EK8_REV_1
>>> +        && sensor->version != ET8EK8_REV_2)
>>> +        dev_info(&client->dev,
>>> +             "unknown version 0x%x detected, "
>>> +             "continuing anyway\n", sensor->version);
>>> +
>>> +    rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
>>> +    if (rval) {
>>> +        dev_err(&client->dev,
>>> +            "invalid register list %s, import failed\n",
>>> +            ET8EK8_NAME);
>>> +        goto out_poweroff;
>>> +    }
>>> +
>>> +    sensor->current_reglist =
>>> +        smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>>> +                       SMIA_REGLIST_MODE);
>>> +    if (!sensor->current_reglist) {
>>> +        dev_err(&client->dev,
>>> +            "invalid register list %s, no mode found\n",
>>> +            ET8EK8_NAME);
>>> +        rval = -ENODEV;
>>> +        goto out_poweroff;
>>> +    }
>>> +
>>> +    smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
>>> +
>>> +    rval = smia_i2c_reglist_find_write(client,
>>> +                       &et8ek8_smia_meta_reglist,
>>> +                       SMIA_REGLIST_POWERON);
>>> +    if (rval) {
>>> +        dev_err(&client->dev,
>>> +            "invalid register list %s, no POWERON mode found\n",
>>> +            ET8EK8_NAME);
>>> +        goto out_poweroff;
>>> +    }
>>> +    rval = et8ek8_stream_on(sensor);    /* Needed to be able to read
>>> EEPROM */
>>> +    if (rval)
>>> +        goto out_poweroff;
>>> +    rval = et8ek8_g_priv_mem(subdev);
>>> +    if (rval)
>>> +        dev_warn(&client->dev,
>>> +            "can not read OTP (EEPROM) memory from sensor\n");
>>> +    rval = et8ek8_stream_off(sensor);
>>> +    if (rval)
>>> +        goto out_poweroff;
>>> +
>>> +    rval = et8ek8_power_off(sensor);
>>> +    if (rval)
>>> +        goto out_poweroff;
>>> +
>>> +    return 0;
>>> +
>>> +out_poweroff:
>>> +    et8ek8_power_off(sensor);
>>> +
>>> +    return rval;
>>> +}
>>> +
>>> +/*
>>> --------------------------------------------------------------------------
>>>
>>> + * sysfs attributes
>>> + */
>>> +static ssize_t
>>> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
>>> +             char *buf)
>>> +{
>>> +    struct v4l2_subdev *subdev =
>>> i2c_get_clientdata(to_i2c_client(dev));
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +
>>> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
>>> +#error PAGE_SIZE too small!
>>> +#endif
>>> +
>>> +    memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
>>> +
>>> +    return ET8EK8_PRIV_MEM_SIZE;
>>> +}
>>> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
>>> +
>>> +/*
>>> --------------------------------------------------------------------------
>>>
>>> + * V4L2 subdev core operations
>>> + */
>>> +
>>> +static int
>>> +et8ek8_registered(struct v4l2_subdev *subdev)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>> +    struct v4l2_mbus_framefmt *format;
>>> +    int rval;
>>> +
>>> +    dev_dbg(&client->dev, "registered!");
>>> +
>>> +    if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
>>> +        dev_err(&client->dev, "could not register sysfs entry\n");
>>> +        return -EBUSY;
>>> +    }
>>> +
>>> +    rval = et8ek8_dev_init(subdev);
>>> +    if (rval)
>>> +        return rval;
>>> +
>>> +    rval = et8ek8_init_controls(sensor);
>>> +    if (rval) {
>>> +        dev_err(&client->dev, "controls initialization failed\n");
>>> +        return rval;
>>> +    }
>>> +
>>> +    format = __et8ek8_get_pad_format(sensor, NULL, 0,
>>> +                     V4L2_SUBDEV_FORMAT_ACTIVE);
>>> +    return 0;
>>> +}
>>> +
>>> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
>>> +{
>>> +    return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
>>> +}
>>> +
>>> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +    int ret = 0;
>>> +
>>> +    mutex_lock(&sensor->power_lock);
>>> +
>>> +    /* If the power count is modified from 0 to != 0 or from != 0 to 0,
>>> +     * update the power state.
>>> +     */
>>> +    if (sensor->power_count == !on) {
>>> +        ret = __et8ek8_set_power(sensor, !!on);
>>> +        if (ret < 0)
>>> +            goto done;
>>> +    }
>>> +
>>> +    /* Update the power count. */
>>> +    sensor->power_count += on ? 1 : -1;
>>> +    WARN_ON(sensor->power_count < 0);
>>> +
>>> +done:
>>> +    mutex_unlock(&sensor->power_lock);
>>> +    return ret;
>>> +}
>>> +
>>> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh
>>> *fh)
>>> +{
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
>>> +    struct v4l2_mbus_framefmt *format;
>>> +    struct smia_reglist *reglist;
>>> +
>>> +    reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>>> +                     SMIA_REGLIST_MODE);
>>> +    format = __et8ek8_get_pad_format(sensor, fh->pad, 0,
>>> V4L2_SUBDEV_FORMAT_TRY);
>>> +    smia_reglist_to_mbus(reglist, format);
>>> +
>>> +    return et8ek8_set_power(sd, true);
>>> +}
>>> +
>>> +static int et8ek8_close(struct v4l2_subdev *sd, struct
>>> v4l2_subdev_fh *fh)
>>> +{
>>> +    return et8ek8_set_power(sd, false);
>>> +}
>>> +
>>> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
>>> +    .s_stream = et8ek8_s_stream,
>>> +    .g_frame_interval = et8ek8_get_frame_interval,
>>> +    .s_frame_interval = et8ek8_set_frame_interval,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
>>> +    .s_power = et8ek8_set_power,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
>>> +    .enum_mbus_code = et8ek8_enum_mbus_code,
>>> +        .enum_frame_size = et8ek8_enum_frame_size,
>>> +        .enum_frame_interval = et8ek8_enum_frame_ival,
>>> +    .get_fmt = et8ek8_get_pad_format,
>>> +    .set_fmt = et8ek8_set_pad_format,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops et8ek8_ops = {
>>> +    .core = &et8ek8_core_ops,
>>> +    .video = &et8ek8_video_ops,
>>> +    .pad = &et8ek8_pad_ops,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
>>> +    .registered = et8ek8_registered,
>>> +    .open = et8ek8_open,
>>> +    .close = et8ek8_close,
>>> +};
>>> +
>>> +/*
>>> --------------------------------------------------------------------------
>>>
>>> + * I2C driver
>>> + */
>>> +#ifdef CONFIG_PM
>>> +
>>> +static int et8ek8_suspend(struct device *dev)
>>> +{
>>> +    struct i2c_client *client = to_i2c_client(dev);
>>> +    struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +
>>> +    if (!sensor->power_count)
>>> +        return 0;
>>> +
>>> +    return __et8ek8_set_power(sensor, false);
>>> +}
>>> +
>>> +static int et8ek8_resume(struct device *dev)
>>> +{
>>> +    struct i2c_client *client = to_i2c_client(dev);
>>> +    struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +
>>> +    if (!sensor->power_count)
>>> +        return 0;
>>> +
>>> +    return __et8ek8_set_power(sensor, true);
>>> +}
>>> +
>>> +static struct dev_pm_ops et8ek8_pm_ops = {
>>> +    .suspend    = et8ek8_suspend,
>>> +    .resume        = et8ek8_resume,
>>> +};
>>> +
>>> +#else
>>> +
>>> +#define et8ek8_pm_ops    NULL
>>> +
>>> +#endif /* CONFIG_PM */
>>> +
>>> +static int et8ek8_probe(struct i2c_client *client,
>>> +            const struct i2c_device_id *devid)
>>> +{
>>> +    struct et8ek8_sensor *sensor;
>>> +    int ret;
>>> +
>>> +    sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
>>> +    if (!sensor)
>>> +        return -ENOMEM;
>>> +
>>> +    sensor->reset = devm_gpiod_get(&client->dev, "reset",
>>> GPIOD_OUT_LOW);
>>> +    if (IS_ERR(sensor->reset)) {
>>> +        dev_dbg(&client->dev, "could not request reset gpio\n");
>>> +        return PTR_ERR(sensor->reset);;
>>> +    }
>>> +
>>> +    sensor->vana = devm_regulator_get(&client->dev, "vana");
>>> +    if (IS_ERR(sensor->vana)) {
>>> +        dev_err(&client->dev, "could not get regulator for vana\n");
>>> +        return PTR_ERR(sensor->vana);
>>> +    }
>>> +
>>> +    sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
>>> +    if (IS_ERR(sensor->ext_clk)) {
>>> +        dev_err(&client->dev, "could not get clock\n");
>>> +        return PTR_ERR(sensor->ext_clk);
>>> +    }
>>> +
>>> +    mutex_init(&sensor->power_lock);
>>> +
>>> +    v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
>>> +    sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> +    sensor->subdev.internal_ops = &et8ek8_internal_ops;
>>> +
>>> +    sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
>>> +    ret = media_entity_pads_init(&sensor->subdev.entity, 1,
>>> &sensor->pad);
>>> +    if (ret < 0) {
>>> +        dev_err(&client->dev, "media entity init failed!\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = v4l2_async_register_subdev(&sensor->subdev);
>>> +    if (ret < 0) {
>>> +        media_entity_cleanup(&sensor->subdev.entity);
>>> +        return ret;
>>> +    }
>>> +
>>> +    dev_dbg(&client->dev, "initialized!\n");
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int __exit et8ek8_remove(struct i2c_client *client)
>>> +{
>>> +    struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>>> +    struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>>> +
>>> +    if (sensor->power_count) {
>>> +        gpiod_set_value(sensor->reset, 0);
>>> +        clk_disable_unprepare(sensor->ext_clk);
>>> +        sensor->power_count = 0;
>>> +    }
>>> +
>>> +    v4l2_device_unregister_subdev(&sensor->subdev);
>>> +    device_remove_file(&client->dev, &dev_attr_priv_mem);
>>> +    v4l2_ctrl_handler_free(&sensor->ctrl_handler);
>>> +    media_entity_cleanup(&sensor->subdev.entity);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct of_device_id et8ek8_of_table[] = {
>>> +    { .compatible = "toshiba,et8ek8" },
>>> +    { },
>>> +};
>>> +
>>> +static const struct i2c_device_id et8ek8_id_table[] = {
>>> +    { ET8EK8_NAME, 0 },
>>> +    { }
>>> +};
>>> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
>>> +
>>> +static struct i2c_driver et8ek8_i2c_driver = {
>>> +    .driver        = {
>>> +        .name    = ET8EK8_NAME,
>>> +        .pm    = &et8ek8_pm_ops,
>>> +        .of_match_table    = et8ek8_of_table,
>>> +    },
>>> +    .probe        = et8ek8_probe,
>>> +    .remove        = __exit_p(et8ek8_remove),
>>> +    .id_table    = et8ek8_id_table,
>>> +};
>>> +
>>> +module_i2c_driver(et8ek8_i2c_driver);
>>> +
>>> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
>>
>> s/nokia.com/iki.fi/ please.
>>
>>> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
>>> +MODULE_LICENSE("GPL");
>>

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

* Re: [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-05-01 10:44     ` Sakari Ailus
  2016-05-01 12:31       ` Ivaylo Dimitrov
@ 2016-05-01 12:50       ` Ivaylo Dimitrov
  2016-05-01 13:41         ` Sakari Ailus
  1 sibling, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-01 12:50 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Hi,

On  1.05.2016 13:44, Sakari Ailus wrote:
> Hi Ivaylo,
>
> On Mon, Apr 25, 2016 at 12:08:03AM +0300, Ivaylo Dimitrov wrote:
>> add driver
>>
>> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
>> ---
>>   drivers/media/i2c/smia/Kconfig  |    8 +
>>   drivers/media/i2c/smia/Makefile |    1 +
>>   drivers/media/i2c/smia/et8ek8.c | 1788 +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 1797 insertions(+)
>>   create mode 100644 drivers/media/i2c/smia/et8ek8.c
>>
>> diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
>> index d9be497..13ca043 100644
>> --- a/drivers/media/i2c/smia/Kconfig
>> +++ b/drivers/media/i2c/smia/Kconfig
>> @@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
>>
>>   	  Also a few helper functions are provided to work with binary
>>   	  register lists.
>> +
>> +config VIDEO_ET8EK8
>> +	tristate "ET8EK8 camera sensor support"
>> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	select VIDEO_SMIAREGS
>> +	---help---
>> +	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
>> +	  It is used for example in Nokia N900 (RX-51).
>> diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
>> index cff67bc..56cf15e 100644
>> --- a/drivers/media/i2c/smia/Makefile
>> +++ b/drivers/media/i2c/smia/Makefile
>> @@ -1 +1,2 @@
>>   obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
>> +obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
>> diff --git a/drivers/media/i2c/smia/et8ek8.c b/drivers/media/i2c/smia/et8ek8.c
>> new file mode 100644
>> index 0000000..46c112d
>> --- /dev/null
>> +++ b/drivers/media/i2c/smia/et8ek8.c
>> @@ -0,0 +1,1788 @@
>> +/*
>> + * drivers/media/video/et8ek8.c
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@nokia.com>
>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>> + *
>> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
>> + *
>> + * This driver is based on the Micron MT9T012 camera imager driver
>> + * (C) Texas Instruments.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#define DEBUG
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <linux/version.h>
>> +#include <linux/v4l2-mediabus.h>
>> +
>> +#include <media/media-entity.h>
>> +#include <media/smiaregs.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define ET8EK8_NAME		"et8ek8"
>> +#define ET8EK8_XCLK_HZ		9600000
>> +#define ET8EK8_PRIV_MEM_SIZE	128
>> +
>> +#define CTRL_GAIN		0
>> +#define CTRL_EXPOSURE		1
>> +#define CTRL_TEST_PATTERN	2
>> +
>> +#define CID_TO_CTRL(id)		((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
>> +				 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
>> +				 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
>> +				 -EINVAL)
>> +
>> +struct et8ek8_sensor {
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pad;
>> +	struct v4l2_mbus_framefmt format;
>> +	struct gpio_desc *reset;
>> +	struct regulator *vana;
>> +	struct clk *ext_clk;
>> +
>> +	u16 version;
>> +
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +	struct v4l2_ctrl *exposure;
>> +	struct v4l2_ctrl *pixel_rate;
>> +	struct smia_reglist *current_reglist;
>> +
>> +	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
>> +
>> +	struct mutex power_lock;
>> +	int power_count;
>> +};
>> +
>> +#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
>> +
>> +enum et8ek8_versions {
>> +	ET8EK8_REV_1 = 0x0001,
>> +	ET8EK8_REV_2,
>> +};
>> +
>> +/*
>> + * This table describes what should be written to the sensor register
>> + * for each gain value. The gain(index in the table) is in terms of
>> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
>> + * the *analog gain, [1] in the digital gain
>> + *
>> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
>> + */
>> +static struct et8ek8_gain {
>> +	u16 analog;
>> +	u16 digital;
>> +} const et8ek8_gain_table[] = {
>> +	{ 32,    0},  /* x1 */
>> +	{ 34,    0},
>> +	{ 37,    0},
>> +	{ 39,    0},
>> +	{ 42,    0},
>> +	{ 45,    0},
>> +	{ 49,    0},
>> +	{ 52,    0},
>> +	{ 56,    0},
>> +	{ 60,    0},
>> +	{ 64,    0},  /* x2 */
>> +	{ 69,    0},
>> +	{ 74,    0},
>> +	{ 79,    0},
>> +	{ 84,    0},
>> +	{ 91,    0},
>> +	{ 97,    0},
>> +	{104,    0},
>> +	{111,    0},
>> +	{119,    0},
>> +	{128,    0},  /* x4 */
>> +	{137,    0},
>> +	{147,    0},
>> +	{158,    0},
>> +	{169,    0},
>> +	{181,    0},
>> +	{194,    0},
>> +	{208,    0},
>> +	{223,    0},
>> +	{239,    0},
>> +	{256,    0},  /* x8 */
>> +	{256,   73},
>> +	{256,  152},
>> +	{256,  236},
>> +	{256,  327},
>> +	{256,  424},
>> +	{256,  528},
>> +	{256,  639},
>> +	{256,  758},
>> +	{256,  886},
>> +	{256, 1023},  /* x16 */
>> +};
>> +
>> +/* Register definitions */
>> +#define REG_REVISION_NUMBER_L	0x1200
>> +#define REG_REVISION_NUMBER_H	0x1201
>> +
>> +#define PRIV_MEM_START_REG	0x0008
>> +#define PRIV_MEM_WIN_SIZE	8
>> +
>> +#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
>> +
>> +#define USE_CRC			1
>> +
>> +/*
>> + *
>> + * Stingray sensor mode settings for Scooby
>> + *
>> + *
>> + */
>> +
>
> It'd be nice to get rid of the register lists, however considering where the
> sensor is used it's unlikely going to find its way elsewhere, so the gain
> might not be worth the effort.
>
> I'd still integrate the functionality in the smia register list library to
> the driver. That sort of functionality ideally should not be needed at all
> but sadly, the documentation of some sensors is too bad to write proper
> drivers. :-(
>
> (SMIA is an ill-conceived name for this library btw., it's got nothing to do
> with SMIA as such. I'd call it et8ek8_reglist for example. Perhaps the
> library only would be a better choice.)
>

ok.  I removed the custom controls found in smia_reglist library and it 
doesn't affect the functionality of the driver (at least I noticed no 
chagne), so maybe it is a good idea to remove the library and move the 
code to the driver. there are only a couple of functions remaining there 
after controls are removed.

>> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
>> +static struct smia_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {	/* 1 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_POWERON,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1207
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x126C, 0xCC },	/* Need to set firstly */
>> +		{ SMIA_REG_8BIT, 0x1269, 0x00 },	/* Strobe and Data of CCP2 delay are minimized. */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },	/* Refined value of Min H_COUNT  */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },	/* Frequency of SPCK setting (SPCK=MRCK) */
>> +		{ SMIA_REG_8BIT, 0x1241, 0x94 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1242, 0x02 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x124B, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1255, 0xFF },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1256, 0x9F },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1258, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* From parallel out to serial out */
>> +		{ SMIA_REG_8BIT, 0x125E, 0xC0 },	/* From w/ embeded data to w/o embeded data */
>> +		{ SMIA_REG_8BIT, 0x1263, 0x98 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1268, 0xC6 },	/* CCP2 out is from STOP to ACTIVE */
>> +		{ SMIA_REG_8BIT, 0x1434, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1163, 0x44 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1166, 0x29 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1140, 0x02 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1011, 0x24 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1151, 0x80 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1152, 0x23 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1014, 0x05 },	/* Initial setting( for improvement2 of lower frequency noise ) */
>> +		{ SMIA_REG_8BIT, 0x1033, 0x06 },
>> +		{ SMIA_REG_8BIT, 0x1034, 0x79 },
>> +		{ SMIA_REG_8BIT, 0x1423, 0x3F },
>> +		{ SMIA_REG_8BIT, 0x1424, 0x3F },
>> +		{ SMIA_REG_8BIT, 0x1426, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1439, 0x00 },	/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x161F, 0x60 },	/* Switch of blemish correction (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x1634, 0x00 },	/* Switch of auto noise correction (0d:disable / 1d:enable) */
>> +		{ SMIA_REG_8BIT, 0x1646, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1648, 0x00 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x113E, 0x01 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x113F, 0x22 },	/* Initial setting */
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
>> +static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {	/* 2 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 560 MHz
>> + * VCO        = 560 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 128 (3072)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 175
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 6
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3072,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1292
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x57 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x06 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x80 },	/* <-changed to v14 7E->80 */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
>> +static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {	/* 3 */
>> +/* (without the +1)
>> + * SPCK       = 96.5333333333333 MHz
>> + * CCP2       = 579.2 MHz
>> + * VCO        = 579.2 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 181
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1008,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 96533333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 3000
>> +		},
>> +		.max_exp = 1004,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x5A },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode4_SVGA_864x656_29.88fps */
>> +static struct smia_reglist mode4_svga_864x656_29_88fps = {	/* 4 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 166 (3984)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3984,
>> +		.height = 672,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 864,
>> +		.window_height = 656,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2988
>> +		},
>> +		.max_exp = 668,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x62 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x62 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xA6 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode5_VGA_648x492_29.93fps */
>> +static struct smia_reglist mode5_vga_648x492_29_93fps = {	/* 5 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2993
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode2_16VGA_2592x1968_3.99fps */
>> +static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {	/* 6 */
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 254 (6096)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 6096,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 399
>> +		},
>> +		.max_exp = 6092,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x07 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0xFE },
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_648x492_5fps */
>> +static struct smia_reglist mode_648x492_5fps = {	/* 7 */
>> +/* (without the +1)
>> + * SPCK       = 13.3333333333333 MHz
>> + * CCP2       = 53.3333333333333 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 5
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 13333333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 499
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x64 },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x71 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x57 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x61 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0xDD },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0x54 },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_5fps */
>> +static struct smia_reglist mode3_4vga_1296x984_5fps = {	/* 8 */
>> +/* (without the +1)
>> + * SPCK       = 49.4 MHz
>> + * CCP2       = 395.2 MHz
>> + * VCO        = 790.4 MHz
>> + * VCOUNT     = 250 (6000)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 247
>> + * VCO_DIV    = 1
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 3000,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 49400000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 501
>> +		},
>> +		.max_exp = 2996,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x7B },
>> +		{ SMIA_REG_8BIT, 0x1238, 0x82 },
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },
>> +		{ SMIA_REG_8BIT, 0x123A, 0x17 },
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1220, 0x89 },
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },
>> +		{ SMIA_REG_8BIT, 0x1222, 0xFA },
>> +		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
>> +static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {	/* 9 */
>> +/* (without the +1)
>> + * SPCK       = 84.2666666666667 MHz
>> + * CCP2       = 505.6 MHz
>> + * VCO        = 505.6 MHz
>> + * VCOUNT     = 88 (2112)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 158
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = SMIA_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1056,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 84266667,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2500
>> +		},
>> +		.max_exp = 1052,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ SMIA_REG_8BIT, 0x1239, 0x4F },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1238, 0x02 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1222, 0x58 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x121D, 0x63 },	/*        */
>> +		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/*        */
>> +		{ SMIA_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
>> +	.magic   = SMIA_MAGIC,
>> +	.version = "V14 03-June-2008",
>> +	.reglist = {
>> +		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
>> +		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
>> +		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
>> +		{ .ptr = &mode4_svga_864x656_29_88fps },
>> +		{ .ptr = &mode5_vga_648x492_29_93fps },
>> +		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
>> +		{ .ptr = &mode_648x492_5fps },
>> +		{ .ptr = &mode3_4vga_1296x984_5fps },
>> +		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
>> +		{ .ptr = 0 }
>> +	}
>> +};
>> +
>> +/*
>> + * Return time of one row in microseconds, .8 fixed point format.
>> + * If the sensor is not set to any mode, return zero.
>> + */
>> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	if (!sensor->current_reglist)
>> +		return 0;
>> +
>> +	clock = sensor->current_reglist->mode.pixel_clock;
>> +	clock = (clock + (1 << 9)) >> 10;
>> +	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
>> +	rt = (rt + (clock >> 1)) / clock;
>> +
>> +	return rt;
>> +}
>> +
>> +/*
>> + * Convert exposure time `us' to rows. Modify `us' to make it to
>> + * correspond to the actual exposure time.
>> + */
>> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
>> +{
>> +	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	/* Assume that the maximum exposure time is at most ~8 s,
>> +	 * and the maximum width (with blanking) ~8000 pixels.
>> +	 * The formula here is in principle as simple as
>> +	 *    rows = exptime / 1e6 / width * pixel_clock
>> +	 * but to get accurate results while coping with value ranges,
>> +	 * have to do some fixed point math.
>> +	 */
>> +
>> +	rt = et8ek8_get_row_time(sensor);
>> +	rows = ((*us << 8) + (rt >> 1)) / rt;
>> +
>> +	if (rows > sensor->current_reglist->mode.max_exp)
>> +		rows = sensor->current_reglist->mode.max_exp;
>> +
>> +	/* Set the exposure time to the rounded value */
>> +	*us = (rt * rows + (1 << 7)) >> 8;
>> +
>> +	return rows;
>> +}
>> +
>> +/*
>> + * Convert exposure time in rows to microseconds
>> + */
>> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
>> +{
>> +	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
>> +}
>> +
>> +/* Called to change the V4L2 gain control value. This function
>> + * rounds and clamps the given value and updates the V4L2 control value.
>> + * If power is on, also updates the sensor analog and digital gains.
>> + * gain is in 0.1 EV (exposure value) units.
>> + */
>> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	struct et8ek8_gain new;
>> +	int r;
>> +
>> +	new = et8ek8_gain_table[gain];
>> +
>> +	/* FIXME: optimise I2C writes! */
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124a, new.analog >> 8);
>> +	if (r)
>> +		return r;
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x1249, new.analog & 0xff);
>> +	if (r)
>> +		return r;
>> +
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124d, new.digital >> 8);
>> +	if (r)
>> +		return r;
>> +	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
>> +				0x124c, new.digital & 0xff);
>> +
>> +	return r;
>> +}
>> +
>> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
>> +
>> +	/* Values for normal mode */
>> +	cbh_mode = 0;
>> +	cbv_mode = 0;
>> +	tp_mode  = 0;
>> +	din_sw   = 0x00;
>> +	r1420    = 0xF0;
>> +
>> +	if (mode != 0) {
>> +		/* Test pattern mode */
>> +		if (mode < 5) {
>> +			cbh_mode = 1;
>> +			cbv_mode = 1;
>> +			tp_mode  = mode + 3;
>> +		} else {
>> +			cbh_mode = 0;
>> +			cbv_mode = 0;
>> +			tp_mode  = mode - 4 + 3;
>> +		}
>> +		din_sw   = 0x01;
>> +		r1420    = 0xE0;
>> +	}
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode << 4);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121, cbh_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124, cbv_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
>> +	if (rval)
>> +		goto out;
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 controls
>> + */
>> +
>> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	const struct smia_mode *mode = &sensor->current_reglist->mode;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_MODE_FRAME_WIDTH:
>> +		ctrl->cur.val = mode->width;
>> +		break;
>> +	case V4L2_CID_MODE_FRAME_HEIGHT:
>> +		ctrl->cur.val = mode->height;
>> +		break;
>> +	case V4L2_CID_MODE_VISIBLE_WIDTH:
>> +		ctrl->cur.val = mode->window_width;
>> +		break;
>> +	case V4L2_CID_MODE_VISIBLE_HEIGHT:
>> +		ctrl->cur.val = mode->window_height;
>> +		break;
>> +	case V4L2_CID_MODE_PIXELCLOCK:
>> +		ctrl->cur.val = mode->pixel_clock;
>
> Please use V4L2_CID_PIXEL_RATE instead. It's a 64-bit control.
>

I already used it in set() operation, I think I is better to remove 
V4L2_CID_PIXEL_RATE and V4L2_CID_MODE_OPSYSCLOCK altogether as those are 
standart controls.

>> +		break;
>> +	case V4L2_CID_MODE_SENSITIVITY:
>> +		ctrl->cur.val = mode->sensitivity;
>> +		break;
>> +	case V4L2_CID_MODE_OPSYSCLOCK:
>> +		ctrl->cur.val = mode->opsys_clock;
>> +		break;
>
> V4L2_CID_LINK_FREQ.
>

see ^^^

>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int uninitialized_var(rows);
>> +
>> +	if (ctrl->id == V4L2_CID_EXPOSURE)
>> +		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_GAIN:
>> +		return et8ek8_set_gain(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_EXPOSURE:
>> +		return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
>> +					  swab16(rows));
>> +
>> +	case V4L2_CID_TEST_PATTERN:
>> +		return et8ek8_set_test_pattern(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_PIXEL_RATE:
>> +		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
>> +		return 0;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
>> +	.g_volatile_ctrl = et8ek8_get_ctrl,
>> +	.s_ctrl = et8ek8_set_ctrl,
>> +};
>> +
>> +static const char *et8ek8_test_pattern_menu[] = {
>> +	"Normal",
>> +	"Vertical colorbar",
>> +	"Horizontal colorbar",
>> +	"Scale",
>> +	"Ramp",
>> +	"Small vertical colorbar",
>> +	"Small horizontal colorbar",
>> +	"Small scale",
>> +	"Small ramp",
>> +};
>> +
>> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_TEST_PATTERN,
>> +		.type		= V4L2_CTRL_TYPE_MENU,
>> +		.name		= "Test pattern mode",
>> +		.min		= 0,
>> +		.max		= ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
>> +		.step		= 0,
>> +		.def		= 0,
>> +		.flags		= 0,
>> +		.qmenu		= et8ek8_test_pattern_menu,
>> +	},
>> +	{
>> +		.id		= V4L2_CID_MODE_CLASS,
>> +		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
>> +		.name		= "SMIA-type sensor information",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				| V4L2_CTRL_FLAG_WRITE_ONLY,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_FRAME_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_FRAME_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_VISIBLE_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_VISIBLE_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_PIXELCLOCK,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Pixel clock [Hz]",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_SENSITIVITY,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Sensivity",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= V4L2_CID_MODE_OPSYSCLOCK,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Output pixel clock [Hz]",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY
>> +				  | V4L2_CTRL_FLAG_VOLATILE,
>
> Many of the above controls are or standard controls should be used, please
> use the native V4L2 control framework functions to create the controls
> instead of the custom one. You get control names for free, for instance.
>

ok.

>> +	},
>> +};
>> +
>> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int i;
>> +	u32 min, max;
>> +
>> +	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
>> +			       ARRAY_SIZE(et8ek8_ctrls) + 2);
>> +
>> +	/* V4L2_CID_GAIN */
>> +	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
>> +			  1, 0);
>> +
>> +	/* V4L2_CID_EXPOSURE */
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor,
>> +				sensor->current_reglist->mode.max_exp);
>> +	sensor->exposure =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +				  V4L2_CID_EXPOSURE, min, max, min, max);
>> +	sensor->pixel_rate =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
>> +
>> +	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
>> +	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
>> +		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
>> +				     NULL);
>> +
>> +	if (sensor->ctrl_handler.error)
>> +		return sensor->ctrl_handler.error;
>> +
>> +	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
>> +	return 0;
>> +}
>> +
>> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_ctrl *ctrl = sensor->exposure;
>> +	u32 min, max;
>> +
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor,
>> +					 sensor->current_reglist->mode.max_exp);
>> +
>> +	v4l2_ctrl_lock(ctrl);
>> +	ctrl->minimum = min;
>> +	ctrl->maximum = max;
>> +	ctrl->step = min;
>> +	ctrl->default_value = max;
>> +	ctrl->val = max;
>> +	ctrl->cur.val = max;
>> +	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
>> +				 sensor->current_reglist->mode.ext_clock);
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +
>> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval;
>> +
>> +	rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	/* Controls set while the power to the sensor is turned off are saved
>> +	 * but not applied to the hardware. Now that we're about to start
>> +	 * streaming apply all the current values to the hardware.
>> +	 */
>> +	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	return 0;
>> +
>> +fail:
>> +	dev_err(&client->dev, "sensor configuration failed\n");
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
>> +}
>> +
>> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
>> +}
>> +
>> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret;
>> +
>> +	if (!streaming)
>> +		return et8ek8_stream_off(sensor);
>> +
>> +	ret = et8ek8_configure(sensor);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return et8ek8_stream_on(sensor);
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev operations
>> + */
>> +
>> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
>> +{
>> +	int rval;
>> +
>> +	gpiod_set_value(sensor->reset, 0);
>> +	udelay(1);
>> +
>> +	clk_disable_unprepare(sensor->ext_clk);
>> +
>> +	rval = regulator_disable(sensor->vana);
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int hz = ET8EK8_XCLK_HZ;
>> +	int val, rval;
>> +
>> +	rval = regulator_enable(sensor->vana);
>> +	if (rval) {
>> +		dev_err(&client->dev, "failed to enable vana regulator\n");
>> +		return rval;
>> +	}
>> +
>> +	if (sensor->current_reglist)
>> +		hz = sensor->current_reglist->mode.ext_clock;
>> +
>> +	rval = clk_set_rate(sensor->ext_clk, hz);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev,
>> +			"unable to set extclk clock freq to %u\n", hz);
>> +		goto out;
>> +	}
>> +	rval = clk_prepare_enable(sensor->ext_clk);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev, "failed to enable extclk\n");
>> +		goto out;
>> +	}
>> +
>> +	if (rval)
>> +		goto out;
>> +
>> +	udelay(10);			/* I wish this is a good value */
>> +
>> +	gpiod_set_value(sensor->reset, 1);
>> +
>> +	msleep(5000*1000/hz+1);				/* Wait 5000 cycles */
>> +
>> +	rval = smia_i2c_reglist_find_write(client,
>> +					   &et8ek8_smia_meta_reglist,
>> +					   SMIA_REGLIST_POWERON);
>> +	if (rval)
>> +		goto out;
>> +
>> +#ifdef USE_CRC
>> +	rval = smia_i2c_read_reg(client,
>> +				 SMIA_REG_8BIT, 0x1263, &val);
>> +	if (rval)
>> +		goto out;
>> +#if USE_CRC
>> +	val |= (1<<4);
>> +#else
>> +	val &= ~(1<<4);
>> +#endif
>> +	rval = smia_i2c_write_reg(client,
>> +				  SMIA_REG_8BIT, 0x1263, val);
>> +	if (rval)
>> +		goto out;
>> +#endif
>> +
>> +out:
>> +	if (rval)
>> +		et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev video operations
>> + */
>> +
>> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist, code);
>> +}
>> +
>> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist, fse);
>> +}
>> +
>> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_interval_enum *fie)
>> +{
>> +	return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist, fie);
>> +}
>> +
>> +static struct v4l2_mbus_framefmt *
>> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
>> +			struct v4l2_subdev_pad_config *cfg,
>> +			unsigned int pad, enum v4l2_subdev_format_whence which)
>> +{
>> +	switch (which) {
>> +	case V4L2_SUBDEV_FORMAT_TRY:
>> +		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
>> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
>> +		return &sensor->format;
>> +	default:
>> +		return NULL;
>> +	}
>> +}
>> +
>> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	fmt->format = *format;
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +        struct smia_reglist *reglist;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
>> +					     &fmt->format);
>> +	smia_reglist_to_mbus(reglist, &fmt->format);
>> +	*format = fmt->format;
>> +
>> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> +		sensor->current_reglist = reglist;
>> +		et8ek8_update_controls(sensor);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	memset(fi, 0, sizeof(*fi));
>> +	fi->interval = sensor->current_reglist->mode.timeperframe;
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct smia_reglist *reglist;
>> +
>> +	reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
>> +					      sensor->current_reglist,
>> +					      &fi->interval);
>> +
>> +	if (!reglist)
>> +		return -EINVAL;
>> +
>> +	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
>> +		return -EINVAL;
>> +
>> +	sensor->current_reglist = reglist;
>> +	et8ek8_update_controls(sensor);
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
>> +	unsigned int offset = 0;
>> +	u8 *ptr  = sensor->priv_mem;
>> +	int rval = 0;
>> +
>> +	/* Read the EEPROM window-by-window, each window 8 bytes */
>> +	do {
>> +		u8 buffer[PRIV_MEM_WIN_SIZE];
>> +		struct i2c_msg msg;
>> +		int bytes, i;
>> +		int ofs;
>> +
>> +		/* Set the current window */
>> +		rval = smia_i2c_write_reg(client,
>> +					  SMIA_REG_8BIT,
>> +					  0x0001,
>> +					  0xe0 | (offset >> 3));
>> +		if (rval < 0)
>> +			goto out;
>> +
>> +		/* Wait for status bit */
>> +		for (i = 0; i < 1000; ++i) {
>> +			u32 status;
>> +			rval = smia_i2c_read_reg(client,
>> +						 SMIA_REG_8BIT,
>> +						 0x0003,
>> +						 &status);
>> +			if (rval < 0)
>> +				goto out;
>> +			if ((status & 0x08) == 0)
>> +				break;
>> +			msleep(1);
>> +		};
>> +
>> +		if (i == 1000) {
>> +			rval = -EIO;
>> +			goto out;
>> +		}
>> +
>> +		/* Read window, 8 bytes at once, and copy to user space */
>> +		ofs = offset & 0x07;	/* Offset within this window */
>> +		bytes = length + ofs > 8 ? 8-ofs : length;
>> +		msg.addr = client->addr;
>> +		msg.flags = 0;
>> +		msg.len = 2;
>> +		msg.buf = buffer;
>> +		ofs += PRIV_MEM_START_REG;
>> +		buffer[0] = (u8)(ofs >> 8);
>> +		buffer[1] = (u8)(ofs & 0xFF);
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		mdelay(ET8EK8_I2C_DELAY);
>> +		msg.addr = client->addr;
>> +		msg.len = bytes;
>> +		msg.flags = I2C_M_RD;
>> +		msg.buf = buffer;
>> +		memset(buffer, 0, sizeof(buffer));
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		rval = 0;
>> +		memcpy(ptr, buffer, bytes);
>> +
>> +		length -= bytes;
>> +		offset += bytes;
>> +		ptr    += bytes;
>> +	} while (length > 0);
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval, rev_l, rev_h;
>> +
>> +	rval = et8ek8_power_on(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "could not power on\n");
>> +		return rval;
>> +	}
>> +
>> +	if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
>> +			      REG_REVISION_NUMBER_L, &rev_l) != 0
>> +	    || smia_i2c_read_reg(client, SMIA_REG_8BIT,
>> +				 REG_REVISION_NUMBER_H, &rev_h) != 0) {
>> +		dev_err(&client->dev,
>> +			"no et8ek8 sensor detected\n");
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +	sensor->version = (rev_h << 8) + rev_l;
>> +	if (sensor->version != ET8EK8_REV_1
>> +	    && sensor->version != ET8EK8_REV_2)
>> +		dev_info(&client->dev,
>> +			 "unknown version 0x%x detected, "
>> +			 "continuing anyway\n", sensor->version);
>> +
>> +	rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, import failed\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +
>> +	sensor->current_reglist =
>> +		smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>> +				       SMIA_REGLIST_MODE);
>> +	if (!sensor->current_reglist) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no mode found\n",
>> +			ET8EK8_NAME);
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +
>> +	smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
>> +
>> +	rval = smia_i2c_reglist_find_write(client,
>> +					   &et8ek8_smia_meta_reglist,
>> +					   SMIA_REGLIST_POWERON);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no POWERON mode found\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +	rval = et8ek8_stream_on(sensor);	/* Needed to be able to read EEPROM */
>> +	if (rval)
>> +		goto out_poweroff;
>> +	rval = et8ek8_g_priv_mem(subdev);
>> +	if (rval)
>> +		dev_warn(&client->dev,
>> +			"can not read OTP (EEPROM) memory from sensor\n");
>> +	rval = et8ek8_stream_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	rval = et8ek8_power_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	return 0;
>> +
>> +out_poweroff:
>> +	et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * sysfs attributes
>> + */
>> +static ssize_t
>> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
>> +		     char *buf)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
>> +#error PAGE_SIZE too small!
>> +#endif
>> +
>> +	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
>> +
>> +	return ET8EK8_PRIV_MEM_SIZE;
>> +}
>> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev core operations
>> + */
>> +
>> +static int
>> +et8ek8_registered(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +	int rval;
>> +
>> +	dev_dbg(&client->dev, "registered!");
>> +
>> +	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
>> +		dev_err(&client->dev, "could not register sysfs entry\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	rval = et8ek8_dev_init(subdev);
>> +	if (rval)
>> +		return rval;
>> +
>> +	rval = et8ek8_init_controls(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "controls initialization failed\n");
>> +		return rval;
>> +	}
>> +
>> +	format = __et8ek8_get_pad_format(sensor, NULL, 0,
>> +					 V4L2_SUBDEV_FORMAT_ACTIVE);
>> +	return 0;
>> +}
>> +
>> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
>> +{
>> +	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
>> +}
>> +
>> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret = 0;
>> +
>> +	mutex_lock(&sensor->power_lock);
>> +
>> +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> +	 * update the power state.
>> +	 */
>> +	if (sensor->power_count == !on) {
>> +		ret = __et8ek8_set_power(sensor, !!on);
>> +		if (ret < 0)
>> +			goto done;
>> +	}
>> +
>> +	/* Update the power count. */
>> +	sensor->power_count += on ? 1 : -1;
>> +	WARN_ON(sensor->power_count < 0);
>> +
>> +done:
>> +	mutex_unlock(&sensor->power_lock);
>> +	return ret;
>> +}
>> +
>> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
>> +	struct v4l2_mbus_framefmt *format;
>> +	struct smia_reglist *reglist;
>> +
>> +	reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
>> +					 SMIA_REGLIST_MODE);
>> +	format = __et8ek8_get_pad_format(sensor, fh->pad, 0, V4L2_SUBDEV_FORMAT_TRY);
>> +	smia_reglist_to_mbus(reglist, format);
>> +
>> +	return et8ek8_set_power(sd, true);
>> +}
>> +
>> +static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	return et8ek8_set_power(sd, false);
>> +}
>> +
>> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
>> +	.s_stream = et8ek8_s_stream,
>> +	.g_frame_interval = et8ek8_get_frame_interval,
>> +	.s_frame_interval = et8ek8_set_frame_interval,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
>> +	.s_power = et8ek8_set_power,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
>> +	.enum_mbus_code = et8ek8_enum_mbus_code,
>> +        .enum_frame_size = et8ek8_enum_frame_size,
>> +        .enum_frame_interval = et8ek8_enum_frame_ival,
>> +	.get_fmt = et8ek8_get_pad_format,
>> +	.set_fmt = et8ek8_set_pad_format,
>> +};
>> +
>> +static const struct v4l2_subdev_ops et8ek8_ops = {
>> +	.core = &et8ek8_core_ops,
>> +	.video = &et8ek8_video_ops,
>> +	.pad = &et8ek8_pad_ops,
>> +};
>> +
>> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
>> +	.registered = et8ek8_registered,
>> +	.open = et8ek8_open,
>> +	.close = et8ek8_close,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * I2C driver
>> + */
>> +#ifdef CONFIG_PM
>> +
>> +static int et8ek8_suspend(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, false);
>> +}
>> +
>> +static int et8ek8_resume(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, true);
>> +}
>> +
>> +static struct dev_pm_ops et8ek8_pm_ops = {
>> +	.suspend	= et8ek8_suspend,
>> +	.resume		= et8ek8_resume,
>> +};
>> +
>> +#else
>> +
>> +#define et8ek8_pm_ops	NULL
>> +
>> +#endif /* CONFIG_PM */
>> +
>> +static int et8ek8_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *devid)
>> +{
>> +	struct et8ek8_sensor *sensor;
>> +	int ret;
>> +
>> +	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
>> +	if (!sensor)
>> +		return -ENOMEM;
>> +
>> +	sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(sensor->reset)) {
>> +		dev_dbg(&client->dev, "could not request reset gpio\n");
>> +		return PTR_ERR(sensor->reset);;
>> +	}
>> +
>> +	sensor->vana = devm_regulator_get(&client->dev, "vana");
>> +	if (IS_ERR(sensor->vana)) {
>> +		dev_err(&client->dev, "could not get regulator for vana\n");
>> +		return PTR_ERR(sensor->vana);
>> +	}
>> +
>> +	sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
>> +	if (IS_ERR(sensor->ext_clk)) {
>> +		dev_err(&client->dev, "could not get clock\n");
>> +		return PTR_ERR(sensor->ext_clk);
>> +	}
>> +
>> +	mutex_init(&sensor->power_lock);
>> +
>> +	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
>> +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	sensor->subdev.internal_ops = &et8ek8_internal_ops;
>> +
>> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "media entity init failed!\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = v4l2_async_register_subdev(&sensor->subdev);
>> +	if (ret < 0) {
>> +		media_entity_cleanup(&sensor->subdev.entity);
>> +		return ret;
>> +	}
>> +
>> +	dev_dbg(&client->dev, "initialized!\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static int __exit et8ek8_remove(struct i2c_client *client)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (sensor->power_count) {
>> +		gpiod_set_value(sensor->reset, 0);
>> +		clk_disable_unprepare(sensor->ext_clk);
>> +		sensor->power_count = 0;
>> +	}
>> +
>> +	v4l2_device_unregister_subdev(&sensor->subdev);
>> +	device_remove_file(&client->dev, &dev_attr_priv_mem);
>> +	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
>> +	media_entity_cleanup(&sensor->subdev.entity);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id et8ek8_of_table[] = {
>> +	{ .compatible = "toshiba,et8ek8" },
>> +	{ },
>> +};
>> +
>> +static const struct i2c_device_id et8ek8_id_table[] = {
>> +	{ ET8EK8_NAME, 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
>> +
>> +static struct i2c_driver et8ek8_i2c_driver = {
>> +	.driver		= {
>> +		.name	= ET8EK8_NAME,
>> +		.pm	= &et8ek8_pm_ops,
>> +		.of_match_table	= et8ek8_of_table,
>> +	},
>> +	.probe		= et8ek8_probe,
>> +	.remove		= __exit_p(et8ek8_remove),
>> +	.id_table	= et8ek8_id_table,
>> +};
>> +
>> +module_i2c_driver(et8ek8_i2c_driver);
>> +
>> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
>
> s/nokia.com/iki.fi/ please.
>

ok

>> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
>> +MODULE_LICENSE("GPL");
>

Lets see if I understand correctly what needs to be done:

1. Replace custom controls with standart controls where possible
2. Move the used code from smia_reglist to et8ek8 driver, drop the remnants.

Thanks,
Ivo

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

* Re: [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver
  2016-05-01 12:50       ` Ivaylo Dimitrov
@ 2016-05-01 13:41         ` Sakari Ailus
  2016-05-03 14:50           ` [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor Ivaylo Dimitrov
  0 siblings, 1 reply; 102+ messages in thread
From: Sakari Ailus @ 2016-05-01 13:41 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

On Sun, May 01, 2016 at 03:50:47PM +0300, Ivaylo Dimitrov wrote:
> Hi,
> 
> On  1.05.2016 13:44, Sakari Ailus wrote:
> >Hi Ivaylo,
> >
> >On Mon, Apr 25, 2016 at 12:08:03AM +0300, Ivaylo Dimitrov wrote:
> >>add driver
> >>
> >>Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
> >>---
> >>  drivers/media/i2c/smia/Kconfig  |    8 +
> >>  drivers/media/i2c/smia/Makefile |    1 +
> >>  drivers/media/i2c/smia/et8ek8.c | 1788 +++++++++++++++++++++++++++++++++++++++
> >>  3 files changed, 1797 insertions(+)
> >>  create mode 100644 drivers/media/i2c/smia/et8ek8.c
> >>
> >>diff --git a/drivers/media/i2c/smia/Kconfig b/drivers/media/i2c/smia/Kconfig
> >>index d9be497..13ca043 100644
> >>--- a/drivers/media/i2c/smia/Kconfig
> >>+++ b/drivers/media/i2c/smia/Kconfig
> >>@@ -7,3 +7,11 @@ config VIDEO_SMIAREGS
> >>
> >>  	  Also a few helper functions are provided to work with binary
> >>  	  register lists.
> >>+
> >>+config VIDEO_ET8EK8
> >>+	tristate "ET8EK8 camera sensor support"
> >>+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> >>+	select VIDEO_SMIAREGS
> >>+	---help---
> >>+	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
> >>+	  It is used for example in Nokia N900 (RX-51).
> >>diff --git a/drivers/media/i2c/smia/Makefile b/drivers/media/i2c/smia/Makefile
> >>index cff67bc..56cf15e 100644
> >>--- a/drivers/media/i2c/smia/Makefile
> >>+++ b/drivers/media/i2c/smia/Makefile
> >>@@ -1 +1,2 @@
> >>  obj-$(CONFIG_VIDEO_SMIAREGS)  += smiaregs.o
> >>+obj-$(CONFIG_VIDEO_ET8EK8)    += et8ek8.o
> >>diff --git a/drivers/media/i2c/smia/et8ek8.c b/drivers/media/i2c/smia/et8ek8.c
> >>new file mode 100644
> >>index 0000000..46c112d
> >>--- /dev/null
> >>+++ b/drivers/media/i2c/smia/et8ek8.c
> >>@@ -0,0 +1,1788 @@
> >>+/*
> >>+ * drivers/media/video/et8ek8.c
> >>+ *
> >>+ * Copyright (C) 2008 Nokia Corporation
> >>+ *
> >>+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
> >>+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> >>+ *
> >>+ * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
> >>+ *
> >>+ * This driver is based on the Micron MT9T012 camera imager driver
> >>+ * (C) Texas Instruments.
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or
> >>+ * modify it under the terms of the GNU General Public License
> >>+ * version 2 as published by the Free Software Foundation.
> >>+ *
> >>+ * This program is distributed in the hope that it will be useful, but
> >>+ * WITHOUT ANY WARRANTY; without even the implied warranty of
> >>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>+ * General Public License for more details.
> >>+ *
> >>+ * You should have received a copy of the GNU General Public License
> >>+ * along with this program; if not, write to the Free Software
> >>+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> >>+ * 02110-1301 USA
> >>+ *
> >>+ */
> >>+
> >>+#define DEBUG
> >>+
> >>+#include <linux/clk.h>
> >>+#include <linux/delay.h>
> >>+#include <linux/i2c.h>
> >>+#include <linux/kernel.h>
> >>+#include <linux/module.h>
> >>+#include <linux/mutex.h>
> >>+#include <linux/gpio/consumer.h>
> >>+#include <linux/regulator/consumer.h>
> >>+#include <linux/slab.h>
> >>+#include <linux/version.h>
> >>+#include <linux/v4l2-mediabus.h>
> >>+
> >>+#include <media/media-entity.h>
> >>+#include <media/smiaregs.h>
> >>+#include <media/v4l2-ctrls.h>
> >>+#include <media/v4l2-device.h>
> >>+#include <media/v4l2-subdev.h>
> >>+
> >>+#define ET8EK8_NAME		"et8ek8"
> >>+#define ET8EK8_XCLK_HZ		9600000
> >>+#define ET8EK8_PRIV_MEM_SIZE	128
> >>+
> >>+#define CTRL_GAIN		0
> >>+#define CTRL_EXPOSURE		1
> >>+#define CTRL_TEST_PATTERN	2
> >>+
> >>+#define CID_TO_CTRL(id)		((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
> >>+				 (id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
> >>+				 (id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
> >>+				 -EINVAL)
> >>+
> >>+struct et8ek8_sensor {
> >>+	struct v4l2_subdev subdev;
> >>+	struct media_pad pad;
> >>+	struct v4l2_mbus_framefmt format;
> >>+	struct gpio_desc *reset;
> >>+	struct regulator *vana;
> >>+	struct clk *ext_clk;
> >>+
> >>+	u16 version;
> >>+
> >>+	struct v4l2_ctrl_handler ctrl_handler;
> >>+	struct v4l2_ctrl *exposure;
> >>+	struct v4l2_ctrl *pixel_rate;
> >>+	struct smia_reglist *current_reglist;
> >>+
> >>+	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
> >>+
> >>+	struct mutex power_lock;
> >>+	int power_count;
> >>+};
> >>+
> >>+#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
> >>+
> >>+enum et8ek8_versions {
> >>+	ET8EK8_REV_1 = 0x0001,
> >>+	ET8EK8_REV_2,
> >>+};
> >>+
> >>+/*
> >>+ * This table describes what should be written to the sensor register
> >>+ * for each gain value. The gain(index in the table) is in terms of
> >>+ * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
> >>+ * the *analog gain, [1] in the digital gain
> >>+ *
> >>+ * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
> >>+ */
> >>+static struct et8ek8_gain {
> >>+	u16 analog;
> >>+	u16 digital;
> >>+} const et8ek8_gain_table[] = {
> >>+	{ 32,    0},  /* x1 */
> >>+	{ 34,    0},
> >>+	{ 37,    0},
> >>+	{ 39,    0},
> >>+	{ 42,    0},
> >>+	{ 45,    0},
> >>+	{ 49,    0},
> >>+	{ 52,    0},
> >>+	{ 56,    0},
> >>+	{ 60,    0},
> >>+	{ 64,    0},  /* x2 */
> >>+	{ 69,    0},
> >>+	{ 74,    0},
> >>+	{ 79,    0},
> >>+	{ 84,    0},
> >>+	{ 91,    0},
> >>+	{ 97,    0},
> >>+	{104,    0},
> >>+	{111,    0},
> >>+	{119,    0},
> >>+	{128,    0},  /* x4 */
> >>+	{137,    0},
> >>+	{147,    0},
> >>+	{158,    0},
> >>+	{169,    0},
> >>+	{181,    0},
> >>+	{194,    0},
> >>+	{208,    0},
> >>+	{223,    0},
> >>+	{239,    0},
> >>+	{256,    0},  /* x8 */
> >>+	{256,   73},
> >>+	{256,  152},
> >>+	{256,  236},
> >>+	{256,  327},
> >>+	{256,  424},
> >>+	{256,  528},
> >>+	{256,  639},
> >>+	{256,  758},
> >>+	{256,  886},
> >>+	{256, 1023},  /* x16 */
> >>+};
> >>+
> >>+/* Register definitions */
> >>+#define REG_REVISION_NUMBER_L	0x1200
> >>+#define REG_REVISION_NUMBER_H	0x1201
> >>+
> >>+#define PRIV_MEM_START_REG	0x0008
> >>+#define PRIV_MEM_WIN_SIZE	8
> >>+
> >>+#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
> >>+
> >>+#define USE_CRC			1
> >>+
> >>+/*
> >>+ *
> >>+ * Stingray sensor mode settings for Scooby
> >>+ *
> >>+ *
> >>+ */
> >>+
> >
> >It'd be nice to get rid of the register lists, however considering where the
> >sensor is used it's unlikely going to find its way elsewhere, so the gain
> >might not be worth the effort.
> >
> >I'd still integrate the functionality in the smia register list library to
> >the driver. That sort of functionality ideally should not be needed at all
> >but sadly, the documentation of some sensors is too bad to write proper
> >drivers. :-(
> >
> >(SMIA is an ill-conceived name for this library btw., it's got nothing to do
> >with SMIA as such. I'd call it et8ek8_reglist for example. Perhaps the
> >library only would be a better choice.)

s/the/renaming the/

> >
> 
> ok.  I removed the custom controls found in smia_reglist library and it
> doesn't affect the functionality of the driver (at least I noticed no
> chagne), so maybe it is a good idea to remove the library and move the code
> to the driver. there are only a couple of functions remaining there after
> controls are removed.

Ack.

> 
> >>+/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
> >>+static struct smia_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {	/* 1 */
> >>+/* (without the +1)
> >>+ * SPCK       = 80 MHz
> >>+ * CCP2       = 640 MHz
> >>+ * VCO        = 640 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 137 (3288)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 200
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_POWERON,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3288,
> >>+		.height = 2016,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 2592,
> >>+		.window_height = 1968,
> >>+		.pixel_clock = 80000000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 1207
> >>+		},
> >>+		.max_exp = 2012,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x126C, 0xCC },	/* Need to set firstly */
> >>+		{ SMIA_REG_8BIT, 0x1269, 0x00 },	/* Strobe and Data of CCP2 delay are minimized. */
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x89 },	/* Refined value of Min H_COUNT  */
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x07 },	/* Frequency of SPCK setting (SPCK=MRCK) */
> >>+		{ SMIA_REG_8BIT, 0x1241, 0x94 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1242, 0x02 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x124B, 0x00 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1255, 0xFF },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1256, 0x9F },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1258, 0x00 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* From parallel out to serial out */
> >>+		{ SMIA_REG_8BIT, 0x125E, 0xC0 },	/* From w/ embeded data to w/o embeded data */
> >>+		{ SMIA_REG_8BIT, 0x1263, 0x98 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1268, 0xC6 },	/* CCP2 out is from STOP to ACTIVE */
> >>+		{ SMIA_REG_8BIT, 0x1434, 0x00 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1163, 0x44 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1166, 0x29 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1140, 0x02 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1011, 0x24 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1151, 0x80 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1152, 0x23 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1014, 0x05 },	/* Initial setting( for improvement2 of lower frequency noise ) */
> >>+		{ SMIA_REG_8BIT, 0x1033, 0x06 },
> >>+		{ SMIA_REG_8BIT, 0x1034, 0x79 },
> >>+		{ SMIA_REG_8BIT, 0x1423, 0x3F },
> >>+		{ SMIA_REG_8BIT, 0x1424, 0x3F },
> >>+		{ SMIA_REG_8BIT, 0x1426, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1439, 0x00 },	/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
> >>+		{ SMIA_REG_8BIT, 0x161F, 0x60 },	/* Switch of blemish correction (0d:disable / 1d:enable) */
> >>+		{ SMIA_REG_8BIT, 0x1634, 0x00 },	/* Switch of auto noise correction (0d:disable / 1d:enable) */
> >>+		{ SMIA_REG_8BIT, 0x1646, 0x00 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1648, 0x00 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x113E, 0x01 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x113F, 0x22 },	/* Initial setting */
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
> >>+static struct smia_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {	/* 2 */
> >>+/* (without the +1)
> >>+ * SPCK       = 80 MHz
> >>+ * CCP2       = 560 MHz
> >>+ * VCO        = 560 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 128 (3072)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 175
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 6
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3072,
> >>+		.height = 2016,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 2592,
> >>+		.window_height = 1968,
> >>+		.pixel_clock = 80000000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 1292
> >>+		},
> >>+		.max_exp = 2012,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x57 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x82 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x06 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x80 },	/* <-changed to v14 7E->80 */
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
> >>+static struct smia_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {	/* 3 */
> >>+/* (without the +1)
> >>+ * SPCK       = 96.5333333333333 MHz
> >>+ * CCP2       = 579.2 MHz
> >>+ * VCO        = 579.2 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 133 (3192)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 181
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 5
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3192,
> >>+		.height = 1008,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 1296,
> >>+		.window_height = 984,
> >>+		.pixel_clock = 96533333,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 3000
> >>+		},
> >>+		.max_exp = 1004,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x5A },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x82 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x63 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode4_SVGA_864x656_29.88fps */
> >>+static struct smia_reglist mode4_svga_864x656_29_88fps = {	/* 4 */
> >>+/* (without the +1)
> >>+ * SPCK       = 80 MHz
> >>+ * CCP2       = 320 MHz
> >>+ * VCO        = 640 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 166 (3984)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 200
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 1
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3984,
> >>+		.height = 672,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 864,
> >>+		.window_height = 656,
> >>+		.pixel_clock = 80000000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 2988
> >>+		},
> >>+		.max_exp = 668,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x62 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x62 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0xA6 },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode5_VGA_648x492_29.93fps */
> >>+static struct smia_reglist mode5_vga_648x492_29_93fps = {	/* 5 */
> >>+/* (without the +1)
> >>+ * SPCK       = 80 MHz
> >>+ * CCP2       = 320 MHz
> >>+ * VCO        = 640 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 221 (5304)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 200
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 1
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 5304,
> >>+		.height = 504,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 648,
> >>+		.window_height = 492,
> >>+		.pixel_clock = 80000000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 2993
> >>+		},
> >>+		.max_exp = 500,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x61 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x61 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0xDD },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode2_16VGA_2592x1968_3.99fps */
> >>+static struct smia_reglist mode2_16vga_2592x1968_3_99fps = {	/* 6 */
> >>+/* (without the +1)
> >>+ * SPCK       = 80 MHz
> >>+ * CCP2       = 640 MHz
> >>+ * VCO        = 640 MHz
> >>+ * VCOUNT     = 254 (6096)
> >>+ * HCOUNT     = 137 (3288)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 200
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3288,
> >>+		.height = 6096,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 2592,
> >>+		.window_height = 1968,
> >>+		.pixel_clock = 80000000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 399
> >>+		},
> >>+		.max_exp = 6092,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x07 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0xFE },
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode_648x492_5fps */
> >>+static struct smia_reglist mode_648x492_5fps = {	/* 7 */
> >>+/* (without the +1)
> >>+ * SPCK       = 13.3333333333333 MHz
> >>+ * CCP2       = 53.3333333333333 MHz
> >>+ * VCO        = 640 MHz
> >>+ * VCOUNT     = 84 (2016)
> >>+ * HCOUNT     = 221 (5304)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 200
> >>+ * VCO_DIV    = 5
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 1
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 5304,
> >>+		.height = 504,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 648,
> >>+		.window_height = 492,
> >>+		.pixel_clock = 13333333,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 499
> >>+		},
> >>+		.max_exp = 500,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x64 },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x71 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x57 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x61 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x61 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0xDD },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x54 },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode3_4VGA_1296x984_5fps */
> >>+static struct smia_reglist mode3_4vga_1296x984_5fps = {	/* 8 */
> >>+/* (without the +1)
> >>+ * SPCK       = 49.4 MHz
> >>+ * CCP2       = 395.2 MHz
> >>+ * VCO        = 790.4 MHz
> >>+ * VCOUNT     = 250 (6000)
> >>+ * HCOUNT     = 137 (3288)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 247
> >>+ * VCO_DIV    = 1
> >>+ * SPCK_DIV   = 7
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3288,
> >>+		.height = 3000,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 1296,
> >>+		.window_height = 984,
> >>+		.pixel_clock = 49400000,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 501
> >>+		},
> >>+		.max_exp = 2996,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x7B },
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x82 },
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x17 },
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x63 },
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x63 },
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x89 },
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },
> >>+		{ SMIA_REG_8BIT, 0x1222, 0xFA },
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x88 },	/* CCP_LVDS_MODE/  */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
> >>+static struct smia_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {	/* 9 */
> >>+/* (without the +1)
> >>+ * SPCK       = 84.2666666666667 MHz
> >>+ * CCP2       = 505.6 MHz
> >>+ * VCO        = 505.6 MHz
> >>+ * VCOUNT     = 88 (2112)
> >>+ * HCOUNT     = 133 (3192)
> >>+ * CKREF_DIV  = 2
> >>+ * CKVAR_DIV  = 158
> >>+ * VCO_DIV    = 0
> >>+ * SPCK_DIV   = 5
> >>+ * MRCK_DIV   = 7
> >>+ * LVDSCK_DIV = 0
> >>+ */
> >>+	.type = SMIA_REGLIST_MODE,
> >>+	.mode = {
> >>+		.sensor_width = 2592,
> >>+		.sensor_height = 1968,
> >>+		.sensor_window_origin_x = 0,
> >>+		.sensor_window_origin_y = 0,
> >>+		.sensor_window_width = 2592,
> >>+		.sensor_window_height = 1968,
> >>+		.width = 3192,
> >>+		.height = 1056,
> >>+		.window_origin_x = 0,
> >>+		.window_origin_y = 0,
> >>+		.window_width = 1296,
> >>+		.window_height = 984,
> >>+		.pixel_clock = 84266667,
> >>+		.ext_clock = 9600000,
> >>+		.timeperframe = {
> >>+			.numerator = 100,
> >>+			.denominator = 2500
> >>+		},
> >>+		.max_exp = 1052,
> >>+		/* .max_gain = 0, */
> >>+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> >>+		.sensitivity = 65536
> >>+	},
> >>+	.regs = {
> >>+		{ SMIA_REG_8BIT, 0x1239, 0x4F },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1238, 0x02 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x123B, 0x70 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x123A, 0x05 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x121B, 0x63 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1220, 0x85 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1221, 0x00 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1222, 0x58 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x1223, 0x00 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x121D, 0x63 },	/*        */
> >>+		{ SMIA_REG_8BIT, 0x125D, 0x83 },	/*        */
> >>+		{ SMIA_REG_TERM, 0, 0}
> >>+	}
> >>+};
> >>+
> >>+static struct smia_meta_reglist et8ek8_smia_meta_reglist = {
> >>+	.magic   = SMIA_MAGIC,
> >>+	.version = "V14 03-June-2008",
> >>+	.reglist = {
> >>+		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
> >>+		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
> >>+		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
> >>+		{ .ptr = &mode4_svga_864x656_29_88fps },
> >>+		{ .ptr = &mode5_vga_648x492_29_93fps },
> >>+		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
> >>+		{ .ptr = &mode_648x492_5fps },
> >>+		{ .ptr = &mode3_4vga_1296x984_5fps },
> >>+		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
> >>+		{ .ptr = 0 }
> >>+	}
> >>+};
> >>+
> >>+/*
> >>+ * Return time of one row in microseconds, .8 fixed point format.
> >>+ * If the sensor is not set to any mode, return zero.
> >>+ */
> >>+static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
> >>+{
> >>+	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
> >>+	unsigned int rt;	/* Row time in .8 fixed point */
> >>+
> >>+	if (!sensor->current_reglist)
> >>+		return 0;
> >>+
> >>+	clock = sensor->current_reglist->mode.pixel_clock;
> >>+	clock = (clock + (1 << 9)) >> 10;
> >>+	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
> >>+	rt = (rt + (clock >> 1)) / clock;
> >>+
> >>+	return rt;
> >>+}
> >>+
> >>+/*
> >>+ * Convert exposure time `us' to rows. Modify `us' to make it to
> >>+ * correspond to the actual exposure time.
> >>+ */
> >>+static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
> >>+{
> >>+	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
> >>+	unsigned int rt;	/* Row time in .8 fixed point */
> >>+
> >>+	/* Assume that the maximum exposure time is at most ~8 s,
> >>+	 * and the maximum width (with blanking) ~8000 pixels.
> >>+	 * The formula here is in principle as simple as
> >>+	 *    rows = exptime / 1e6 / width * pixel_clock
> >>+	 * but to get accurate results while coping with value ranges,
> >>+	 * have to do some fixed point math.
> >>+	 */
> >>+
> >>+	rt = et8ek8_get_row_time(sensor);
> >>+	rows = ((*us << 8) + (rt >> 1)) / rt;
> >>+
> >>+	if (rows > sensor->current_reglist->mode.max_exp)
> >>+		rows = sensor->current_reglist->mode.max_exp;
> >>+
> >>+	/* Set the exposure time to the rounded value */
> >>+	*us = (rt * rows + (1 << 7)) >> 8;
> >>+
> >>+	return rows;
> >>+}
> >>+
> >>+/*
> >>+ * Convert exposure time in rows to microseconds
> >>+ */
> >>+static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
> >>+{
> >>+	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
> >>+}
> >>+
> >>+/* Called to change the V4L2 gain control value. This function
> >>+ * rounds and clamps the given value and updates the V4L2 control value.
> >>+ * If power is on, also updates the sensor analog and digital gains.
> >>+ * gain is in 0.1 EV (exposure value) units.
> >>+ */
> >>+static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
> >>+{
> >>+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> >>+	struct et8ek8_gain new;
> >>+	int r;
> >>+
> >>+	new = et8ek8_gain_table[gain];
> >>+
> >>+	/* FIXME: optimise I2C writes! */
> >>+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> >>+				0x124a, new.analog >> 8);
> >>+	if (r)
> >>+		return r;
> >>+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> >>+				0x1249, new.analog & 0xff);
> >>+	if (r)
> >>+		return r;
> >>+
> >>+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> >>+				0x124d, new.digital >> 8);
> >>+	if (r)
> >>+		return r;
> >>+	r = smia_i2c_write_reg(client, SMIA_REG_8BIT,
> >>+				0x124c, new.digital & 0xff);
> >>+
> >>+	return r;
> >>+}
> >>+
> >>+static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
> >>+{
> >>+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> >>+	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
> >>+
> >>+	/* Values for normal mode */
> >>+	cbh_mode = 0;
> >>+	cbv_mode = 0;
> >>+	tp_mode  = 0;
> >>+	din_sw   = 0x00;
> >>+	r1420    = 0xF0;
> >>+
> >>+	if (mode != 0) {
> >>+		/* Test pattern mode */
> >>+		if (mode < 5) {
> >>+			cbh_mode = 1;
> >>+			cbv_mode = 1;
> >>+			tp_mode  = mode + 3;
> >>+		} else {
> >>+			cbh_mode = 0;
> >>+			cbv_mode = 0;
> >>+			tp_mode  = mode - 4 + 3;
> >>+		}
> >>+		din_sw   = 0x01;
> >>+		r1420    = 0xE0;
> >>+	}
> >>+
> >>+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x111B, tp_mode << 4);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1121, cbh_mode << 7);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1124, cbv_mode << 7);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x112C, din_sw);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+	rval = smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1420, r1420);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+out:
> >>+	return rval;
> >>+}
> >>+
> >>+/* -----------------------------------------------------------------------------
> >>+ * V4L2 controls
> >>+ */
> >>+
> >>+static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
> >>+{
> >>+	struct et8ek8_sensor *sensor =
> >>+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> >>+	const struct smia_mode *mode = &sensor->current_reglist->mode;
> >>+
> >>+	switch (ctrl->id) {
> >>+	case V4L2_CID_MODE_FRAME_WIDTH:
> >>+		ctrl->cur.val = mode->width;
> >>+		break;
> >>+	case V4L2_CID_MODE_FRAME_HEIGHT:
> >>+		ctrl->cur.val = mode->height;
> >>+		break;
> >>+	case V4L2_CID_MODE_VISIBLE_WIDTH:
> >>+		ctrl->cur.val = mode->window_width;
> >>+		break;
> >>+	case V4L2_CID_MODE_VISIBLE_HEIGHT:
> >>+		ctrl->cur.val = mode->window_height;
> >>+		break;
> >>+	case V4L2_CID_MODE_PIXELCLOCK:
> >>+		ctrl->cur.val = mode->pixel_clock;
> >
> >Please use V4L2_CID_PIXEL_RATE instead. It's a 64-bit control.
> >
> 
> I already used it in set() operation, I think I is better to remove
> V4L2_CID_PIXEL_RATE and V4L2_CID_MODE_OPSYSCLOCK altogether as those are
> standart controls.
> 
> >>+		break;
> >>+	case V4L2_CID_MODE_SENSITIVITY:
> >>+		ctrl->cur.val = mode->sensitivity;
> >>+		break;
> >>+	case V4L2_CID_MODE_OPSYSCLOCK:
> >>+		ctrl->cur.val = mode->opsys_clock;
> >>+		break;
> >
> >V4L2_CID_LINK_FREQ.
> >

Ack.

> 
> see ^^^
> 
> >>+	}
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
> >>+{
> >>+	struct et8ek8_sensor *sensor =
> >>+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> >>+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> >>+	int uninitialized_var(rows);
> >>+
> >>+	if (ctrl->id == V4L2_CID_EXPOSURE)
> >>+		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
> >>+
> >>+	switch (ctrl->id) {
> >>+	case V4L2_CID_GAIN:
> >>+		return et8ek8_set_gain(sensor, ctrl->val);
> >>+
> >>+	case V4L2_CID_EXPOSURE:
> >>+		return smia_i2c_write_reg(client, SMIA_REG_16BIT, 0x1243,
> >>+					  swab16(rows));
> >>+
> >>+	case V4L2_CID_TEST_PATTERN:
> >>+		return et8ek8_set_test_pattern(sensor, ctrl->val);
> >>+
> >>+	case V4L2_CID_PIXEL_RATE:
> >>+		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
> >>+		return 0;
> >>+
> >>+	default:
> >>+		return -EINVAL;
> >>+	}
> >>+}
> >>+
> >>+static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
> >>+	.g_volatile_ctrl = et8ek8_get_ctrl,
> >>+	.s_ctrl = et8ek8_set_ctrl,
> >>+};
> >>+
> >>+static const char *et8ek8_test_pattern_menu[] = {
> >>+	"Normal",
> >>+	"Vertical colorbar",
> >>+	"Horizontal colorbar",
> >>+	"Scale",
> >>+	"Ramp",
> >>+	"Small vertical colorbar",
> >>+	"Small horizontal colorbar",
> >>+	"Small scale",
> >>+	"Small ramp",
> >>+};
> >>+
> >>+static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_TEST_PATTERN,
> >>+		.type		= V4L2_CTRL_TYPE_MENU,
> >>+		.name		= "Test pattern mode",
> >>+		.min		= 0,
> >>+		.max		= ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
> >>+		.step		= 0,
> >>+		.def		= 0,
> >>+		.flags		= 0,
> >>+		.qmenu		= et8ek8_test_pattern_menu,
> >>+	},
> >>+	{
> >>+		.id		= V4L2_CID_MODE_CLASS,
> >>+		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
> >>+		.name		= "SMIA-type sensor information",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				| V4L2_CTRL_FLAG_WRITE_ONLY,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_FRAME_WIDTH,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Frame width",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_FRAME_HEIGHT,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Frame height",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_VISIBLE_WIDTH,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Visible width",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_VISIBLE_HEIGHT,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Visible height",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_PIXELCLOCK,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Pixel clock [Hz]",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_SENSITIVITY,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Sensivity",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >>+	},
> >>+	{
> >>+		.ops		= &et8ek8_ctrl_ops,
> >>+		.id		= V4L2_CID_MODE_OPSYSCLOCK,
> >>+		.type		= V4L2_CTRL_TYPE_INTEGER,
> >>+		.name		= "Output pixel clock [Hz]",
> >>+		.min		= 0,
> >>+		.max		= 0,
> >>+		.step		= 1,
> >>+		.def		= 0,
> >>+		.flags		= V4L2_CTRL_FLAG_READ_ONLY
> >>+				  | V4L2_CTRL_FLAG_VOLATILE,
> >
> >Many of the above controls are or standard controls should be used, please
> >use the native V4L2 control framework functions to create the controls
> >instead of the custom one. You get control names for free, for instance.
> >
> 
> ok.
> 
> >>+	},
> >>+};
> >>+
> >>+static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
> >>+{
> >>+	unsigned int i;
> >>+	u32 min, max;
> >>+
> >>+	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
> >>+			       ARRAY_SIZE(et8ek8_ctrls) + 2);
> >>+
> >>+	/* V4L2_CID_GAIN */
> >>+	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> >>+			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
> >>+			  1, 0);
> >>+
> >>+	/* V4L2_CID_EXPOSURE */
> >>+	min = et8ek8_exposure_rows_to_us(sensor, 1);
> >>+	max = et8ek8_exposure_rows_to_us(sensor,
> >>+				sensor->current_reglist->mode.max_exp);
> >>+	sensor->exposure =
> >>+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> >>+				  V4L2_CID_EXPOSURE, min, max, min, max);
> >>+	sensor->pixel_rate =
> >>+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> >>+		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
> >>+
> >>+	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
> >>+	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
> >>+		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
> >>+				     NULL);
> >>+
> >>+	if (sensor->ctrl_handler.error)
> >>+		return sensor->ctrl_handler.error;
> >>+
> >>+	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
> >>+	return 0;
> >>+}
> >>+
> >>+static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
> >>+{
> >>+	struct v4l2_ctrl *ctrl = sensor->exposure;
> >>+	u32 min, max;
> >>+
> >>+	min = et8ek8_exposure_rows_to_us(sensor, 1);
> >>+	max = et8ek8_exposure_rows_to_us(sensor,
> >>+					 sensor->current_reglist->mode.max_exp);
> >>+
> >>+	v4l2_ctrl_lock(ctrl);
> >>+	ctrl->minimum = min;
> >>+	ctrl->maximum = max;
> >>+	ctrl->step = min;
> >>+	ctrl->default_value = max;
> >>+	ctrl->val = max;
> >>+	ctrl->cur.val = max;
> >>+	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
> >>+				 sensor->current_reglist->mode.ext_clock);
> >>+	v4l2_ctrl_unlock(ctrl);
> >>+}
> >>+
> >>+static int et8ek8_configure(struct et8ek8_sensor *sensor)
> >>+{
> >>+	struct v4l2_subdev *subdev = &sensor->subdev;
> >>+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >>+	int rval;
> >>+
> >>+	rval = smia_i2c_write_regs(client, sensor->current_reglist->regs);
> >>+	if (rval)
> >>+		goto fail;
> >>+
> >>+	/* Controls set while the power to the sensor is turned off are saved
> >>+	 * but not applied to the hardware. Now that we're about to start
> >>+	 * streaming apply all the current values to the hardware.
> >>+	 */
> >>+	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
> >>+	if (rval)
> >>+		goto fail;
> >>+
> >>+	return 0;
> >>+
> >>+fail:
> >>+	dev_err(&client->dev, "sensor configuration failed\n");
> >>+	return rval;
> >>+}
> >>+
> >>+static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
> >>+{
> >>+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> >>+
> >>+	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0xb0);
> >>+}
> >>+
> >>+static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
> >>+{
> >>+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> >>+
> >>+	return smia_i2c_write_reg(client, SMIA_REG_8BIT, 0x1252, 0x30);
> >>+}
> >>+
> >>+static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	int ret;
> >>+
> >>+	if (!streaming)
> >>+		return et8ek8_stream_off(sensor);
> >>+
> >>+	ret = et8ek8_configure(sensor);
> >>+	if (ret < 0)
> >>+		return ret;
> >>+
> >>+	return et8ek8_stream_on(sensor);
> >>+}
> >>+
> >>+/* --------------------------------------------------------------------------
> >>+ * V4L2 subdev operations
> >>+ */
> >>+
> >>+static int et8ek8_power_off(struct et8ek8_sensor *sensor)
> >>+{
> >>+	int rval;
> >>+
> >>+	gpiod_set_value(sensor->reset, 0);
> >>+	udelay(1);
> >>+
> >>+	clk_disable_unprepare(sensor->ext_clk);
> >>+
> >>+	rval = regulator_disable(sensor->vana);
> >>+	return rval;
> >>+}
> >>+
> >>+static int et8ek8_power_on(struct et8ek8_sensor *sensor)
> >>+{
> >>+	struct v4l2_subdev *subdev = &sensor->subdev;
> >>+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >>+	unsigned int hz = ET8EK8_XCLK_HZ;
> >>+	int val, rval;
> >>+
> >>+	rval = regulator_enable(sensor->vana);
> >>+	if (rval) {
> >>+		dev_err(&client->dev, "failed to enable vana regulator\n");
> >>+		return rval;
> >>+	}
> >>+
> >>+	if (sensor->current_reglist)
> >>+		hz = sensor->current_reglist->mode.ext_clock;
> >>+
> >>+	rval = clk_set_rate(sensor->ext_clk, hz);
> >>+	if (rval < 0) {
> >>+		dev_err(&client->dev,
> >>+			"unable to set extclk clock freq to %u\n", hz);
> >>+		goto out;
> >>+	}
> >>+	rval = clk_prepare_enable(sensor->ext_clk);
> >>+	if (rval < 0) {
> >>+		dev_err(&client->dev, "failed to enable extclk\n");
> >>+		goto out;
> >>+	}
> >>+
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+	udelay(10);			/* I wish this is a good value */
> >>+
> >>+	gpiod_set_value(sensor->reset, 1);
> >>+
> >>+	msleep(5000*1000/hz+1);				/* Wait 5000 cycles */
> >>+
> >>+	rval = smia_i2c_reglist_find_write(client,
> >>+					   &et8ek8_smia_meta_reglist,
> >>+					   SMIA_REGLIST_POWERON);
> >>+	if (rval)
> >>+		goto out;
> >>+
> >>+#ifdef USE_CRC
> >>+	rval = smia_i2c_read_reg(client,
> >>+				 SMIA_REG_8BIT, 0x1263, &val);
> >>+	if (rval)
> >>+		goto out;
> >>+#if USE_CRC
> >>+	val |= (1<<4);
> >>+#else
> >>+	val &= ~(1<<4);
> >>+#endif
> >>+	rval = smia_i2c_write_reg(client,
> >>+				  SMIA_REG_8BIT, 0x1263, val);
> >>+	if (rval)
> >>+		goto out;
> >>+#endif
> >>+
> >>+out:
> >>+	if (rval)
> >>+		et8ek8_power_off(sensor);
> >>+
> >>+	return rval;
> >>+}
> >>+
> >>+/* --------------------------------------------------------------------------
> >>+ * V4L2 subdev video operations
> >>+ */
> >>+
> >>+static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
> >>+				 struct v4l2_subdev_pad_config *cfg,
> >>+				 struct v4l2_subdev_mbus_code_enum *code)
> >>+{
> >>+	return smia_reglist_enum_mbus_code(&et8ek8_smia_meta_reglist, code);
> >>+}
> >>+
> >>+static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
> >>+				  struct v4l2_subdev_pad_config *cfg,
> >>+				  struct v4l2_subdev_frame_size_enum *fse)
> >>+{
> >>+	return smia_reglist_enum_frame_size(&et8ek8_smia_meta_reglist, fse);
> >>+}
> >>+
> >>+static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
> >>+				  struct v4l2_subdev_pad_config *cfg,
> >>+				  struct v4l2_subdev_frame_interval_enum *fie)
> >>+{
> >>+	return smia_reglist_enum_frame_ival(&et8ek8_smia_meta_reglist, fie);
> >>+}
> >>+
> >>+static struct v4l2_mbus_framefmt *
> >>+__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
> >>+			struct v4l2_subdev_pad_config *cfg,
> >>+			unsigned int pad, enum v4l2_subdev_format_whence which)
> >>+{
> >>+	switch (which) {
> >>+	case V4L2_SUBDEV_FORMAT_TRY:
> >>+		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
> >>+	case V4L2_SUBDEV_FORMAT_ACTIVE:
> >>+		return &sensor->format;
> >>+	default:
> >>+		return NULL;
> >>+	}
> >>+}
> >>+
> >>+static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
> >>+				 struct v4l2_subdev_pad_config *cfg,
> >>+				 struct v4l2_subdev_format *fmt)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct v4l2_mbus_framefmt *format;
> >>+
> >>+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> >>+	if (format == NULL)
> >>+		return -EINVAL;
> >>+
> >>+	fmt->format = *format;
> >>+	return 0;
> >>+}
> >>+
> >>+static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
> >>+				 struct v4l2_subdev_pad_config *cfg,
> >>+				 struct v4l2_subdev_format *fmt)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct v4l2_mbus_framefmt *format;
> >>+        struct smia_reglist *reglist;
> >>+
> >>+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> >>+	if (format == NULL)
> >>+		return -EINVAL;
> >>+
> >>+	reglist = smia_reglist_find_mode_fmt(&et8ek8_smia_meta_reglist,
> >>+					     &fmt->format);
> >>+	smia_reglist_to_mbus(reglist, &fmt->format);
> >>+	*format = fmt->format;
> >>+
> >>+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> >>+		sensor->current_reglist = reglist;
> >>+		et8ek8_update_controls(sensor);
> >>+	}
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
> >>+				     struct v4l2_subdev_frame_interval *fi)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+
> >>+	memset(fi, 0, sizeof(*fi));
> >>+	fi->interval = sensor->current_reglist->mode.timeperframe;
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
> >>+				     struct v4l2_subdev_frame_interval *fi)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct smia_reglist *reglist;
> >>+
> >>+	reglist = smia_reglist_find_mode_ival(&et8ek8_smia_meta_reglist,
> >>+					      sensor->current_reglist,
> >>+					      &fi->interval);
> >>+
> >>+	if (!reglist)
> >>+		return -EINVAL;
> >>+
> >>+	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
> >>+		return -EINVAL;
> >>+
> >>+	sensor->current_reglist = reglist;
> >>+	et8ek8_update_controls(sensor);
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >>+	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
> >>+	unsigned int offset = 0;
> >>+	u8 *ptr  = sensor->priv_mem;
> >>+	int rval = 0;
> >>+
> >>+	/* Read the EEPROM window-by-window, each window 8 bytes */
> >>+	do {
> >>+		u8 buffer[PRIV_MEM_WIN_SIZE];
> >>+		struct i2c_msg msg;
> >>+		int bytes, i;
> >>+		int ofs;
> >>+
> >>+		/* Set the current window */
> >>+		rval = smia_i2c_write_reg(client,
> >>+					  SMIA_REG_8BIT,
> >>+					  0x0001,
> >>+					  0xe0 | (offset >> 3));
> >>+		if (rval < 0)
> >>+			goto out;
> >>+
> >>+		/* Wait for status bit */
> >>+		for (i = 0; i < 1000; ++i) {
> >>+			u32 status;
> >>+			rval = smia_i2c_read_reg(client,
> >>+						 SMIA_REG_8BIT,
> >>+						 0x0003,
> >>+						 &status);
> >>+			if (rval < 0)
> >>+				goto out;
> >>+			if ((status & 0x08) == 0)
> >>+				break;
> >>+			msleep(1);
> >>+		};
> >>+
> >>+		if (i == 1000) {
> >>+			rval = -EIO;
> >>+			goto out;
> >>+		}
> >>+
> >>+		/* Read window, 8 bytes at once, and copy to user space */
> >>+		ofs = offset & 0x07;	/* Offset within this window */
> >>+		bytes = length + ofs > 8 ? 8-ofs : length;
> >>+		msg.addr = client->addr;
> >>+		msg.flags = 0;
> >>+		msg.len = 2;
> >>+		msg.buf = buffer;
> >>+		ofs += PRIV_MEM_START_REG;
> >>+		buffer[0] = (u8)(ofs >> 8);
> >>+		buffer[1] = (u8)(ofs & 0xFF);
> >>+		rval = i2c_transfer(client->adapter, &msg, 1);
> >>+		if (rval < 0)
> >>+			goto out;
> >>+		mdelay(ET8EK8_I2C_DELAY);
> >>+		msg.addr = client->addr;
> >>+		msg.len = bytes;
> >>+		msg.flags = I2C_M_RD;
> >>+		msg.buf = buffer;
> >>+		memset(buffer, 0, sizeof(buffer));
> >>+		rval = i2c_transfer(client->adapter, &msg, 1);
> >>+		if (rval < 0)
> >>+			goto out;
> >>+		rval = 0;
> >>+		memcpy(ptr, buffer, bytes);
> >>+
> >>+		length -= bytes;
> >>+		offset += bytes;
> >>+		ptr    += bytes;
> >>+	} while (length > 0);
> >>+
> >>+out:
> >>+	return rval;
> >>+}
> >>+
> >>+static int et8ek8_dev_init(struct v4l2_subdev *subdev)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >>+	int rval, rev_l, rev_h;
> >>+
> >>+	rval = et8ek8_power_on(sensor);
> >>+	if (rval) {
> >>+		dev_err(&client->dev, "could not power on\n");
> >>+		return rval;
> >>+	}
> >>+
> >>+	if (smia_i2c_read_reg(client, SMIA_REG_8BIT,
> >>+			      REG_REVISION_NUMBER_L, &rev_l) != 0
> >>+	    || smia_i2c_read_reg(client, SMIA_REG_8BIT,
> >>+				 REG_REVISION_NUMBER_H, &rev_h) != 0) {
> >>+		dev_err(&client->dev,
> >>+			"no et8ek8 sensor detected\n");
> >>+		rval = -ENODEV;
> >>+		goto out_poweroff;
> >>+	}
> >>+	sensor->version = (rev_h << 8) + rev_l;
> >>+	if (sensor->version != ET8EK8_REV_1
> >>+	    && sensor->version != ET8EK8_REV_2)
> >>+		dev_info(&client->dev,
> >>+			 "unknown version 0x%x detected, "
> >>+			 "continuing anyway\n", sensor->version);
> >>+
> >>+	rval = smia_reglist_import(&et8ek8_smia_meta_reglist);
> >>+	if (rval) {
> >>+		dev_err(&client->dev,
> >>+			"invalid register list %s, import failed\n",
> >>+			ET8EK8_NAME);
> >>+		goto out_poweroff;
> >>+	}
> >>+
> >>+	sensor->current_reglist =
> >>+		smia_reglist_find_type(&et8ek8_smia_meta_reglist,
> >>+				       SMIA_REGLIST_MODE);
> >>+	if (!sensor->current_reglist) {
> >>+		dev_err(&client->dev,
> >>+			"invalid register list %s, no mode found\n",
> >>+			ET8EK8_NAME);
> >>+		rval = -ENODEV;
> >>+		goto out_poweroff;
> >>+	}
> >>+
> >>+	smia_reglist_to_mbus(sensor->current_reglist, &sensor->format);
> >>+
> >>+	rval = smia_i2c_reglist_find_write(client,
> >>+					   &et8ek8_smia_meta_reglist,
> >>+					   SMIA_REGLIST_POWERON);
> >>+	if (rval) {
> >>+		dev_err(&client->dev,
> >>+			"invalid register list %s, no POWERON mode found\n",
> >>+			ET8EK8_NAME);
> >>+		goto out_poweroff;
> >>+	}
> >>+	rval = et8ek8_stream_on(sensor);	/* Needed to be able to read EEPROM */
> >>+	if (rval)
> >>+		goto out_poweroff;
> >>+	rval = et8ek8_g_priv_mem(subdev);
> >>+	if (rval)
> >>+		dev_warn(&client->dev,
> >>+			"can not read OTP (EEPROM) memory from sensor\n");
> >>+	rval = et8ek8_stream_off(sensor);
> >>+	if (rval)
> >>+		goto out_poweroff;
> >>+
> >>+	rval = et8ek8_power_off(sensor);
> >>+	if (rval)
> >>+		goto out_poweroff;
> >>+
> >>+	return 0;
> >>+
> >>+out_poweroff:
> >>+	et8ek8_power_off(sensor);
> >>+
> >>+	return rval;
> >>+}
> >>+
> >>+/* --------------------------------------------------------------------------
> >>+ * sysfs attributes
> >>+ */
> >>+static ssize_t
> >>+et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
> >>+		     char *buf)
> >>+{
> >>+	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+
> >>+#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
> >>+#error PAGE_SIZE too small!
> >>+#endif
> >>+
> >>+	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
> >>+
> >>+	return ET8EK8_PRIV_MEM_SIZE;
> >>+}
> >>+static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
> >>+
> >>+/* --------------------------------------------------------------------------
> >>+ * V4L2 subdev core operations
> >>+ */
> >>+
> >>+static int
> >>+et8ek8_registered(struct v4l2_subdev *subdev)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >>+	struct v4l2_mbus_framefmt *format;
> >>+	int rval;
> >>+
> >>+	dev_dbg(&client->dev, "registered!");
> >>+
> >>+	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
> >>+		dev_err(&client->dev, "could not register sysfs entry\n");
> >>+		return -EBUSY;
> >>+	}
> >>+
> >>+	rval = et8ek8_dev_init(subdev);
> >>+	if (rval)
> >>+		return rval;
> >>+
> >>+	rval = et8ek8_init_controls(sensor);
> >>+	if (rval) {
> >>+		dev_err(&client->dev, "controls initialization failed\n");
> >>+		return rval;
> >>+	}
> >>+
> >>+	format = __et8ek8_get_pad_format(sensor, NULL, 0,
> >>+					 V4L2_SUBDEV_FORMAT_ACTIVE);
> >>+	return 0;
> >>+}
> >>+
> >>+static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
> >>+{
> >>+	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
> >>+}
> >>+
> >>+static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+	int ret = 0;
> >>+
> >>+	mutex_lock(&sensor->power_lock);
> >>+
> >>+	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
> >>+	 * update the power state.
> >>+	 */
> >>+	if (sensor->power_count == !on) {
> >>+		ret = __et8ek8_set_power(sensor, !!on);
> >>+		if (ret < 0)
> >>+			goto done;
> >>+	}
> >>+
> >>+	/* Update the power count. */
> >>+	sensor->power_count += on ? 1 : -1;
> >>+	WARN_ON(sensor->power_count < 0);
> >>+
> >>+done:
> >>+	mutex_unlock(&sensor->power_lock);
> >>+	return ret;
> >>+}
> >>+
> >>+static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> >>+{
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
> >>+	struct v4l2_mbus_framefmt *format;
> >>+	struct smia_reglist *reglist;
> >>+
> >>+	reglist = smia_reglist_find_type(&et8ek8_smia_meta_reglist,
> >>+					 SMIA_REGLIST_MODE);
> >>+	format = __et8ek8_get_pad_format(sensor, fh->pad, 0, V4L2_SUBDEV_FORMAT_TRY);
> >>+	smia_reglist_to_mbus(reglist, format);
> >>+
> >>+	return et8ek8_set_power(sd, true);
> >>+}
> >>+
> >>+static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> >>+{
> >>+	return et8ek8_set_power(sd, false);
> >>+}
> >>+
> >>+static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
> >>+	.s_stream = et8ek8_s_stream,
> >>+	.g_frame_interval = et8ek8_get_frame_interval,
> >>+	.s_frame_interval = et8ek8_set_frame_interval,
> >>+};
> >>+
> >>+static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
> >>+	.s_power = et8ek8_set_power,
> >>+};
> >>+
> >>+static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
> >>+	.enum_mbus_code = et8ek8_enum_mbus_code,
> >>+        .enum_frame_size = et8ek8_enum_frame_size,
> >>+        .enum_frame_interval = et8ek8_enum_frame_ival,
> >>+	.get_fmt = et8ek8_get_pad_format,
> >>+	.set_fmt = et8ek8_set_pad_format,
> >>+};
> >>+
> >>+static const struct v4l2_subdev_ops et8ek8_ops = {
> >>+	.core = &et8ek8_core_ops,
> >>+	.video = &et8ek8_video_ops,
> >>+	.pad = &et8ek8_pad_ops,
> >>+};
> >>+
> >>+static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
> >>+	.registered = et8ek8_registered,
> >>+	.open = et8ek8_open,
> >>+	.close = et8ek8_close,
> >>+};
> >>+
> >>+/* --------------------------------------------------------------------------
> >>+ * I2C driver
> >>+ */
> >>+#ifdef CONFIG_PM
> >>+
> >>+static int et8ek8_suspend(struct device *dev)
> >>+{
> >>+	struct i2c_client *client = to_i2c_client(dev);
> >>+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+
> >>+	if (!sensor->power_count)
> >>+		return 0;
> >>+
> >>+	return __et8ek8_set_power(sensor, false);
> >>+}
> >>+
> >>+static int et8ek8_resume(struct device *dev)
> >>+{
> >>+	struct i2c_client *client = to_i2c_client(dev);
> >>+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+
> >>+	if (!sensor->power_count)
> >>+		return 0;
> >>+
> >>+	return __et8ek8_set_power(sensor, true);
> >>+}
> >>+
> >>+static struct dev_pm_ops et8ek8_pm_ops = {
> >>+	.suspend	= et8ek8_suspend,
> >>+	.resume		= et8ek8_resume,
> >>+};
> >>+
> >>+#else
> >>+
> >>+#define et8ek8_pm_ops	NULL
> >>+
> >>+#endif /* CONFIG_PM */
> >>+
> >>+static int et8ek8_probe(struct i2c_client *client,
> >>+			const struct i2c_device_id *devid)
> >>+{
> >>+	struct et8ek8_sensor *sensor;
> >>+	int ret;
> >>+
> >>+	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
> >>+	if (!sensor)
> >>+		return -ENOMEM;
> >>+
> >>+	sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
> >>+	if (IS_ERR(sensor->reset)) {
> >>+		dev_dbg(&client->dev, "could not request reset gpio\n");
> >>+		return PTR_ERR(sensor->reset);;
> >>+	}
> >>+
> >>+	sensor->vana = devm_regulator_get(&client->dev, "vana");
> >>+	if (IS_ERR(sensor->vana)) {
> >>+		dev_err(&client->dev, "could not get regulator for vana\n");
> >>+		return PTR_ERR(sensor->vana);
> >>+	}
> >>+
> >>+	sensor->ext_clk = devm_clk_get(&client->dev, "extclk");
> >>+	if (IS_ERR(sensor->ext_clk)) {
> >>+		dev_err(&client->dev, "could not get clock\n");
> >>+		return PTR_ERR(sensor->ext_clk);
> >>+	}
> >>+
> >>+	mutex_init(&sensor->power_lock);
> >>+
> >>+	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
> >>+	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> >>+	sensor->subdev.internal_ops = &et8ek8_internal_ops;
> >>+
> >>+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> >>+	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
> >>+	if (ret < 0) {
> >>+		dev_err(&client->dev, "media entity init failed!\n");
> >>+		return ret;
> >>+	}
> >>+
> >>+	ret = v4l2_async_register_subdev(&sensor->subdev);
> >>+	if (ret < 0) {
> >>+		media_entity_cleanup(&sensor->subdev.entity);
> >>+		return ret;
> >>+	}
> >>+
> >>+	dev_dbg(&client->dev, "initialized!\n");
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static int __exit et8ek8_remove(struct i2c_client *client)
> >>+{
> >>+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> >>+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> >>+
> >>+	if (sensor->power_count) {
> >>+		gpiod_set_value(sensor->reset, 0);
> >>+		clk_disable_unprepare(sensor->ext_clk);
> >>+		sensor->power_count = 0;
> >>+	}
> >>+
> >>+	v4l2_device_unregister_subdev(&sensor->subdev);
> >>+	device_remove_file(&client->dev, &dev_attr_priv_mem);
> >>+	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
> >>+	media_entity_cleanup(&sensor->subdev.entity);
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static const struct of_device_id et8ek8_of_table[] = {
> >>+	{ .compatible = "toshiba,et8ek8" },
> >>+	{ },
> >>+};
> >>+
> >>+static const struct i2c_device_id et8ek8_id_table[] = {
> >>+	{ ET8EK8_NAME, 0 },
> >>+	{ }
> >>+};
> >>+MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
> >>+
> >>+static struct i2c_driver et8ek8_i2c_driver = {
> >>+	.driver		= {
> >>+		.name	= ET8EK8_NAME,
> >>+		.pm	= &et8ek8_pm_ops,
> >>+		.of_match_table	= et8ek8_of_table,
> >>+	},
> >>+	.probe		= et8ek8_probe,
> >>+	.remove		= __exit_p(et8ek8_remove),
> >>+	.id_table	= et8ek8_id_table,
> >>+};
> >>+
> >>+module_i2c_driver(et8ek8_i2c_driver);
> >>+
> >>+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
> >
> >s/nokia.com/iki.fi/ please.
> >
> 
> ok
> 
> >>+MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
> >>+MODULE_LICENSE("GPL");
> >
> 
> Lets see if I understand correctly what needs to be done:
> 
> 1. Replace custom controls with standart controls where possible
> 2. Move the used code from smia_reglist to et8ek8 driver, drop the remnants.

Yeah.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-27 16:59             ` Sebastian Reichel
@ 2016-05-02  7:06               ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-05-02  7:06 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

Hi!

> On Wed, Apr 27, 2016 at 06:45:29PM +0200, Pavel Machek wrote:
> > > I don't have pre-production N900s. The phone I use for development
> > > is HW revision 2101 with Finish keyboard layout. Apart from that
> > > I have my productive phone, which is rev 2204 with German layout.
> > 
> > How do you check hw revision?
> 
> 0xFFFF tells you the HW revision (e.g. when executed with -I, but
> also during normal kernel loading operation). Apart from that there
> is /proc/cpuinfo.

/proc/cpuinfo says revision : 0000. 0xFFFF says:

Initializing NOLO...
Device: RX-51
HW revision: 2101
NOLO version: 1.4.14

... So I seem to have 2101. Ok, and the prepared initrd seems to work
for me. (Thanks!)

length: 640256 offset: 4501504 timestamp type: monotonic
Buffer 7 mapped at address 0xb68fb000.
Press enter to start capture

0 (0) [-] 0 640256 bytes 532.452761 532.452913 28.544 fps
1 (1) [-] 1 640256 bytes 532.518129 532.518221 15.298 fps

/camera$ ./yavta --capture=8 --pause --skip 0 --format UYVY --size
656x488 /dev/video6 --file=/tmp/delme#
/camera$ md5sum /tmp/delme*
541b8dfeab4f56824274aa7b61c88dcf  /tmp/delme000000
9b685af72dedd12ed519bdc68475e671  /tmp/delme000001
67128a3af14be16c94f7e26c365f2b21  /tmp/delme000002
0d411d0052710b15af05b6a7e51a43f1  /tmp/delme000003
74a44a4a0f33354b86af6e419e624308  /tmp/delme000004
38842dc2e7cdc1163012f4811f2c293c  /tmp/delme000005
78b88cfe381560fd6e3b8647ec553f10  /tmp/delme000006
58d0fc91cc0d1d3cee1a900eb8ee5094  /tmp/delme000007

So yes, it seems to work here. At least as a random generator :-).

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-01 13:41         ` Sakari Ailus
@ 2016-05-03 14:50           ` Ivaylo Dimitrov
  2016-05-22 10:07             ` Ivaylo Dimitrov
                               ` (2 more replies)
  0 siblings, 3 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-03 14:50 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media, Ivaylo Dimitrov

The sensor is found in Nokia N900 main camera

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 .../bindings/media/i2c/toshiba,et8ek8.txt          |   53 +
 drivers/media/i2c/Kconfig                          |    1 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/et8ek8/Kconfig                   |    6 +
 drivers/media/i2c/et8ek8/Makefile                  |    2 +
 drivers/media/i2c/et8ek8/et8ek8_driver.c           | 1711 ++++++++++++++++++++
 drivers/media/i2c/et8ek8/et8ek8_mode.c             |  591 +++++++
 drivers/media/i2c/et8ek8/et8ek8_reg.h              |  100 ++
 include/uapi/linux/v4l2-controls.h                 |    5 +
 9 files changed, 2470 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
 create mode 100644 drivers/media/i2c/et8ek8/Kconfig
 create mode 100644 drivers/media/i2c/et8ek8/Makefile
 create mode 100644 drivers/media/i2c/et8ek8/et8ek8_driver.c
 create mode 100644 drivers/media/i2c/et8ek8/et8ek8_mode.c
 create mode 100644 drivers/media/i2c/et8ek8/et8ek8_reg.h

diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
new file mode 100644
index 0000000..55f712c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
@@ -0,0 +1,53 @@
+Toshiba et8ek8 5MP sensor
+
+Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device
+
+More detailed documentation can be found in
+Documentation/devicetree/bindings/media/video-interfaces.txt .
+
+
+Mandatory properties
+--------------------
+
+- compatible: "toshiba,et8ek8"
+- reg: I2C address (0x3e, or an alternative address)
+- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor
+  dependent).
+- clocks: External clock to the sensor
+- clock-frequency: Frequency of the external clock to the sensor
+
+
+Optional properties
+-------------------
+
+- reset-gpios: XSHUTDOWN GPIO
+
+
+Endpoint node mandatory properties
+----------------------------------
+
+- clock-lanes: <0>
+- data-lanes: <1..n>
+- remote-endpoint: A phandle to the bus receiver's endpoint node.
+
+
+Example
+-------
+
+&i2c3 {
+	clock-frequency = <400000>;
+
+	cam1: camera@3e {
+		compatible = "toshiba,et8ek8";
+		reg = <0x3e>;
+		vana-supply = <&vaux4>;
+		clocks = <&isp 0>;
+		clock-frequency = <9600000>;
+		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
+		port {
+			csi_cam1: endpoint {
+				remote-endpoint = <&csi_out1>;
+			};
+		};
+	};
+};
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 993dc50..e964787 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -629,6 +629,7 @@ config VIDEO_S5K5BAF
 	  camera sensor with an embedded SoC image signal processor.
 
 source "drivers/media/i2c/smiapp/Kconfig"
+source "drivers/media/i2c/et8ek8/Kconfig"
 
 config VIDEO_S5C73M3
 	tristate "Samsung S5C73M3 sensor support"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 94f2c99..907b180 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -2,6 +2,7 @@ msp3400-objs	:=	msp3400-driver.o msp3400-kthreads.o
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
 
 obj-$(CONFIG_VIDEO_SMIAPP)	+= smiapp/
+obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8/
 obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 obj-y				+= soc_camera/
diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig
new file mode 100644
index 0000000..1439936
--- /dev/null
+++ b/drivers/media/i2c/et8ek8/Kconfig
@@ -0,0 +1,6 @@
+config VIDEO_ET8EK8
+	tristate "ET8EK8 camera sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
+	  It is used for example in Nokia N900 (RX-51).
diff --git a/drivers/media/i2c/et8ek8/Makefile b/drivers/media/i2c/et8ek8/Makefile
new file mode 100644
index 0000000..66d1b7d
--- /dev/null
+++ b/drivers/media/i2c/et8ek8/Makefile
@@ -0,0 +1,2 @@
+et8ek8-objs			+= et8ek8_mode.o et8ek8_driver.o
+obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8.o
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
new file mode 100644
index 0000000..1eaef78
--- /dev/null
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -0,0 +1,1711 @@
+/*
+ * et8ek8_driver.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
+ *
+ * This driver is based on the Micron MT9T012 camera imager driver
+ * (C) Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/version.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "et8ek8_reg.h"
+
+#define ET8EK8_NAME		"et8ek8"
+#define ET8EK8_PRIV_MEM_SIZE	128
+
+#define ET8EK8_CID_USER_FRAME_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 1)
+#define ET8EK8_CID_USER_FRAME_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 2)
+#define ET8EK8_CID_USER_VISIBLE_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 3)
+#define ET8EK8_CID_USER_VISIBLE_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 4)
+#define ET8EK8_CID_USER_SENSITIVITY	(V4L2_CID_USER_ET8EK8_BASE + 5)
+
+struct et8ek8_sensor {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt format;
+	struct gpio_desc *reset;
+	struct regulator *vana;
+	struct clk *ext_clk;
+	u32 xclk_freq;
+
+	u16 version;
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *pixel_rate;
+	struct et8ek8_reglist *current_reglist;
+
+	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
+
+	struct mutex power_lock;
+	int power_count;
+};
+
+#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
+
+enum et8ek8_versions {
+	ET8EK8_REV_1 = 0x0001,
+	ET8EK8_REV_2,
+};
+
+/*
+ * This table describes what should be written to the sensor register
+ * for each gain value. The gain(index in the table) is in terms of
+ * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
+ * the *analog gain, [1] in the digital gain
+ *
+ * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
+ */
+static struct et8ek8_gain {
+	u16 analog;
+	u16 digital;
+} const et8ek8_gain_table[] = {
+	{ 32,    0},  /* x1 */
+	{ 34,    0},
+	{ 37,    0},
+	{ 39,    0},
+	{ 42,    0},
+	{ 45,    0},
+	{ 49,    0},
+	{ 52,    0},
+	{ 56,    0},
+	{ 60,    0},
+	{ 64,    0},  /* x2 */
+	{ 69,    0},
+	{ 74,    0},
+	{ 79,    0},
+	{ 84,    0},
+	{ 91,    0},
+	{ 97,    0},
+	{104,    0},
+	{111,    0},
+	{119,    0},
+	{128,    0},  /* x4 */
+	{137,    0},
+	{147,    0},
+	{158,    0},
+	{169,    0},
+	{181,    0},
+	{194,    0},
+	{208,    0},
+	{223,    0},
+	{239,    0},
+	{256,    0},  /* x8 */
+	{256,   73},
+	{256,  152},
+	{256,  236},
+	{256,  327},
+	{256,  424},
+	{256,  528},
+	{256,  639},
+	{256,  758},
+	{256,  886},
+	{256, 1023},  /* x16 */
+};
+
+/* Register definitions */
+#define REG_REVISION_NUMBER_L	0x1200
+#define REG_REVISION_NUMBER_H	0x1201
+
+#define PRIV_MEM_START_REG	0x0008
+#define PRIV_MEM_WIN_SIZE	8
+
+#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
+
+#define USE_CRC			1
+
+/*
+ *
+ * Register access helpers
+ *
+ */
+
+/*
+ * Read a 8/16/32-bit i2c register.  The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int et8ek8_i2c_read_reg(struct i2c_client *client, u16 data_length,
+			       u16 reg, u32 *val)
+{
+	int r;
+	struct i2c_msg msg[1];
+	unsigned char data[4];
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
+		return -EINVAL;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 2;
+	msg->buf = data;
+
+	/* high byte goes out first */
+	data[0] = (u8) (reg >> 8);
+	data[1] = (u8) (reg & 0xff);
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		goto err;
+
+	msg->len = data_length;
+	msg->flags = I2C_M_RD;
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		goto err;
+
+	*val = 0;
+	/* high byte comes first */
+	if (data_length == ET8EK8_REG_8BIT)
+		*val = data[0];
+	else
+		*val = (data[0] << 8) + data[1];
+
+	return 0;
+
+err:
+	dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r);
+
+	return r;
+}
+
+static void et8ek8_i2c_create_msg(struct i2c_client *client, u16 len, u16 reg,
+				  u32 val, struct i2c_msg *msg,
+				  unsigned char *buf)
+{
+	msg->addr = client->addr;
+	msg->flags = 0; /* Write */
+	msg->len = 2 + len;
+	msg->buf = buf;
+
+	/* high byte goes out first */
+	buf[0] = (u8) (reg >> 8);
+	buf[1] = (u8) (reg & 0xff);
+
+	switch (len) {
+	case ET8EK8_REG_8BIT:
+		buf[2] = (u8) (val) & 0xff;
+		break;
+	case ET8EK8_REG_16BIT:
+		buf[2] = (u8) (val >> 8) & 0xff;
+		buf[3] = (u8) (val & 0xff);
+		break;
+	case ET8EK8_REG_32BIT:
+		buf[2] = (u8) (val >> 24) & 0xff;
+		buf[3] = (u8) (val >> 16) & 0xff;
+		buf[4] = (u8) (val >> 8) & 0xff;
+		buf[5] = (u8) (val & 0xff);
+		break;
+	default:
+		BUG();
+	}
+}
+
+/*
+ * A buffered write method that puts the wanted register write
+ * commands in a message list and passes the list to the i2c framework
+ */
+static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
+					  const struct et8ek8_reg *wnext,
+					  int cnt)
+{
+	/* FIXME: check how big cnt is */
+	struct i2c_msg msg[cnt];
+	unsigned char data[cnt][6];
+	int wcnt = 0;
+	u16 reg, data_length;
+	u32 val;
+
+	/* Create new write messages for all writes */
+	while (wcnt < cnt) {
+		data_length = wnext->type;
+		reg = wnext->reg;
+		val = wnext->val;
+		wnext++;
+
+		et8ek8_i2c_create_msg(client, data_length, reg,
+				    val, &msg[wcnt], &data[wcnt][0]);
+
+		/* Update write count */
+		wcnt++;
+	}
+
+	/* Now we send everything ... */
+	return i2c_transfer(client->adapter, msg, wcnt);
+}
+
+/*
+ * Write a list of registers to i2c device.
+ *
+ * The list of registers is terminated by ET8EK8_REG_TERM.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int et8ek8_i2c_write_regs(struct i2c_client *client,
+				 const struct et8ek8_reg reglist[])
+{
+	int r, cnt = 0;
+	const struct et8ek8_reg *next, *wnext;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (reglist == NULL)
+		return -EINVAL;
+
+	/* Initialize list pointers to the start of the list */
+	next = wnext = reglist;
+
+	do {
+		/*
+		 * We have to go through the list to figure out how
+		 * many regular writes we have in a row
+		 */
+		while (next->type != ET8EK8_REG_TERM
+		       && next->type != ET8EK8_REG_DELAY) {
+			/*
+			 * Here we check that the actual length fields
+			 * are valid
+			 */
+			if (next->type != ET8EK8_REG_8BIT
+			    &&  next->type != ET8EK8_REG_16BIT) {
+				dev_err(&client->dev,
+					"Invalid value on entry %d 0x%x\n",
+					cnt, next->type);
+				return -EINVAL;
+			}
+
+			/*
+			 * Increment count of successive writes and
+			 * read pointer
+			 */
+			cnt++;
+			next++;
+		}
+
+		/* Now we start writing ... */
+		r = et8ek8_i2c_buffered_write_regs(client, wnext, cnt);
+
+		/* ... and then check that everything was OK */
+		if (r < 0) {
+			dev_err(&client->dev, "i2c transfer error !!!\n");
+			return r;
+		}
+
+		/*
+		 * If we ran into a sleep statement when going through
+		 * the list, this is where we snooze for the required time
+		 */
+		if (next->type == ET8EK8_REG_DELAY) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(msecs_to_jiffies(next->val));
+			/*
+			 * ZZZ ...
+			 * Update list pointers and cnt and start over ...
+			 */
+			next++;
+			wnext = next;
+			cnt = 0;
+		}
+	} while (next->type != ET8EK8_REG_TERM);
+
+	return 0;
+}
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int et8ek8_i2c_write_reg(struct i2c_client *client, u16 data_length,
+				u16 reg, u32 val)
+{
+	int r;
+	struct i2c_msg msg[1];
+	unsigned char data[6];
+
+	if (!client->adapter)
+		return -ENODEV;
+	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
+		return -EINVAL;
+
+	et8ek8_i2c_create_msg(client, data_length, reg, val, msg, data);
+
+	r = i2c_transfer(client->adapter, msg, 1);
+	if (r < 0)
+		dev_err(&client->dev,
+			"wrote 0x%x to offset 0x%x error %d\n", val, reg, r);
+	else
+		r = 0; /* on success i2c_transfer() return messages trasfered */
+
+	return r;
+}
+
+static struct et8ek8_reglist *et8ek8_reglist_find_type(
+		struct et8ek8_meta_reglist *meta,
+		u16 type)
+{
+	struct et8ek8_reglist **next = &meta->reglist[0].ptr;
+
+	while (*next) {
+		if ((*next)->type == type)
+			return *next;
+
+		next++;
+	}
+
+	return NULL;
+}
+
+static int et8ek8_i2c_reglist_find_write(struct i2c_client *client,
+					 struct et8ek8_meta_reglist *meta,
+					 u16 type)
+{
+	struct et8ek8_reglist *reglist;
+
+	reglist = et8ek8_reglist_find_type(meta, type);
+	if (IS_ERR(reglist))
+		return PTR_ERR(reglist);
+
+	return et8ek8_i2c_write_regs(client, reglist->regs);
+}
+
+static struct et8ek8_reglist **et8ek8_reglist_first(
+		struct et8ek8_meta_reglist *meta)
+{
+	return &meta->reglist[0].ptr;
+}
+
+static void et8ek8_reglist_to_mbus(const struct et8ek8_reglist *reglist,
+				   struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->width = reglist->mode.window_width;
+	fmt->height = reglist->mode.window_height;
+
+	if (reglist->mode.pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
+		fmt->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
+	else
+		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+}
+
+static struct et8ek8_reglist *et8ek8_reglist_find_mode_fmt(
+		struct et8ek8_meta_reglist *meta,
+		struct v4l2_mbus_framefmt *fmt)
+{
+	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
+	struct et8ek8_reglist *best_match = NULL;
+	struct et8ek8_reglist *best_other = NULL;
+	struct v4l2_mbus_framefmt format;
+	unsigned int max_dist_match = (unsigned int)-1;
+	unsigned int max_dist_other = (unsigned int)-1;
+
+	/* Find the mode with the closest image size. The distance between
+	 * image sizes is the size in pixels of the non-overlapping regions
+	 * between the requested size and the frame-specified size.
+	 *
+	 * Store both the closest mode that matches the requested format, and
+	 * the closest mode for all other formats. The best match is returned
+	 * if found, otherwise the best mode with a non-matching format is
+	 * returned.
+	 */
+	for (; *list; list++) {
+		unsigned int dist;
+
+		if ((*list)->type != ET8EK8_REGLIST_MODE)
+			continue;
+
+		et8ek8_reglist_to_mbus(*list, &format);
+
+		dist = min(fmt->width, format.width)
+		     * min(fmt->height, format.height);
+		dist = format.width * format.height
+		     + fmt->width * fmt->height - 2 * dist;
+
+
+		if (fmt->code == format.code) {
+			if (dist < max_dist_match || best_match == NULL) {
+				best_match = *list;
+				max_dist_match = dist;
+			}
+		} else {
+			if (dist < max_dist_other || best_other == NULL) {
+				best_other = *list;
+				max_dist_other = dist;
+			}
+		}
+	}
+
+	return best_match ? best_match : best_other;
+}
+
+#define TIMEPERFRAME_AVG_FPS(t)						\
+	(((t).denominator + ((t).numerator >> 1)) / (t).numerator)
+
+static struct et8ek8_reglist *et8ek8_reglist_find_mode_ival(
+		struct et8ek8_meta_reglist *meta,
+		struct et8ek8_reglist *current_reglist,
+		struct v4l2_fract *timeperframe)
+{
+	int fps = TIMEPERFRAME_AVG_FPS(*timeperframe);
+	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
+	struct et8ek8_mode *current_mode = &current_reglist->mode;
+
+	for (; *list; list++) {
+		struct et8ek8_mode *mode = &(*list)->mode;
+
+		if ((*list)->type != ET8EK8_REGLIST_MODE)
+			continue;
+
+		if (mode->window_width != current_mode->window_width
+		    || mode->window_height != current_mode->window_height)
+			continue;
+
+		if (TIMEPERFRAME_AVG_FPS(mode->timeperframe) == fps)
+			return *list;
+	}
+
+	return NULL;
+}
+
+static int et8ek8_reglist_cmp(const void *a, const void *b)
+{
+	const struct et8ek8_reglist **list1 = (const struct et8ek8_reglist **)a,
+		**list2 = (const struct et8ek8_reglist **)b;
+
+	/* Put real modes in the beginning. */
+	if ((*list1)->type == ET8EK8_REGLIST_MODE &&
+	    (*list2)->type != ET8EK8_REGLIST_MODE)
+		return -1;
+	if ((*list1)->type != ET8EK8_REGLIST_MODE &&
+	    (*list2)->type == ET8EK8_REGLIST_MODE)
+		return 1;
+
+	/* Descending width. */
+	if ((*list1)->mode.window_width > (*list2)->mode.window_width)
+		return -1;
+	if ((*list1)->mode.window_width < (*list2)->mode.window_width)
+		return 1;
+
+	if ((*list1)->mode.window_height > (*list2)->mode.window_height)
+		return -1;
+	if ((*list1)->mode.window_height < (*list2)->mode.window_height)
+		return 1;
+
+	return 0;
+}
+
+static int et8ek8_reglist_import(struct i2c_client *client,
+				 struct et8ek8_meta_reglist *meta)
+{
+	uintptr_t nlists = 0;
+
+	if (meta->magic != ET8EK8_MAGIC) {
+		dev_err(&client->dev,
+			"invalid camera sensor firmware (0x%08X)\n",
+			meta->magic);
+		return -EILSEQ;
+	}
+
+	dev_info(&client->dev, "meta_reglist version %s\n", meta->version);
+
+	while (meta->reglist[nlists].ptr != NULL)
+		nlists++;
+
+	if (!nlists)
+		return -EINVAL;
+
+	sort(&meta->reglist[0].ptr, nlists, sizeof(meta->reglist[0].ptr),
+	     et8ek8_reglist_cmp, NULL);
+
+	nlists = 0;
+	while (meta->reglist[nlists].ptr != NULL) {
+		struct et8ek8_reglist *list;
+
+		list = meta->reglist[nlists].ptr;
+
+		dev_dbg(&client->dev,
+		       "%s: type %d\tw %d\th %d\tfmt %x\tival %d/%d\tptr %p\n",
+		       __func__,
+		       list->type,
+		       list->mode.window_width, list->mode.window_height,
+		       list->mode.pixel_format,
+		       list->mode.timeperframe.numerator,
+		       list->mode.timeperframe.denominator,
+		       (void *)meta->reglist[nlists].ptr);
+
+		nlists++;
+	}
+
+	return 0;
+}
+
+/*
+ * Return time of one row in microseconds, .8 fixed point format.
+ * If the sensor is not set to any mode, return zero.
+ */
+static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
+{
+	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
+	unsigned int rt;	/* Row time in .8 fixed point */
+
+	if (!sensor->current_reglist)
+		return 0;
+
+	clock = sensor->current_reglist->mode.pixel_clock;
+	clock = (clock + (1 << 9)) >> 10;
+	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
+	rt = (rt + (clock >> 1)) / clock;
+
+	return rt;
+}
+
+/*
+ * Convert exposure time `us' to rows. Modify `us' to make it to
+ * correspond to the actual exposure time.
+ */
+static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
+{
+	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
+	unsigned int rt;	/* Row time in .8 fixed point */
+
+	/* Assume that the maximum exposure time is at most ~8 s,
+	 * and the maximum width (with blanking) ~8000 pixels.
+	 * The formula here is in principle as simple as
+	 *    rows = exptime / 1e6 / width * pixel_clock
+	 * but to get accurate results while coping with value ranges,
+	 * have to do some fixed point math.
+	 */
+
+	rt = et8ek8_get_row_time(sensor);
+	rows = ((*us << 8) + (rt >> 1)) / rt;
+
+	if (rows > sensor->current_reglist->mode.max_exp)
+		rows = sensor->current_reglist->mode.max_exp;
+
+	/* Set the exposure time to the rounded value */
+	*us = (rt * rows + (1 << 7)) >> 8;
+
+	return rows;
+}
+
+/*
+ * Convert exposure time in rows to microseconds
+ */
+static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
+{
+	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
+}
+
+/* Called to change the V4L2 gain control value. This function
+ * rounds and clamps the given value and updates the V4L2 control value.
+ * If power is on, also updates the sensor analog and digital gains.
+ * gain is in 0.1 EV (exposure value) units.
+ */
+static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	struct et8ek8_gain new;
+	int r;
+
+	new = et8ek8_gain_table[gain];
+
+	/* FIXME: optimise I2C writes! */
+	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
+				0x124a, new.analog >> 8);
+	if (r)
+		return r;
+	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
+				0x1249, new.analog & 0xff);
+	if (r)
+		return r;
+
+	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
+				0x124d, new.digital >> 8);
+	if (r)
+		return r;
+	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
+				0x124c, new.digital & 0xff);
+
+	return r;
+}
+
+static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
+
+	/* Values for normal mode */
+	cbh_mode = 0;
+	cbv_mode = 0;
+	tp_mode  = 0;
+	din_sw   = 0x00;
+	r1420    = 0xF0;
+
+	if (mode != 0) {
+		/* Test pattern mode */
+		if (mode < 5) {
+			cbh_mode = 1;
+			cbv_mode = 1;
+			tp_mode  = mode + 3;
+		} else {
+			cbh_mode = 0;
+			cbv_mode = 0;
+			tp_mode  = mode - 4 + 3;
+		}
+		din_sw   = 0x01;
+		r1420    = 0xE0;
+	}
+
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x111B,
+				    tp_mode << 4);
+	if (rval)
+		goto out;
+
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1121,
+				    cbh_mode << 7);
+	if (rval)
+		goto out;
+
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1124,
+				    cbv_mode << 7);
+	if (rval)
+		goto out;
+
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x112C, din_sw);
+	if (rval)
+		goto out;
+
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1420, r1420);
+	if (rval)
+		goto out;
+
+out:
+	return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct et8ek8_sensor *sensor =
+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+	const struct et8ek8_mode *mode = &sensor->current_reglist->mode;
+
+	switch (ctrl->id) {
+	case ET8EK8_CID_USER_FRAME_WIDTH:
+		ctrl->cur.val = mode->width;
+		break;
+	case ET8EK8_CID_USER_FRAME_HEIGHT:
+		ctrl->cur.val = mode->height;
+		break;
+	case ET8EK8_CID_USER_VISIBLE_WIDTH:
+		ctrl->cur.val = mode->window_width;
+		break;
+	case ET8EK8_CID_USER_VISIBLE_HEIGHT:
+		ctrl->cur.val = mode->window_height;
+		break;
+	case ET8EK8_CID_USER_SENSITIVITY:
+		ctrl->cur.val = mode->sensitivity;
+		break;
+	}
+
+	return 0;
+}
+
+static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct et8ek8_sensor *sensor =
+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+	int uninitialized_var(rows);
+
+	if (ctrl->id == V4L2_CID_EXPOSURE)
+		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		return et8ek8_set_gain(sensor, ctrl->val);
+
+	case V4L2_CID_EXPOSURE:
+		return et8ek8_i2c_write_reg(client, ET8EK8_REG_16BIT, 0x1243,
+					    swab16(rows));
+
+	case V4L2_CID_TEST_PATTERN:
+		return et8ek8_set_test_pattern(sensor, ctrl->val);
+
+	case V4L2_CID_PIXEL_RATE:
+		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
+	.g_volatile_ctrl = et8ek8_get_ctrl,
+	.s_ctrl = et8ek8_set_ctrl,
+};
+
+static const char * const et8ek8_test_pattern_menu[] = {
+	"Normal",
+	"Vertical colorbar",
+	"Horizontal colorbar",
+	"Scale",
+	"Ramp",
+	"Small vertical colorbar",
+	"Small horizontal colorbar",
+	"Small scale",
+	"Small ramp",
+};
+
+static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
+	{
+		.id		= V4L2_CID_USER_ET8EK8_BASE,
+		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
+		.name		= "et8ek8 driver controls",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_WRITE_ONLY,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= ET8EK8_CID_USER_FRAME_WIDTH,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Frame width",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= ET8EK8_CID_USER_FRAME_HEIGHT,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Frame height",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= ET8EK8_CID_USER_VISIBLE_WIDTH,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Visible width",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= ET8EK8_CID_USER_VISIBLE_HEIGHT,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Visible height",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+		.ops		= &et8ek8_ctrl_ops,
+		.id		= ET8EK8_CID_USER_SENSITIVITY,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Sensitivity",
+		.min		= 0,
+		.max		= 0,
+		.step		= 1,
+		.def		= 0,
+		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
+				  V4L2_CTRL_FLAG_VOLATILE,
+	},
+};
+
+static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
+{
+	unsigned int i;
+	u32 min, max;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
+			       ARRAY_SIZE(et8ek8_ctrls) + 2);
+
+	/* V4L2_CID_GAIN */
+	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
+			  1, 0);
+
+	/* V4L2_CID_EXPOSURE */
+	min = et8ek8_exposure_rows_to_us(sensor, 1);
+	max = et8ek8_exposure_rows_to_us(sensor,
+				sensor->current_reglist->mode.max_exp);
+	sensor->exposure =
+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+				  V4L2_CID_EXPOSURE, min, max, min, max);
+
+	/* V4L2_CID_PIXEL_RATE */
+	sensor->pixel_rate =
+		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
+
+	/* V4L2_CID_TEST_PATTERN */
+	v4l2_ctrl_new_std_menu_items(&sensor->ctrl_handler,
+				     &et8ek8_ctrl_ops, V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
+				     0, 0, et8ek8_test_pattern_menu);
+
+	/* V4L2_CID_USER_ET8EK8_BASE */
+	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
+		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
+				     NULL);
+
+	if (sensor->ctrl_handler.error)
+		return sensor->ctrl_handler.error;
+
+	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
+	return 0;
+}
+
+static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_ctrl *ctrl = sensor->exposure;
+	struct et8ek8_mode *mode = &sensor->current_reglist->mode;
+	u32 min, max, pixel_rate;
+	static const int S = 8;
+
+	min = et8ek8_exposure_rows_to_us(sensor, 1);
+	max = et8ek8_exposure_rows_to_us(sensor, mode->max_exp);
+
+	/*
+	 * Calculate average pixel clock per line. Assume buffers can spread
+	 * the data over horizontal blanking time. Rounding upwards.
+	 * Formula taken from stock Nokia N900 kernel
+	 */
+	pixel_rate = ((mode->pixel_clock + (1 << S) - 1) >> S) + mode->width;
+	pixel_rate = mode->window_width * (pixel_rate - 1) / mode->width;
+
+	v4l2_ctrl_lock(ctrl);
+	ctrl->minimum = min;
+	ctrl->maximum = max;
+	ctrl->step = min;
+	ctrl->default_value = max;
+	ctrl->val = max;
+	ctrl->cur.val = max;
+	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, pixel_rate << S);
+	v4l2_ctrl_unlock(ctrl);
+}
+
+static int et8ek8_configure(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_subdev *subdev = &sensor->subdev;
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int rval;
+
+	rval = et8ek8_i2c_write_regs(client, sensor->current_reglist->regs);
+	if (rval)
+		goto fail;
+
+	/* Controls set while the power to the sensor is turned off are saved
+	 * but not applied to the hardware. Now that we're about to start
+	 * streaming apply all the current values to the hardware.
+	 */
+	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
+	if (rval)
+		goto fail;
+
+	return 0;
+
+fail:
+	dev_err(&client->dev, "sensor configuration failed\n");
+	return rval;
+}
+
+static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0xb0);
+}
+
+static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0x30);
+}
+
+static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	int ret;
+
+	if (!streaming)
+		return et8ek8_stream_off(sensor);
+
+	ret = et8ek8_configure(sensor);
+	if (ret < 0)
+		return ret;
+
+	return et8ek8_stream_on(sensor);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int et8ek8_power_off(struct et8ek8_sensor *sensor)
+{
+	int rval;
+
+	gpiod_set_value(sensor->reset, 0);
+	udelay(1);
+
+	clk_disable_unprepare(sensor->ext_clk);
+
+	rval = regulator_disable(sensor->vana);
+	return rval;
+}
+
+static int et8ek8_power_on(struct et8ek8_sensor *sensor)
+{
+	struct v4l2_subdev *subdev = &sensor->subdev;
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	unsigned int xclk_freq;
+	int val, rval;
+
+	rval = regulator_enable(sensor->vana);
+	if (rval) {
+		dev_err(&client->dev, "failed to enable vana regulator\n");
+		return rval;
+	}
+
+	if (sensor->current_reglist)
+		xclk_freq = sensor->current_reglist->mode.ext_clock;
+	else
+		xclk_freq = sensor->xclk_freq;
+
+	rval = clk_set_rate(sensor->ext_clk, xclk_freq);
+	if (rval < 0) {
+		dev_err(&client->dev, "unable to set extclk clock freq to %u\n",
+			xclk_freq);
+		goto out;
+	}
+	rval = clk_prepare_enable(sensor->ext_clk);
+	if (rval < 0) {
+		dev_err(&client->dev, "failed to enable extclk\n");
+		goto out;
+	}
+
+	if (rval)
+		goto out;
+
+	udelay(10); /* I wish this is a good value */
+
+	gpiod_set_value(sensor->reset, 1);
+
+	msleep(5000 * 1000 / xclk_freq + 1); /* Wait 5000 cycles */
+
+	rval = et8ek8_i2c_reglist_find_write(client, &meta_reglist,
+					     ET8EK8_REGLIST_POWERON);
+	if (rval)
+		goto out;
+
+#ifdef USE_CRC
+	rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT, 0x1263, &val);
+	if (rval)
+		goto out;
+#if USE_CRC /* TODO get crc setting from DT */
+	val |= BIT(4);
+#else
+	val &= ~BIT(4);
+#endif
+	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1263, val);
+	if (rval)
+		goto out;
+#endif
+
+out:
+	if (rval)
+		et8ek8_power_off(sensor);
+
+	return rval;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+#define MAX_FMTS 4
+static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct et8ek8_reglist **list =
+			et8ek8_reglist_first(&meta_reglist);
+	u32 pixelformat[MAX_FMTS];
+	int npixelformat = 0;
+
+	if (code->index >= MAX_FMTS)
+		return -EINVAL;
+
+	for (; *list; list++) {
+		struct et8ek8_mode *mode = &(*list)->mode;
+		int i;
+
+		if ((*list)->type != ET8EK8_REGLIST_MODE)
+			continue;
+
+		for (i = 0; i < npixelformat; i++) {
+			if (pixelformat[i] == mode->pixel_format)
+				break;
+		}
+		if (i != npixelformat)
+			continue;
+
+		if (code->index == npixelformat) {
+			if (mode->pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
+				code->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
+			else
+				code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+			return 0;
+		}
+
+		pixelformat[npixelformat] = mode->pixel_format;
+		npixelformat++;
+	}
+
+	return -EINVAL;
+}
+
+static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct et8ek8_reglist **list =
+			et8ek8_reglist_first(&meta_reglist);
+	struct v4l2_mbus_framefmt format;
+	int cmp_width = INT_MAX;
+	int cmp_height = INT_MAX;
+	int index = fse->index;
+
+	for (; *list; list++) {
+		if ((*list)->type != ET8EK8_REGLIST_MODE)
+			continue;
+
+		et8ek8_reglist_to_mbus(*list, &format);
+		if (fse->code != format.code)
+			continue;
+
+		/* Assume that the modes are grouped by frame size. */
+		if (format.width == cmp_width && format.height == cmp_height)
+			continue;
+
+		cmp_width = format.width;
+		cmp_height = format.height;
+
+		if (index-- == 0) {
+			fse->min_width = format.width;
+			fse->min_height = format.height;
+			fse->max_width = format.width;
+			fse->max_height = format.height;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct et8ek8_reglist **list =
+			et8ek8_reglist_first(&meta_reglist);
+	struct v4l2_mbus_framefmt format;
+	int index = fie->index;
+
+	for (; *list; list++) {
+		struct et8ek8_mode *mode = &(*list)->mode;
+
+		if ((*list)->type != ET8EK8_REGLIST_MODE)
+			continue;
+
+		et8ek8_reglist_to_mbus(*list, &format);
+		if (fie->code != format.code)
+			continue;
+
+		if (fie->width != format.width || fie->height != format.height)
+			continue;
+
+		if (index-- == 0) {
+			fie->interval = mode->timeperframe;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *
+__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
+			struct v4l2_subdev_pad_config *cfg,
+			unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &sensor->format;
+	default:
+		return NULL;
+	}
+}
+
+static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct v4l2_mbus_framefmt *format;
+	struct et8ek8_reglist *reglist;
+
+	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	reglist = et8ek8_reglist_find_mode_fmt(&meta_reglist,
+					     &fmt->format);
+	et8ek8_reglist_to_mbus(reglist, &fmt->format);
+	*format = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		sensor->current_reglist = reglist;
+		et8ek8_update_controls(sensor);
+	}
+
+	return 0;
+}
+
+static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	memset(fi, 0, sizeof(*fi));
+	fi->interval = sensor->current_reglist->mode.timeperframe;
+
+	return 0;
+}
+
+static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct et8ek8_reglist *reglist;
+
+	reglist = et8ek8_reglist_find_mode_ival(&meta_reglist,
+					      sensor->current_reglist,
+					      &fi->interval);
+
+	if (!reglist)
+		return -EINVAL;
+
+	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
+		return -EINVAL;
+
+	sensor->current_reglist = reglist;
+	et8ek8_update_controls(sensor);
+
+	return 0;
+}
+
+static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
+	unsigned int offset = 0;
+	u8 *ptr  = sensor->priv_mem;
+	int rval = 0;
+
+	/* Read the EEPROM window-by-window, each window 8 bytes */
+	do {
+		u8 buffer[PRIV_MEM_WIN_SIZE];
+		struct i2c_msg msg;
+		int bytes, i;
+		int ofs;
+
+		/* Set the current window */
+		rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x0001,
+					    0xe0 | (offset >> 3));
+		if (rval < 0)
+			goto out;
+
+		/* Wait for status bit */
+		for (i = 0; i < 1000; ++i) {
+			u32 status;
+
+			rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
+						   0x0003, &status);
+			if (rval < 0)
+				goto out;
+			if ((status & 0x08) == 0)
+				break;
+			usleep_range(1000, 2000);
+		};
+
+		if (i == 1000) {
+			rval = -EIO;
+			goto out;
+		}
+
+		/* Read window, 8 bytes at once, and copy to user space */
+		ofs = offset & 0x07;	/* Offset within this window */
+		bytes = length + ofs > 8 ? 8-ofs : length;
+		msg.addr = client->addr;
+		msg.flags = 0;
+		msg.len = 2;
+		msg.buf = buffer;
+		ofs += PRIV_MEM_START_REG;
+		buffer[0] = (u8)(ofs >> 8);
+		buffer[1] = (u8)(ofs & 0xFF);
+		rval = i2c_transfer(client->adapter, &msg, 1);
+		if (rval < 0)
+			goto out;
+		mdelay(ET8EK8_I2C_DELAY);
+		msg.addr = client->addr;
+		msg.len = bytes;
+		msg.flags = I2C_M_RD;
+		msg.buf = buffer;
+		memset(buffer, 0, sizeof(buffer));
+		rval = i2c_transfer(client->adapter, &msg, 1);
+		if (rval < 0)
+			goto out;
+		rval = 0;
+		memcpy(ptr, buffer, bytes);
+
+		length -= bytes;
+		offset += bytes;
+		ptr    += bytes;
+	} while (length > 0);
+
+out:
+	return rval;
+}
+
+static int et8ek8_dev_init(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int rval, rev_l, rev_h;
+
+	rval = et8ek8_power_on(sensor);
+	if (rval) {
+		dev_err(&client->dev, "could not power on\n");
+		return rval;
+	}
+
+	if (et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
+				REG_REVISION_NUMBER_L, &rev_l) != 0
+	    || et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
+				   REG_REVISION_NUMBER_H, &rev_h) != 0) {
+		dev_err(&client->dev,
+			"no et8ek8 sensor detected\n");
+		rval = -ENODEV;
+		goto out_poweroff;
+	}
+	sensor->version = (rev_h << 8) + rev_l;
+	if (sensor->version != ET8EK8_REV_1
+	    && sensor->version != ET8EK8_REV_2)
+		dev_info(&client->dev,
+			 "unknown version 0x%x detected, continuing anyway\n",
+			 sensor->version);
+
+	rval = et8ek8_reglist_import(client, &meta_reglist);
+	if (rval) {
+		dev_err(&client->dev,
+			"invalid register list %s, import failed\n",
+			ET8EK8_NAME);
+		goto out_poweroff;
+	}
+
+	sensor->current_reglist =
+		et8ek8_reglist_find_type(&meta_reglist,
+				       ET8EK8_REGLIST_MODE);
+	if (!sensor->current_reglist) {
+		dev_err(&client->dev,
+			"invalid register list %s, no mode found\n",
+			ET8EK8_NAME);
+		rval = -ENODEV;
+		goto out_poweroff;
+	}
+
+	et8ek8_reglist_to_mbus(sensor->current_reglist, &sensor->format);
+
+	rval = et8ek8_i2c_reglist_find_write(client,
+					   &meta_reglist,
+					   ET8EK8_REGLIST_POWERON);
+	if (rval) {
+		dev_err(&client->dev,
+			"invalid register list %s, no POWERON mode found\n",
+			ET8EK8_NAME);
+		goto out_poweroff;
+	}
+	rval = et8ek8_stream_on(sensor); /* Needed to be able to read EEPROM */
+	if (rval)
+		goto out_poweroff;
+	rval = et8ek8_g_priv_mem(subdev);
+	if (rval)
+		dev_warn(&client->dev,
+			"can not read OTP (EEPROM) memory from sensor\n");
+	rval = et8ek8_stream_off(sensor);
+	if (rval)
+		goto out_poweroff;
+
+	rval = et8ek8_power_off(sensor);
+	if (rval)
+		goto out_poweroff;
+
+	return 0;
+
+out_poweroff:
+	et8ek8_power_off(sensor);
+
+	return rval;
+}
+
+/* --------------------------------------------------------------------------
+ * sysfs attributes
+ */
+static ssize_t
+et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
+		     char *buf)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
+#error PAGE_SIZE too small!
+#endif
+
+	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
+
+	return ET8EK8_PRIV_MEM_SIZE;
+}
+static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int
+et8ek8_registered(struct v4l2_subdev *subdev)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	struct v4l2_mbus_framefmt *format;
+	int rval;
+
+	dev_dbg(&client->dev, "registered!");
+
+	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
+		dev_err(&client->dev, "could not register sysfs entry\n");
+		return -EBUSY;
+	}
+
+	rval = et8ek8_dev_init(subdev);
+	if (rval)
+		return rval;
+
+	rval = et8ek8_init_controls(sensor);
+	if (rval) {
+		dev_err(&client->dev, "controls initialization failed\n");
+		return rval;
+	}
+
+	format = __et8ek8_get_pad_format(sensor, NULL, 0,
+					 V4L2_SUBDEV_FORMAT_ACTIVE);
+	return 0;
+}
+
+static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
+{
+	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
+}
+
+static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+	int ret = 0;
+
+	mutex_lock(&sensor->power_lock);
+
+	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
+	 * update the power state.
+	 */
+	if (sensor->power_count == !on) {
+		ret = __et8ek8_set_power(sensor, !!on);
+		if (ret < 0)
+			goto done;
+	}
+
+	/* Update the power count. */
+	sensor->power_count += on ? 1 : -1;
+	WARN_ON(sensor->power_count < 0);
+
+done:
+	mutex_unlock(&sensor->power_lock);
+	return ret;
+}
+
+static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
+	struct v4l2_mbus_framefmt *format;
+	struct et8ek8_reglist *reglist;
+
+	reglist = et8ek8_reglist_find_type(&meta_reglist, ET8EK8_REGLIST_MODE);
+	format = __et8ek8_get_pad_format(sensor, fh->pad, 0,
+					 V4L2_SUBDEV_FORMAT_TRY);
+	et8ek8_reglist_to_mbus(reglist, format);
+
+	return et8ek8_set_power(sd, true);
+}
+
+static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return et8ek8_set_power(sd, false);
+}
+
+static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
+	.s_stream = et8ek8_s_stream,
+	.g_frame_interval = et8ek8_get_frame_interval,
+	.s_frame_interval = et8ek8_set_frame_interval,
+};
+
+static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
+	.s_power = et8ek8_set_power,
+};
+
+static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
+	.enum_mbus_code = et8ek8_enum_mbus_code,
+	.enum_frame_size = et8ek8_enum_frame_size,
+	.enum_frame_interval = et8ek8_enum_frame_ival,
+	.get_fmt = et8ek8_get_pad_format,
+	.set_fmt = et8ek8_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops et8ek8_ops = {
+	.core = &et8ek8_core_ops,
+	.video = &et8ek8_video_ops,
+	.pad = &et8ek8_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
+	.registered = et8ek8_registered,
+	.open = et8ek8_open,
+	.close = et8ek8_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int et8ek8_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (!sensor->power_count)
+		return 0;
+
+	return __et8ek8_set_power(sensor, false);
+}
+
+static int et8ek8_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (!sensor->power_count)
+		return 0;
+
+	return __et8ek8_set_power(sensor, true);
+}
+
+static const struct dev_pm_ops et8ek8_pm_ops = {
+	.suspend	= et8ek8_suspend,
+	.resume		= et8ek8_resume,
+};
+
+#else
+
+#define et8ek8_pm_ops	NULL
+
+#endif /* CONFIG_PM */
+
+static int et8ek8_probe(struct i2c_client *client,
+			const struct i2c_device_id *devid)
+{
+	struct et8ek8_sensor *sensor;
+	struct device *dev = &client->dev;
+	int ret;
+
+	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(sensor->reset)) {
+		dev_dbg(&client->dev, "could not request reset gpio\n");
+		return PTR_ERR(sensor->reset);
+	}
+
+	sensor->vana = devm_regulator_get(dev, "vana");
+	if (IS_ERR(sensor->vana)) {
+		dev_err(&client->dev, "could not get regulator for vana\n");
+		return PTR_ERR(sensor->vana);
+	}
+
+	sensor->ext_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(sensor->ext_clk)) {
+		dev_err(&client->dev, "could not get clock\n");
+		return PTR_ERR(sensor->ext_clk);
+	}
+
+	ret = of_property_read_u32(dev->of_node, "clock-frequency",
+				   &sensor->xclk_freq);
+	if (ret) {
+		dev_warn(dev, "can't get clock-frequency\n");
+		return ret;
+	}
+
+	mutex_init(&sensor->power_lock);
+
+	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
+	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->subdev.internal_ops = &et8ek8_internal_ops;
+
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+	if (ret < 0) {
+		dev_err(&client->dev, "media entity init failed!\n");
+		return ret;
+	}
+
+	ret = v4l2_async_register_subdev(&sensor->subdev);
+	if (ret < 0) {
+		media_entity_cleanup(&sensor->subdev.entity);
+		return ret;
+	}
+
+	dev_dbg(dev, "initialized!\n");
+
+	return 0;
+}
+
+static int __exit et8ek8_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
+
+	if (sensor->power_count) {
+		gpiod_set_value(sensor->reset, 0);
+		clk_disable_unprepare(sensor->ext_clk);
+		sensor->power_count = 0;
+	}
+
+	v4l2_device_unregister_subdev(&sensor->subdev);
+	device_remove_file(&client->dev, &dev_attr_priv_mem);
+	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
+	media_entity_cleanup(&sensor->subdev.entity);
+
+	return 0;
+}
+
+static const struct of_device_id et8ek8_of_table[] = {
+	{ .compatible = "toshiba,et8ek8" },
+	{ },
+};
+
+static const struct i2c_device_id et8ek8_id_table[] = {
+	{ ET8EK8_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
+
+static struct i2c_driver et8ek8_i2c_driver = {
+	.driver		= {
+		.name	= ET8EK8_NAME,
+		.pm	= &et8ek8_pm_ops,
+		.of_match_table	= et8ek8_of_table,
+	},
+	.probe		= et8ek8_probe,
+	.remove		= __exit_p(et8ek8_remove),
+	.id_table	= et8ek8_id_table,
+};
+
+module_i2c_driver(et8ek8_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
+MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/et8ek8/et8ek8_mode.c b/drivers/media/i2c/et8ek8/et8ek8_mode.c
new file mode 100644
index 0000000..e5c367b
--- /dev/null
+++ b/drivers/media/i2c/et8ek8/et8ek8_mode.c
@@ -0,0 +1,591 @@
+/*
+ * et8ek8_mode.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include "et8ek8_reg.h"
+
+/*
+ *
+ * Stingray sensor mode settings for Scooby
+ *
+ *
+ */
+
+/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
+static struct et8ek8_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 640 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_POWERON,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 2016,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 1207
+		},
+		.max_exp = 2012,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		/* Need to set firstly */
+		{ ET8EK8_REG_8BIT, 0x126C, 0xCC },
+		/* Strobe and Data of CCP2 delay are minimized. */
+		{ ET8EK8_REG_8BIT, 0x1269, 0x00 },
+		/* Refined value of Min H_COUNT  */
+		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
+		/* Frequency of SPCK setting (SPCK=MRCK) */
+		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
+		{ ET8EK8_REG_8BIT, 0x1241, 0x94 },
+		{ ET8EK8_REG_8BIT, 0x1242, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x124B, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1255, 0xFF },
+		{ ET8EK8_REG_8BIT, 0x1256, 0x9F },
+		{ ET8EK8_REG_8BIT, 0x1258, 0x00 },
+		/* From parallel out to serial out */
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 },
+		/* From w/ embeded data to w/o embeded data */
+		{ ET8EK8_REG_8BIT, 0x125E, 0xC0 },
+		/* CCP2 out is from STOP to ACTIVE */
+		{ ET8EK8_REG_8BIT, 0x1263, 0x98 },
+		{ ET8EK8_REG_8BIT, 0x1268, 0xC6 },
+		{ ET8EK8_REG_8BIT, 0x1434, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1163, 0x44 },
+		{ ET8EK8_REG_8BIT, 0x1166, 0x29 },
+		{ ET8EK8_REG_8BIT, 0x1140, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x1011, 0x24 },
+		{ ET8EK8_REG_8BIT, 0x1151, 0x80 },
+		{ ET8EK8_REG_8BIT, 0x1152, 0x23 },
+		/* Initial setting for improvement2 of lower frequency noise */
+		{ ET8EK8_REG_8BIT, 0x1014, 0x05 },
+		{ ET8EK8_REG_8BIT, 0x1033, 0x06 },
+		{ ET8EK8_REG_8BIT, 0x1034, 0x79 },
+		{ ET8EK8_REG_8BIT, 0x1423, 0x3F },
+		{ ET8EK8_REG_8BIT, 0x1424, 0x3F },
+		{ ET8EK8_REG_8BIT, 0x1426, 0x00 },
+		/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
+		{ ET8EK8_REG_8BIT, 0x1439, 0x00 },
+		/* Switch of blemish correction (0d:disable / 1d:enable) */
+		{ ET8EK8_REG_8BIT, 0x161F, 0x60 },
+		/* Switch of auto noise correction (0d:disable / 1d:enable) */
+		{ ET8EK8_REG_8BIT, 0x1634, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1646, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1648, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x113E, 0x01 },
+		{ ET8EK8_REG_8BIT, 0x113F, 0x22 },
+		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
+static struct et8ek8_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 560 MHz
+ * VCO        = 560 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 128 (3072)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 175
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 6
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3072,
+		.height = 2016,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 1292
+		},
+		.max_exp = 2012,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x57 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x06 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0x80 }, /* <-changed to v14 7E->80 */
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
+static struct et8ek8_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {
+/* (without the +1)
+ * SPCK       = 96.5333333333333 MHz
+ * CCP2       = 579.2 MHz
+ * VCO        = 579.2 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 133 (3192)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 181
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 5
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3192,
+		.height = 1008,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 96533333,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 3000
+		},
+		.max_exp = 1004,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x5A },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode4_SVGA_864x656_29.88fps */
+static struct et8ek8_reglist mode4_svga_864x656_29_88fps = {
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 320 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 166 (3984)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3984,
+		.height = 672,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 864,
+		.window_height = 656,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2988
+		},
+		.max_exp = 668,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x62 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x62 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0xA6 },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode5_VGA_648x492_29.93fps */
+static struct et8ek8_reglist mode5_vga_648x492_29_93fps = {
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 320 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 221 (5304)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 5304,
+		.height = 504,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 648,
+		.window_height = 492,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2993
+		},
+		.max_exp = 500,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode2_16VGA_2592x1968_3.99fps */
+static struct et8ek8_reglist mode2_16vga_2592x1968_3_99fps = {
+/* (without the +1)
+ * SPCK       = 80 MHz
+ * CCP2       = 640 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 254 (6096)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 6096,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 2592,
+		.window_height = 1968,
+		.pixel_clock = 80000000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 399
+		},
+		.max_exp = 6092,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0xFE },
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode_648x492_5fps */
+static struct et8ek8_reglist mode_648x492_5fps = {
+/* (without the +1)
+ * SPCK       = 13.3333333333333 MHz
+ * CCP2       = 53.3333333333333 MHz
+ * VCO        = 640 MHz
+ * VCOUNT     = 84 (2016)
+ * HCOUNT     = 221 (5304)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 200
+ * VCO_DIV    = 5
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 1
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 5304,
+		.height = 504,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 648,
+		.window_height = 492,
+		.pixel_clock = 13333333,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 499
+		},
+		.max_exp = 500,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x57 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode3_4VGA_1296x984_5fps */
+static struct et8ek8_reglist mode3_4vga_1296x984_5fps = {
+/* (without the +1)
+ * SPCK       = 49.4 MHz
+ * CCP2       = 395.2 MHz
+ * VCO        = 790.4 MHz
+ * VCOUNT     = 250 (6000)
+ * HCOUNT     = 137 (3288)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 247
+ * VCO_DIV    = 1
+ * SPCK_DIV   = 7
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3288,
+		.height = 3000,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 49400000,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 501
+		},
+		.max_exp = 2996,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x7B },
+		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
+		{ ET8EK8_REG_8BIT, 0x123A, 0x17 },
+		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
+		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
+		{ ET8EK8_REG_8BIT, 0x1222, 0xFA },
+		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
+static struct et8ek8_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {
+/* (without the +1)
+ * SPCK       = 84.2666666666667 MHz
+ * CCP2       = 505.6 MHz
+ * VCO        = 505.6 MHz
+ * VCOUNT     = 88 (2112)
+ * HCOUNT     = 133 (3192)
+ * CKREF_DIV  = 2
+ * CKVAR_DIV  = 158
+ * VCO_DIV    = 0
+ * SPCK_DIV   = 5
+ * MRCK_DIV   = 7
+ * LVDSCK_DIV = 0
+ */
+	.type = ET8EK8_REGLIST_MODE,
+	.mode = {
+		.sensor_width = 2592,
+		.sensor_height = 1968,
+		.sensor_window_origin_x = 0,
+		.sensor_window_origin_y = 0,
+		.sensor_window_width = 2592,
+		.sensor_window_height = 1968,
+		.width = 3192,
+		.height = 1056,
+		.window_origin_x = 0,
+		.window_origin_y = 0,
+		.window_width = 1296,
+		.window_height = 984,
+		.pixel_clock = 84266667,
+		.ext_clock = 9600000,
+		.timeperframe = {
+			.numerator = 100,
+			.denominator = 2500
+		},
+		.max_exp = 1052,
+		/* .max_gain = 0, */
+		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.sensitivity = 65536
+	},
+	.regs = {
+		{ ET8EK8_REG_8BIT, 0x1239, 0x4F },	/*        */
+		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x1222, 0x58 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },	/*        */
+		{ ET8EK8_REG_8BIT, 0x125D, 0x83 },	/*        */
+		{ ET8EK8_REG_TERM, 0, 0}
+	}
+};
+
+struct et8ek8_meta_reglist meta_reglist = {
+	.magic   = ET8EK8_MAGIC,
+	.version = "V14 03-June-2008",
+	.reglist = {
+		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
+		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
+		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
+		{ .ptr = &mode4_svga_864x656_29_88fps },
+		{ .ptr = &mode5_vga_648x492_29_93fps },
+		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
+		{ .ptr = &mode_648x492_5fps },
+		{ .ptr = &mode3_4vga_1296x984_5fps },
+		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
+		{ .ptr = 0 }
+	}
+};
diff --git a/drivers/media/i2c/et8ek8/et8ek8_reg.h b/drivers/media/i2c/et8ek8/et8ek8_reg.h
new file mode 100644
index 0000000..1f138b0
--- /dev/null
+++ b/drivers/media/i2c/et8ek8/et8ek8_reg.h
@@ -0,0 +1,100 @@
+/*
+ * et8ek8.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef ET8EK8REGS_H
+#define ET8EK8REGS_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-subdev.h>
+
+struct v4l2_mbus_framefmt;
+struct v4l2_subdev_pad_mbus_code_enum;
+
+#define ET8EK8_MAGIC			0x531A0002
+
+struct et8ek8_mode {
+	/* Physical sensor resolution and current image window */
+	__u16 sensor_width;
+	__u16 sensor_height;
+	__u16 sensor_window_origin_x;
+	__u16 sensor_window_origin_y;
+	__u16 sensor_window_width;
+	__u16 sensor_window_height;
+
+	/* Image data coming from sensor (after scaling) */
+	__u16 width;
+	__u16 height;
+	__u16 window_origin_x;
+	__u16 window_origin_y;
+	__u16 window_width;
+	__u16 window_height;
+
+	__u32 pixel_clock;		/* in Hz */
+	__u32 ext_clock;		/* in Hz */
+	struct v4l2_fract timeperframe;
+	__u32 max_exp;			/* Maximum exposure value */
+	__u32 pixel_format;		/* V4L2_PIX_FMT_xxx */
+	__u32 sensitivity;		/* 16.16 fixed point */
+};
+
+#define ET8EK8_REG_8BIT			1
+#define ET8EK8_REG_16BIT		2
+#define ET8EK8_REG_32BIT		4
+#define ET8EK8_REG_DELAY		100
+#define ET8EK8_REG_TERM			0xff
+struct et8ek8_reg {
+	u16 type;
+	u16 reg;			/* 16-bit offset */
+	u32 val;			/* 8/16/32-bit value */
+};
+
+/* Possible struct smia_reglist types. */
+#define ET8EK8_REGLIST_STANDBY		0
+#define ET8EK8_REGLIST_POWERON		1
+#define ET8EK8_REGLIST_RESUME		2
+#define ET8EK8_REGLIST_STREAMON		3
+#define ET8EK8_REGLIST_STREAMOFF	4
+#define ET8EK8_REGLIST_DISABLED		5
+
+#define ET8EK8_REGLIST_MODE		10
+
+#define ET8EK8_REGLIST_LSC_ENABLE	100
+#define ET8EK8_REGLIST_LSC_DISABLE	101
+#define ET8EK8_REGLIST_ANR_ENABLE	102
+#define ET8EK8_REGLIST_ANR_DISABLE	103
+
+struct et8ek8_reglist {
+	u32 type;
+	struct et8ek8_mode mode;
+	struct et8ek8_reg regs[];
+};
+
+#define ET8EK8_MAX_LEN			32
+struct et8ek8_meta_reglist {
+	u32 magic;
+	char version[ET8EK8_MAX_LEN];
+	union {
+		struct et8ek8_reglist *ptr;
+	} reglist[];
+};
+
+extern struct et8ek8_meta_reglist meta_reglist;
+
+#endif /* ET8EK8REGS */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..97a08d8 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -180,6 +180,11 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
 
+/* The base for the et8ek8 driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_ET8EK8_BASE		(V4L2_CID_USER_BASE + 0x1090)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
-- 
1.9.1


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

* Re: [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check
  2016-05-01 10:45     ` Sakari Ailus
@ 2016-05-03 18:25       ` Ivaylo Dimitrov
  2016-05-24  9:09       ` Pali Rohár
  1 sibling, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-03 18:25 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Hi,

On  1.05.2016 13:45, Sakari Ailus wrote:
> Hi Ivaylo,
>
> On Mon, Apr 25, 2016 at 12:08:04AM +0300, Ivaylo Dimitrov wrote:
>> From: Sakari Ailus <sakari.ailus@iki.fi>
>>
>> Required added multiplier (and divisor) calculation did not take into
>> account the existing divisor when checking the values against the minimum
>> divisor. Do just that.
>>
>> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
>> ---
>>   drivers/media/i2c/smiapp-pll.c | 3 ++-
>>   1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
>> index e3348db..5ad1edb 100644
>> --- a/drivers/media/i2c/smiapp-pll.c
>> +++ b/drivers/media/i2c/smiapp-pll.c
>> @@ -227,7 +227,8 @@ static int __smiapp_pll_calculate(
>>
>>   	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
>>   	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
>> -	more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
>> +	more_mul_factor = lcm(more_mul_factor,
>> +			      DIV_ROUND_UP(op_limits->min_sys_clk_div, div));
>>   	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
>>   		more_mul_factor);
>>   	i = roundup(more_mul_min, more_mul_factor);
>
> I remember writing the patch, but I don't remember what for, or whether it
> was really needed. Does the secondary sensor work without this one?
>

[  107.285919] smiapp 2-0010: lanes 1
[  107.286010] smiapp 2-0010: reset -2, nvm 0, clk 9600000, mode 0
[  107.286041] smiapp 2-0010: freq 0: 60000000
[  107.289306] twl4030reg_is_enabled VAUX4 0
[  107.303680] twl4030reg_enable VAUX4 0
[  107.352233] smiapp 2-0010: module 0x01-0x022b
[  107.352325] smiapp 2-0010: module revision 0x04-0x00 date 00-00-00
[  107.352355] smiapp 2-0010: sensor 0x00-0x0000
[  107.352386] smiapp 2-0010: sensor revision 0x00 firmware version 0x00
[  107.352416] smiapp 2-0010: smia version 10 smiapp version 00
[  107.352447] smiapp 2-0010: the sensor is called vs6555, ident 01022b04

.
.
.

[  107.595672] smiapp 2-0010: unable to compute pre_pll divisor
[  107.611816] smiapp 2-0010: link freq 60000000 Hz, bpp 10 not ok
[  107.611816] smiapp 2-0010: no valid link frequencies for 10 bpp
[  107.618072] smiapp 2-0010: no supported mbus code found

Or, in short, it does not work without this patch.

Thanks,
Ivo

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

* Re: [RFC PATCH 06/24] smiapp: Add quirk control support
  2016-05-01 10:46     ` Sakari Ailus
@ 2016-05-03 18:32       ` Ivaylo Dimitrov
  0 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-03 18:32 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media, Sakari Ailus

Hi,

On  1.05.2016 13:46, Sakari Ailus wrote:
> On Mon, Apr 25, 2016 at 12:08:06AM +0300, Ivaylo Dimitrov wrote:
>> From: Sakari Ailus <sakari.ailus@linux.intel.com>
>>
>> Quirk controls can be set up in the init quirk.
>>
>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>
> Do you need quirk controls for something at the moment? I guess not at least
> with the secondary sensor?
>

Yes, vs6555 doesn't seem to need quirks ATM, I guess that patch comes 
from N9/50 adaptation kernel.

Ivo

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-03 14:50           ` [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor Ivaylo Dimitrov
@ 2016-05-22 10:07             ` Ivaylo Dimitrov
  2016-05-24 11:19             ` Pavel Machek
  2016-05-25 21:45             ` Sakari Ailus
  2 siblings, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-05-22 10:07 UTC (permalink / raw)
  To: sakari.ailus; +Cc: sre, pali.rohar, pavel, linux-media



On  3.05.2016 17:50, Ivaylo Dimitrov wrote:
> The sensor is found in Nokia N900 main camera
>
> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
> ---
>   .../bindings/media/i2c/toshiba,et8ek8.txt          |   53 +
>   drivers/media/i2c/Kconfig                          |    1 +
>   drivers/media/i2c/Makefile                         |    1 +
>   drivers/media/i2c/et8ek8/Kconfig                   |    6 +
>   drivers/media/i2c/et8ek8/Makefile                  |    2 +
>   drivers/media/i2c/et8ek8/et8ek8_driver.c           | 1711 ++++++++++++++++++++
>   drivers/media/i2c/et8ek8/et8ek8_mode.c             |  591 +++++++
>   drivers/media/i2c/et8ek8/et8ek8_reg.h              |  100 ++
>   include/uapi/linux/v4l2-controls.h                 |    5 +
>   9 files changed, 2470 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
>   create mode 100644 drivers/media/i2c/et8ek8/Kconfig
>   create mode 100644 drivers/media/i2c/et8ek8/Makefile
>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_driver.c
>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_mode.c
>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_reg.h
>


ping

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

* Re: [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check
  2016-05-01 10:45     ` Sakari Ailus
  2016-05-03 18:25       ` Ivaylo Dimitrov
@ 2016-05-24  9:09       ` Pali Rohár
  1 sibling, 0 replies; 102+ messages in thread
From: Pali Rohár @ 2016-05-24  9:09 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Ivaylo Dimitrov, sre, pavel, linux-media

On Sunday 01 May 2016 13:45:24 Sakari Ailus wrote:
> Hi Ivaylo,
> 
> On Mon, Apr 25, 2016 at 12:08:04AM +0300, Ivaylo Dimitrov wrote:
> > From: Sakari Ailus <sakari.ailus@iki.fi>
> > 
> > Required added multiplier (and divisor) calculation did not take into
> > account the existing divisor when checking the values against the minimum
> > divisor. Do just that.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
> > ---
> >  drivers/media/i2c/smiapp-pll.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
> > index e3348db..5ad1edb 100644
> > --- a/drivers/media/i2c/smiapp-pll.c
> > +++ b/drivers/media/i2c/smiapp-pll.c
> > @@ -227,7 +227,8 @@ static int __smiapp_pll_calculate(
> >  
> >  	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
> >  	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
> > -	more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
> > +	more_mul_factor = lcm(more_mul_factor,
> > +			      DIV_ROUND_UP(op_limits->min_sys_clk_div, div));
> >  	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
> >  		more_mul_factor);
> >  	i = roundup(more_mul_min, more_mul_factor);
> 
> I remember writing the patch, but I don't remember what for, or whether it
> was really needed. Does the secondary sensor work without this one?

Hi! You sent me this patch more then 3 years ago. Look at our private
email discussion, e.g. email with Message-Id <201303281524.10538@pali>
and subject "Re: Nokia N900 - smiapp driver" which was sent years ago
Thu, 28 Mar 2013 15:24:10 +0100.

-- 
Pali Rohár
pali.rohar@gmail.com

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

* Re: [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check
  2016-04-24 21:08   ` [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check Ivaylo Dimitrov
  2016-05-01 10:45     ` Sakari Ailus
@ 2016-05-24 10:17     ` Pavel Machek
  1 sibling, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-05-24 10:17 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On Mon 2016-04-25 00:08:04, Ivaylo Dimitrov wrote:
> From: Sakari Ailus <sakari.ailus@iki.fi>
> 
> Required added multiplier (and divisor) calculation did not take into
> account the existing divisor when checking the values against the minimum
> divisor. Do just that.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>

Acked-by: Pavel Machek <pavel@ucw.cz>

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-03 14:50           ` [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor Ivaylo Dimitrov
  2016-05-22 10:07             ` Ivaylo Dimitrov
@ 2016-05-24 11:19             ` Pavel Machek
  2016-06-04 19:16               ` Ivaylo Dimitrov
  2016-06-06  9:04               ` Sylwester Nawrocki
  2016-05-25 21:45             ` Sakari Ailus
  2 siblings, 2 replies; 102+ messages in thread
From: Pavel Machek @ 2016-05-24 11:19 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> The sensor is found in Nokia N900 main camera
> 
> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>

> +/*
> + * A buffered write method that puts the wanted register write
> + * commands in a message list and passes the list to the i2c framework
> + */
> +static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
> +					  const struct et8ek8_reg *wnext,
> +					  int cnt)
> +{
> +	/* FIXME: check how big cnt is */
> +	struct i2c_msg msg[cnt];
> +	unsigned char data[cnt][6];

Uff, no, variable length arrays in the kernel. No, I don't think that
should be done. Rather allocate maximum length here and then check its
> cnt?

> +/*
> + * Write a list of registers to i2c device.
> + *
> + * The list of registers is terminated by ET8EK8_REG_TERM.
> + * Returns zero if successful, or non-zero otherwise.
> + */
> +static int et8ek8_i2c_write_regs(struct i2c_client *client,
> +				 const struct et8ek8_reg reglist[])
> +{
> +	int r, cnt = 0;
> +	const struct et8ek8_reg *next, *wnext;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if (reglist == NULL)

(!reglist) ? :-). Actually, you can keep your preffered style there,
but maybe ammount of if (something that can not happen) return
... should be reduced. Noone should ever call this without valid
reglist or client->adapter, right?

> +		return -EINVAL;
> +
> +	/* Initialize list pointers to the start of the list */
> +	next = wnext = reglist;
> +
> +	do {
> +		/*
> +		 * We have to go through the list to figure out how
> +		 * many regular writes we have in a row
> +		 */
> +		while (next->type != ET8EK8_REG_TERM
> +		       && next->type != ET8EK8_REG_DELAY) {
> +			/*
> +			 * Here we check that the actual length fields
> +			 * are valid
> +			 */
> +			if (next->type != ET8EK8_REG_8BIT
> +			    &&  next->type != ET8EK8_REG_16BIT) {

Extra space after &&

> +				dev_err(&client->dev,
> +					"Invalid value on entry %d 0x%x\n",
> +					cnt, next->type);
> +				return -EINVAL;
> +			}

And maybe this could be just BUG_ON(). 

> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_fmt(
> +		struct et8ek8_meta_reglist *meta,
> +		struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
> +	struct et8ek8_reglist *best_match = NULL;
> +	struct et8ek8_reglist *best_other = NULL;
> +	struct v4l2_mbus_framefmt format;
> +	unsigned int max_dist_match = (unsigned int)-1;
> +	unsigned int max_dist_other = (unsigned int)-1;
> +
> +	/* Find the mode with the closest image size. The distance between
> +	 * image sizes is the size in pixels of the non-overlapping regions

You may want to run checkpatch. I guess it will complain. I doubt it
matters much :-).

> +	while (meta->reglist[nlists].ptr != NULL)
> +		nlists++;

...!= NULL) can be removed. ... here and in other places.

> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x111B,
> +				    tp_mode << 4);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1121,
> +				    cbh_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1124,
> +				    cbv_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x112C, din_sw);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1420, r1420);
> +	if (rval)
> +		goto out;
> +
> +out:
> +	return rval;
> +}

Goto out when all out does is return is a bit of overkill.

> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct et8ek8_sensor *sensor =
> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> +	const struct et8ek8_mode *mode = &sensor->current_reglist->mode;
> +
> +	switch (ctrl->id) {
> +	case ET8EK8_CID_USER_FRAME_WIDTH:
> +		ctrl->cur.val = mode->width;
> +		break;
> +	case ET8EK8_CID_USER_FRAME_HEIGHT:
> +		ctrl->cur.val = mode->height;
> +		break;
> +	case ET8EK8_CID_USER_VISIBLE_WIDTH:
> +		ctrl->cur.val = mode->window_width;
> +		break;
> +	case ET8EK8_CID_USER_VISIBLE_HEIGHT:
> +		ctrl->cur.val = mode->window_height;
> +		break;
> +	case ET8EK8_CID_USER_SENSITIVITY:
> +		ctrl->cur.val = mode->sensitivity;
> +		break;
> +	}

default: return -EINVAL ?

> +	/*
> +	 * Calculate average pixel clock per line. Assume buffers can spread
> +	 * the data over horizontal blanking time. Rounding upwards.
> +	 * Formula taken from stock Nokia N900 kernel
> +	 */

"kernel."  ?

> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
> +{
> +	int rval;
> +
> +	gpiod_set_value(sensor->reset, 0);
> +	udelay(1);
> +
> +	clk_disable_unprepare(sensor->ext_clk);
> +
> +	rval = regulator_disable(sensor->vana);
> +	return rval;
> +}

get rid of rval, return directly?

> +	udelay(10); /* I wish this is a good value */

Me too ;-).

> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
> +	unsigned int offset = 0;
> +	u8 *ptr  = sensor->priv_mem;
> +	int rval = 0;
> +
> +	/* Read the EEPROM window-by-window, each window 8 bytes */
> +	do {
> +		u8 buffer[PRIV_MEM_WIN_SIZE];
> +		struct i2c_msg msg;
> +		int bytes, i;
> +		int ofs;
> +
> +		/* Set the current window */
> +		rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x0001,
> +					    0xe0 | (offset >> 3));
> +		if (rval < 0)
> +			goto out;

out: only does return, cleaning it up here will help readability.

> +		/* Wait for status bit */
> +		for (i = 0; i < 1000; ++i) {
> +			u32 status;
> +
> +			rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
> +						   0x0003, &status);
> +			if (rval < 0)
> +				goto out;
> +			if ((status & 0x08) == 0)
> +				break;
> +			usleep_range(1000, 2000);
> +		};
> +
> +		if (i == 1000) {
> +			rval = -EIO;
> +			goto out;
> +		}

Especially here.

> +		if (rval < 0)
> +			goto out;
> +		rval = 0;

And here.


> +#ifndef ET8EK8REGS_H
> +#define ET8EK8REGS_H
> +
> +#include <linux/i2c.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/v4l2-subdev.h>
> +
> +struct v4l2_mbus_framefmt;
> +struct v4l2_subdev_pad_mbus_code_enum;
> +
> +#define ET8EK8_MAGIC			0x531A0002
> +
> +struct et8ek8_mode {
> +	/* Physical sensor resolution and current image window */
> +	__u16 sensor_width;
> +	__u16 sensor_height;

Is this exported to userspace?

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-03 14:50           ` [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor Ivaylo Dimitrov
  2016-05-22 10:07             ` Ivaylo Dimitrov
  2016-05-24 11:19             ` Pavel Machek
@ 2016-05-25 21:45             ` Sakari Ailus
  2016-06-04 19:54               ` Ivaylo Dimitrov
  2 siblings, 1 reply; 102+ messages in thread
From: Sakari Ailus @ 2016-05-25 21:45 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

I've got some comments here but I haven't reviewed everything yet. What's
missing is

- the user space interface for selecting the sensor configuration "mode",

- passing information on the sensor configuration to the user space.

I'll try to take a look at those some time in the near future.


I very much appreciate your work towards finally upstreaming this! :-)

On Tue, May 03, 2016 at 05:50:04PM +0300, Ivaylo Dimitrov wrote:
> The sensor is found in Nokia N900 main camera
> 
> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
> ---
>  .../bindings/media/i2c/toshiba,et8ek8.txt          |   53 +
>  drivers/media/i2c/Kconfig                          |    1 +
>  drivers/media/i2c/Makefile                         |    1 +
>  drivers/media/i2c/et8ek8/Kconfig                   |    6 +
>  drivers/media/i2c/et8ek8/Makefile                  |    2 +
>  drivers/media/i2c/et8ek8/et8ek8_driver.c           | 1711 ++++++++++++++++++++
>  drivers/media/i2c/et8ek8/et8ek8_mode.c             |  591 +++++++
>  drivers/media/i2c/et8ek8/et8ek8_reg.h              |  100 ++
>  include/uapi/linux/v4l2-controls.h                 |    5 +
>  9 files changed, 2470 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
>  create mode 100644 drivers/media/i2c/et8ek8/Kconfig
>  create mode 100644 drivers/media/i2c/et8ek8/Makefile
>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_driver.c
>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_mode.c
>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_reg.h
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
> new file mode 100644
> index 0000000..55f712c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
> @@ -0,0 +1,53 @@
> +Toshiba et8ek8 5MP sensor
> +
> +Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device
> +
> +More detailed documentation can be found in
> +Documentation/devicetree/bindings/media/video-interfaces.txt .
> +
> +
> +Mandatory properties
> +--------------------
> +
> +- compatible: "toshiba,et8ek8"
> +- reg: I2C address (0x3e, or an alternative address)
> +- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor
> +  dependent).

As these are bindings for a particular sensor, 2,8 volts it is.

The sensor has also a digital voltage supply but it might be controlled by
the same GPIO which controls the CCP2 switch. Ugly stuff. Perhaps we could
just omit that here.

> +- clocks: External clock to the sensor
> +- clock-frequency: Frequency of the external clock to the sensor
> +
> +
> +Optional properties
> +-------------------
> +
> +- reset-gpios: XSHUTDOWN GPIO

I guess this should be mandatory.

> +
> +
> +Endpoint node mandatory properties
> +----------------------------------
> +
> +- clock-lanes: <0>
> +- data-lanes: <1..n>
> +- remote-endpoint: A phandle to the bus receiver's endpoint node.
> +
> +
> +Example
> +-------
> +
> +&i2c3 {
> +	clock-frequency = <400000>;
> +
> +	cam1: camera@3e {
> +		compatible = "toshiba,et8ek8";
> +		reg = <0x3e>;
> +		vana-supply = <&vaux4>;
> +		clocks = <&isp 0>;
> +		clock-frequency = <9600000>;
> +		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
> +		port {
> +			csi_cam1: endpoint {
> +				remote-endpoint = <&csi_out1>;
> +			};
> +		};
> +	};
> +};

Please split the DT documentation from the driver.

I remember having discussed showing the module in DT with Sebastian but I
couldn't find the patches anywhere. We currently consider the lens and
sensor entirely separate, the module has not been shown in software as
there's been nothing to control it.

Sebastian: do you still have those patches around somewhere?

> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 993dc50..e964787 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -629,6 +629,7 @@ config VIDEO_S5K5BAF
>  	  camera sensor with an embedded SoC image signal processor.
>  
>  source "drivers/media/i2c/smiapp/Kconfig"
> +source "drivers/media/i2c/et8ek8/Kconfig"
>  
>  config VIDEO_S5C73M3
>  	tristate "Samsung S5C73M3 sensor support"
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 94f2c99..907b180 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -2,6 +2,7 @@ msp3400-objs	:=	msp3400-driver.o msp3400-kthreads.o
>  obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
>  
>  obj-$(CONFIG_VIDEO_SMIAPP)	+= smiapp/
> +obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8/
>  obj-$(CONFIG_VIDEO_CX25840) += cx25840/
>  obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
>  obj-y				+= soc_camera/
> diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig
> new file mode 100644
> index 0000000..1439936
> --- /dev/null
> +++ b/drivers/media/i2c/et8ek8/Kconfig
> @@ -0,0 +1,6 @@
> +config VIDEO_ET8EK8
> +	tristate "ET8EK8 camera sensor support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	---help---
> +	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
> +	  It is used for example in Nokia N900 (RX-51).
> diff --git a/drivers/media/i2c/et8ek8/Makefile b/drivers/media/i2c/et8ek8/Makefile
> new file mode 100644
> index 0000000..66d1b7d
> --- /dev/null
> +++ b/drivers/media/i2c/et8ek8/Makefile
> @@ -0,0 +1,2 @@
> +et8ek8-objs			+= et8ek8_mode.o et8ek8_driver.o
> +obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8.o
> diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
> new file mode 100644
> index 0000000..1eaef78
> --- /dev/null
> +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
> @@ -0,0 +1,1711 @@
> +/*
> + * et8ek8_driver.c
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>

tuukkat76@gmail.com

> + *
> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
> + *
> + * This driver is based on the Micron MT9T012 camera imager driver
> + * (C) Texas Instruments.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/gpio/consumer.h>

Alphabetical order, please.

> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/sort.h>
> +#include <linux/version.h>

Is linux/version.h needed? 

> +#include <linux/v4l2-mediabus.h>
> +
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "et8ek8_reg.h"
> +
> +#define ET8EK8_NAME		"et8ek8"
> +#define ET8EK8_PRIV_MEM_SIZE	128
> +
> +#define ET8EK8_CID_USER_FRAME_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 1)
> +#define ET8EK8_CID_USER_FRAME_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 2)
> +#define ET8EK8_CID_USER_VISIBLE_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 3)
> +#define ET8EK8_CID_USER_VISIBLE_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 4)
> +#define ET8EK8_CID_USER_SENSITIVITY	(V4L2_CID_USER_ET8EK8_BASE + 5)

If you have custom controls, 

> +
> +struct et8ek8_sensor {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pad;
> +	struct v4l2_mbus_framefmt format;
> +	struct gpio_desc *reset;
> +	struct regulator *vana;
> +	struct clk *ext_clk;
> +	u32 xclk_freq;
> +
> +	u16 version;
> +
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *pixel_rate;
> +	struct et8ek8_reglist *current_reglist;
> +
> +	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
> +
> +	struct mutex power_lock;
> +	int power_count;
> +};
> +
> +#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
> +
> +enum et8ek8_versions {
> +	ET8EK8_REV_1 = 0x0001,
> +	ET8EK8_REV_2,
> +};
> +
> +/*
> + * This table describes what should be written to the sensor register
> + * for each gain value. The gain(index in the table) is in terms of
> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
> + * the *analog gain, [1] in the digital gain
> + *
> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
> + */
> +static struct et8ek8_gain {
> +	u16 analog;
> +	u16 digital;
> +} const et8ek8_gain_table[] = {
> +	{ 32,    0},  /* x1 */
> +	{ 34,    0},
> +	{ 37,    0},
> +	{ 39,    0},
> +	{ 42,    0},
> +	{ 45,    0},
> +	{ 49,    0},
> +	{ 52,    0},
> +	{ 56,    0},
> +	{ 60,    0},
> +	{ 64,    0},  /* x2 */
> +	{ 69,    0},
> +	{ 74,    0},
> +	{ 79,    0},
> +	{ 84,    0},
> +	{ 91,    0},
> +	{ 97,    0},
> +	{104,    0},
> +	{111,    0},
> +	{119,    0},
> +	{128,    0},  /* x4 */
> +	{137,    0},
> +	{147,    0},
> +	{158,    0},
> +	{169,    0},
> +	{181,    0},
> +	{194,    0},
> +	{208,    0},
> +	{223,    0},
> +	{239,    0},
> +	{256,    0},  /* x8 */
> +	{256,   73},
> +	{256,  152},
> +	{256,  236},
> +	{256,  327},
> +	{256,  424},
> +	{256,  528},
> +	{256,  639},
> +	{256,  758},
> +	{256,  886},
> +	{256, 1023},  /* x16 */
> +};
> +
> +/* Register definitions */
> +#define REG_REVISION_NUMBER_L	0x1200
> +#define REG_REVISION_NUMBER_H	0x1201
> +
> +#define PRIV_MEM_START_REG	0x0008
> +#define PRIV_MEM_WIN_SIZE	8
> +
> +#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
> +
> +#define USE_CRC			1
> +
> +/*
> + *
> + * Register access helpers
> + *
> + */
> +
> +/*
> + * Read a 8/16/32-bit i2c register.  The value is returned in 'val'.
> + * Returns zero if successful, or non-zero otherwise.
> + */
> +static int et8ek8_i2c_read_reg(struct i2c_client *client, u16 data_length,
> +			       u16 reg, u32 *val)
> +{
> +	int r;
> +	struct i2c_msg msg[1];
> +	unsigned char data[4];
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
> +		return -EINVAL;
> +
> +	msg->addr = client->addr;
> +	msg->flags = 0;
> +	msg->len = 2;
> +	msg->buf = data;
> +
> +	/* high byte goes out first */
> +	data[0] = (u8) (reg >> 8);
> +	data[1] = (u8) (reg & 0xff);
> +	r = i2c_transfer(client->adapter, msg, 1);
> +	if (r < 0)
> +		goto err;
> +
> +	msg->len = data_length;
> +	msg->flags = I2C_M_RD;
> +	r = i2c_transfer(client->adapter, msg, 1);
> +	if (r < 0)
> +		goto err;
> +
> +	*val = 0;
> +	/* high byte comes first */
> +	if (data_length == ET8EK8_REG_8BIT)
> +		*val = data[0];
> +	else
> +		*val = (data[0] << 8) + data[1];
> +
> +	return 0;
> +
> +err:
> +	dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r);
> +
> +	return r;
> +}
> +
> +static void et8ek8_i2c_create_msg(struct i2c_client *client, u16 len, u16 reg,
> +				  u32 val, struct i2c_msg *msg,
> +				  unsigned char *buf)
> +{
> +	msg->addr = client->addr;
> +	msg->flags = 0; /* Write */
> +	msg->len = 2 + len;
> +	msg->buf = buf;
> +
> +	/* high byte goes out first */
> +	buf[0] = (u8) (reg >> 8);
> +	buf[1] = (u8) (reg & 0xff);
> +
> +	switch (len) {
> +	case ET8EK8_REG_8BIT:
> +		buf[2] = (u8) (val) & 0xff;
> +		break;
> +	case ET8EK8_REG_16BIT:
> +		buf[2] = (u8) (val >> 8) & 0xff;
> +		buf[3] = (u8) (val & 0xff);
> +		break;
> +	case ET8EK8_REG_32BIT:
> +		buf[2] = (u8) (val >> 24) & 0xff;
> +		buf[3] = (u8) (val >> 16) & 0xff;
> +		buf[4] = (u8) (val >> 8) & 0xff;
> +		buf[5] = (u8) (val & 0xff);
> +		break;
> +	default:
> +		BUG();
> +	}
> +}
> +
> +/*
> + * A buffered write method that puts the wanted register write
> + * commands in a message list and passes the list to the i2c framework
> + */
> +static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
> +					  const struct et8ek8_reg *wnext,
> +					  int cnt)
> +{
> +	/* FIXME: check how big cnt is */
> +	struct i2c_msg msg[cnt];
> +	unsigned char data[cnt][6];
> +	int wcnt = 0;
> +	u16 reg, data_length;
> +	u32 val;
> +
> +	/* Create new write messages for all writes */
> +	while (wcnt < cnt) {
> +		data_length = wnext->type;
> +		reg = wnext->reg;
> +		val = wnext->val;
> +		wnext++;
> +
> +		et8ek8_i2c_create_msg(client, data_length, reg,
> +				    val, &msg[wcnt], &data[wcnt][0]);
> +
> +		/* Update write count */
> +		wcnt++;
> +	}
> +
> +	/* Now we send everything ... */
> +	return i2c_transfer(client->adapter, msg, wcnt);
> +}
> +
> +/*
> + * Write a list of registers to i2c device.
> + *
> + * The list of registers is terminated by ET8EK8_REG_TERM.
> + * Returns zero if successful, or non-zero otherwise.
> + */
> +static int et8ek8_i2c_write_regs(struct i2c_client *client,
> +				 const struct et8ek8_reg reglist[])
> +{
> +	int r, cnt = 0;
> +	const struct et8ek8_reg *next, *wnext;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if (reglist == NULL)
> +		return -EINVAL;
> +
> +	/* Initialize list pointers to the start of the list */
> +	next = wnext = reglist;
> +
> +	do {
> +		/*
> +		 * We have to go through the list to figure out how
> +		 * many regular writes we have in a row
> +		 */
> +		while (next->type != ET8EK8_REG_TERM
> +		       && next->type != ET8EK8_REG_DELAY) {
> +			/*
> +			 * Here we check that the actual length fields
> +			 * are valid
> +			 */
> +			if (next->type != ET8EK8_REG_8BIT
> +			    &&  next->type != ET8EK8_REG_16BIT) {
> +				dev_err(&client->dev,
> +					"Invalid value on entry %d 0x%x\n",
> +					cnt, next->type);
> +				return -EINVAL;
> +			}
> +
> +			/*
> +			 * Increment count of successive writes and
> +			 * read pointer
> +			 */
> +			cnt++;
> +			next++;
> +		}
> +
> +		/* Now we start writing ... */
> +		r = et8ek8_i2c_buffered_write_regs(client, wnext, cnt);
> +
> +		/* ... and then check that everything was OK */
> +		if (r < 0) {
> +			dev_err(&client->dev, "i2c transfer error !!!\n");
> +			return r;
> +		}
> +
> +		/*
> +		 * If we ran into a sleep statement when going through
> +		 * the list, this is where we snooze for the required time
> +		 */
> +		if (next->type == ET8EK8_REG_DELAY) {
> +			set_current_state(TASK_UNINTERRUPTIBLE);
> +			schedule_timeout(msecs_to_jiffies(next->val));
> +			/*
> +			 * ZZZ ...
> +			 * Update list pointers and cnt and start over ...
> +			 */
> +			next++;
> +			wnext = next;
> +			cnt = 0;
> +		}
> +	} while (next->type != ET8EK8_REG_TERM);
> +
> +	return 0;
> +}
> +
> +/*
> + * Write to a 8/16-bit register.
> + * Returns zero if successful, or non-zero otherwise.
> + */
> +static int et8ek8_i2c_write_reg(struct i2c_client *client, u16 data_length,
> +				u16 reg, u32 val)
> +{
> +	int r;
> +	struct i2c_msg msg[1];
> +	unsigned char data[6];
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
> +		return -EINVAL;
> +
> +	et8ek8_i2c_create_msg(client, data_length, reg, val, msg, data);
> +
> +	r = i2c_transfer(client->adapter, msg, 1);
> +	if (r < 0)
> +		dev_err(&client->dev,
> +			"wrote 0x%x to offset 0x%x error %d\n", val, reg, r);
> +	else
> +		r = 0; /* on success i2c_transfer() return messages trasfered */
> +
> +	return r;
> +}
> +
> +static struct et8ek8_reglist *et8ek8_reglist_find_type(
> +		struct et8ek8_meta_reglist *meta,
> +		u16 type)
> +{
> +	struct et8ek8_reglist **next = &meta->reglist[0].ptr;
> +
> +	while (*next) {
> +		if ((*next)->type == type)
> +			return *next;
> +
> +		next++;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int et8ek8_i2c_reglist_find_write(struct i2c_client *client,
> +					 struct et8ek8_meta_reglist *meta,
> +					 u16 type)
> +{
> +	struct et8ek8_reglist *reglist;
> +
> +	reglist = et8ek8_reglist_find_type(meta, type);
> +	if (IS_ERR(reglist))
> +		return PTR_ERR(reglist);
> +
> +	return et8ek8_i2c_write_regs(client, reglist->regs);
> +}
> +
> +static struct et8ek8_reglist **et8ek8_reglist_first(
> +		struct et8ek8_meta_reglist *meta)
> +{
> +	return &meta->reglist[0].ptr;
> +}
> +
> +static void et8ek8_reglist_to_mbus(const struct et8ek8_reglist *reglist,
> +				   struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->width = reglist->mode.window_width;
> +	fmt->height = reglist->mode.window_height;
> +
> +	if (reglist->mode.pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
> +		fmt->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
> +	else
> +		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
> +}
> +
> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_fmt(
> +		struct et8ek8_meta_reglist *meta,
> +		struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
> +	struct et8ek8_reglist *best_match = NULL;
> +	struct et8ek8_reglist *best_other = NULL;
> +	struct v4l2_mbus_framefmt format;
> +	unsigned int max_dist_match = (unsigned int)-1;
> +	unsigned int max_dist_other = (unsigned int)-1;
> +
> +	/* Find the mode with the closest image size. The distance between
> +	 * image sizes is the size in pixels of the non-overlapping regions
> +	 * between the requested size and the frame-specified size.
> +	 *
> +	 * Store both the closest mode that matches the requested format, and
> +	 * the closest mode for all other formats. The best match is returned
> +	 * if found, otherwise the best mode with a non-matching format is
> +	 * returned.
> +	 */
> +	for (; *list; list++) {
> +		unsigned int dist;
> +
> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
> +			continue;
> +
> +		et8ek8_reglist_to_mbus(*list, &format);
> +
> +		dist = min(fmt->width, format.width)
> +		     * min(fmt->height, format.height);
> +		dist = format.width * format.height
> +		     + fmt->width * fmt->height - 2 * dist;
> +
> +
> +		if (fmt->code == format.code) {
> +			if (dist < max_dist_match || best_match == NULL) {
> +				best_match = *list;
> +				max_dist_match = dist;
> +			}
> +		} else {
> +			if (dist < max_dist_other || best_other == NULL) {
> +				best_other = *list;
> +				max_dist_other = dist;
> +			}
> +		}
> +	}
> +
> +	return best_match ? best_match : best_other;
> +}
> +
> +#define TIMEPERFRAME_AVG_FPS(t)						\
> +	(((t).denominator + ((t).numerator >> 1)) / (t).numerator)
> +
> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_ival(
> +		struct et8ek8_meta_reglist *meta,
> +		struct et8ek8_reglist *current_reglist,
> +		struct v4l2_fract *timeperframe)
> +{
> +	int fps = TIMEPERFRAME_AVG_FPS(*timeperframe);
> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
> +	struct et8ek8_mode *current_mode = &current_reglist->mode;
> +
> +	for (; *list; list++) {
> +		struct et8ek8_mode *mode = &(*list)->mode;
> +
> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
> +			continue;
> +
> +		if (mode->window_width != current_mode->window_width
> +		    || mode->window_height != current_mode->window_height)
> +			continue;
> +
> +		if (TIMEPERFRAME_AVG_FPS(mode->timeperframe) == fps)
> +			return *list;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int et8ek8_reglist_cmp(const void *a, const void *b)
> +{
> +	const struct et8ek8_reglist **list1 = (const struct et8ek8_reglist **)a,
> +		**list2 = (const struct et8ek8_reglist **)b;
> +
> +	/* Put real modes in the beginning. */
> +	if ((*list1)->type == ET8EK8_REGLIST_MODE &&
> +	    (*list2)->type != ET8EK8_REGLIST_MODE)
> +		return -1;
> +	if ((*list1)->type != ET8EK8_REGLIST_MODE &&
> +	    (*list2)->type == ET8EK8_REGLIST_MODE)
> +		return 1;
> +
> +	/* Descending width. */
> +	if ((*list1)->mode.window_width > (*list2)->mode.window_width)
> +		return -1;
> +	if ((*list1)->mode.window_width < (*list2)->mode.window_width)
> +		return 1;
> +
> +	if ((*list1)->mode.window_height > (*list2)->mode.window_height)
> +		return -1;
> +	if ((*list1)->mode.window_height < (*list2)->mode.window_height)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static int et8ek8_reglist_import(struct i2c_client *client,
> +				 struct et8ek8_meta_reglist *meta)
> +{
> +	uintptr_t nlists = 0;
> +
> +	if (meta->magic != ET8EK8_MAGIC) {

No need to check this anymore.

> +		dev_err(&client->dev,
> +			"invalid camera sensor firmware (0x%08X)\n",
> +			meta->magic);
> +		return -EILSEQ;
> +	}
> +
> +	dev_info(&client->dev, "meta_reglist version %s\n", meta->version);
> +
> +	while (meta->reglist[nlists].ptr != NULL)
> +		nlists++;
> +
> +	if (!nlists)
> +		return -EINVAL;
> +
> +	sort(&meta->reglist[0].ptr, nlists, sizeof(meta->reglist[0].ptr),
> +	     et8ek8_reglist_cmp, NULL);
> +
> +	nlists = 0;
> +	while (meta->reglist[nlists].ptr != NULL) {

I guess you could just loop over nlists. You just counted the entries above.

> +		struct et8ek8_reglist *list;
> +
> +		list = meta->reglist[nlists].ptr;
> +
> +		dev_dbg(&client->dev,
> +		       "%s: type %d\tw %d\th %d\tfmt %x\tival %d/%d\tptr %p\n",
> +		       __func__,
> +		       list->type,
> +		       list->mode.window_width, list->mode.window_height,
> +		       list->mode.pixel_format,
> +		       list->mode.timeperframe.numerator,
> +		       list->mode.timeperframe.denominator,
> +		       (void *)meta->reglist[nlists].ptr);
> +
> +		nlists++;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Return time of one row in microseconds, .8 fixed point format.
> + * If the sensor is not set to any mode, return zero.
> + */
> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
> +{
> +	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
> +	unsigned int rt;	/* Row time in .8 fixed point */
> +
> +	if (!sensor->current_reglist)
> +		return 0;
> +
> +	clock = sensor->current_reglist->mode.pixel_clock;
> +	clock = (clock + (1 << 9)) >> 10;
> +	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
> +	rt = (rt + (clock >> 1)) / clock;
> +
> +	return rt;
> +}
> +
> +/*
> + * Convert exposure time `us' to rows. Modify `us' to make it to
> + * correspond to the actual exposure time.
> + */
> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
> +{
> +	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
> +	unsigned int rt;	/* Row time in .8 fixed point */
> +
> +	/* Assume that the maximum exposure time is at most ~8 s,
> +	 * and the maximum width (with blanking) ~8000 pixels.
> +	 * The formula here is in principle as simple as
> +	 *    rows = exptime / 1e6 / width * pixel_clock
> +	 * but to get accurate results while coping with value ranges,
> +	 * have to do some fixed point math.
> +	 */
> +
> +	rt = et8ek8_get_row_time(sensor);
> +	rows = ((*us << 8) + (rt >> 1)) / rt;
> +
> +	if (rows > sensor->current_reglist->mode.max_exp)
> +		rows = sensor->current_reglist->mode.max_exp;
> +
> +	/* Set the exposure time to the rounded value */
> +	*us = (rt * rows + (1 << 7)) >> 8;
> +
> +	return rows;
> +}
> +
> +/*
> + * Convert exposure time in rows to microseconds
> + */
> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
> +{
> +	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
> +}
> +
> +/* Called to change the V4L2 gain control value. This function
> + * rounds and clamps the given value and updates the V4L2 control value.
> + * If power is on, also updates the sensor analog and digital gains.
> + * gain is in 0.1 EV (exposure value) units.
> + */
> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	struct et8ek8_gain new;
> +	int r;
> +
> +	new = et8ek8_gain_table[gain];
> +
> +	/* FIXME: optimise I2C writes! */
> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
> +				0x124a, new.analog >> 8);
> +	if (r)
> +		return r;
> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
> +				0x1249, new.analog & 0xff);
> +	if (r)
> +		return r;
> +
> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
> +				0x124d, new.digital >> 8);
> +	if (r)
> +		return r;
> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
> +				0x124c, new.digital & 0xff);
> +
> +	return r;
> +}
> +
> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
> +
> +	/* Values for normal mode */
> +	cbh_mode = 0;
> +	cbv_mode = 0;
> +	tp_mode  = 0;
> +	din_sw   = 0x00;
> +	r1420    = 0xF0;
> +
> +	if (mode != 0) {
> +		/* Test pattern mode */
> +		if (mode < 5) {
> +			cbh_mode = 1;
> +			cbv_mode = 1;
> +			tp_mode  = mode + 3;
> +		} else {
> +			cbh_mode = 0;
> +			cbv_mode = 0;
> +			tp_mode  = mode - 4 + 3;
> +		}
> +		din_sw   = 0x01;
> +		r1420    = 0xE0;
> +	}
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x111B,
> +				    tp_mode << 4);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1121,
> +				    cbh_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1124,
> +				    cbv_mode << 7);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x112C, din_sw);
> +	if (rval)
> +		goto out;
> +
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1420, r1420);
> +	if (rval)
> +		goto out;
> +
> +out:
> +	return rval;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 controls
> + */
> +
> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct et8ek8_sensor *sensor =
> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> +	const struct et8ek8_mode *mode = &sensor->current_reglist->mode;
> +
> +	switch (ctrl->id) {
> +	case ET8EK8_CID_USER_FRAME_WIDTH:
> +		ctrl->cur.val = mode->width;
> +		break;
> +	case ET8EK8_CID_USER_FRAME_HEIGHT:
> +		ctrl->cur.val = mode->height;
> +		break;
> +	case ET8EK8_CID_USER_VISIBLE_WIDTH:
> +		ctrl->cur.val = mode->window_width;
> +		break;
> +	case ET8EK8_CID_USER_VISIBLE_HEIGHT:
> +		ctrl->cur.val = mode->window_height;
> +		break;
> +	case ET8EK8_CID_USER_SENSITIVITY:
> +		ctrl->cur.val = mode->sensitivity;

All the values use for this control are 65536. Thus it provides no
information to the user space nor it controls anything. Please drop it.

> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct et8ek8_sensor *sensor =
> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +	int uninitialized_var(rows);
> +
> +	if (ctrl->id == V4L2_CID_EXPOSURE)
> +		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_GAIN:
> +		return et8ek8_set_gain(sensor, ctrl->val);
> +
> +	case V4L2_CID_EXPOSURE:
> +		return et8ek8_i2c_write_reg(client, ET8EK8_REG_16BIT, 0x1243,
> +					    swab16(rows));
> +
> +	case V4L2_CID_TEST_PATTERN:
> +		return et8ek8_set_test_pattern(sensor, ctrl->val);
> +
> +	case V4L2_CID_PIXEL_RATE:
> +		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
> +	.g_volatile_ctrl = et8ek8_get_ctrl,
> +	.s_ctrl = et8ek8_set_ctrl,
> +};
> +
> +static const char * const et8ek8_test_pattern_menu[] = {
> +	"Normal",
> +	"Vertical colorbar",
> +	"Horizontal colorbar",
> +	"Scale",
> +	"Ramp",
> +	"Small vertical colorbar",
> +	"Small horizontal colorbar",
> +	"Small scale",
> +	"Small ramp",
> +};
> +
> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
> +	{
> +		.id		= V4L2_CID_USER_ET8EK8_BASE,
> +		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
> +		.name		= "et8ek8 driver controls",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_WRITE_ONLY,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= ET8EK8_CID_USER_FRAME_WIDTH,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Frame width",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= ET8EK8_CID_USER_FRAME_HEIGHT,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Frame height",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= ET8EK8_CID_USER_VISIBLE_WIDTH,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Visible width",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= ET8EK8_CID_USER_VISIBLE_HEIGHT,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Visible height",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +	{
> +		.ops		= &et8ek8_ctrl_ops,
> +		.id		= ET8EK8_CID_USER_SENSITIVITY,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Sensitivity",
> +		.min		= 0,
> +		.max		= 0,
> +		.step		= 1,
> +		.def		= 0,
> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
> +				  V4L2_CTRL_FLAG_VOLATILE,
> +	},
> +};
> +
> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
> +{
> +	unsigned int i;
> +	u32 min, max;
> +
> +	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
> +			       ARRAY_SIZE(et8ek8_ctrls) + 2);
> +
> +	/* V4L2_CID_GAIN */
> +	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
> +			  1, 0);
> +
> +	/* V4L2_CID_EXPOSURE */
> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
> +	max = et8ek8_exposure_rows_to_us(sensor,
> +				sensor->current_reglist->mode.max_exp);
> +	sensor->exposure =
> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +				  V4L2_CID_EXPOSURE, min, max, min, max);
> +
> +	/* V4L2_CID_PIXEL_RATE */
> +	sensor->pixel_rate =
> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
> +		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
> +
> +	/* V4L2_CID_TEST_PATTERN */
> +	v4l2_ctrl_new_std_menu_items(&sensor->ctrl_handler,
> +				     &et8ek8_ctrl_ops, V4L2_CID_TEST_PATTERN,
> +				     ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
> +				     0, 0, et8ek8_test_pattern_menu);
> +
> +	/* V4L2_CID_USER_ET8EK8_BASE */
> +	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
> +		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
> +				     NULL);
> +
> +	if (sensor->ctrl_handler.error)
> +		return sensor->ctrl_handler.error;
> +
> +	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
> +	return 0;
> +}
> +
> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_ctrl *ctrl = sensor->exposure;
> +	struct et8ek8_mode *mode = &sensor->current_reglist->mode;
> +	u32 min, max, pixel_rate;
> +	static const int S = 8;
> +
> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
> +	max = et8ek8_exposure_rows_to_us(sensor, mode->max_exp);
> +
> +	/*
> +	 * Calculate average pixel clock per line. Assume buffers can spread
> +	 * the data over horizontal blanking time. Rounding upwards.
> +	 * Formula taken from stock Nokia N900 kernel
> +	 */
> +	pixel_rate = ((mode->pixel_clock + (1 << S) - 1) >> S) + mode->width;
> +	pixel_rate = mode->window_width * (pixel_rate - 1) / mode->width;
> +
> +	v4l2_ctrl_lock(ctrl);
> +	ctrl->minimum = min;
> +	ctrl->maximum = max;
> +	ctrl->step = min;
> +	ctrl->default_value = max;
> +	ctrl->val = max;
> +	ctrl->cur.val = max;
> +	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, pixel_rate << S);
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +
> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_subdev *subdev = &sensor->subdev;
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	int rval;
> +
> +	rval = et8ek8_i2c_write_regs(client, sensor->current_reglist->regs);
> +	if (rval)
> +		goto fail;
> +
> +	/* Controls set while the power to the sensor is turned off are saved
> +	 * but not applied to the hardware. Now that we're about to start
> +	 * streaming apply all the current values to the hardware.
> +	 */
> +	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
> +	if (rval)
> +		goto fail;
> +
> +	return 0;
> +
> +fail:
> +	dev_err(&client->dev, "sensor configuration failed\n");
> +	return rval;
> +}
> +
> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +
> +	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0xb0);
> +}
> +
> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> +
> +	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0x30);
> +}
> +
> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	int ret;
> +
> +	if (!streaming)
> +		return et8ek8_stream_off(sensor);
> +
> +	ret = et8ek8_configure(sensor);
> +	if (ret < 0)
> +		return ret;
> +
> +	return et8ek8_stream_on(sensor);
> +}
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
> +{
> +	int rval;
> +
> +	gpiod_set_value(sensor->reset, 0);
> +	udelay(1);
> +
> +	clk_disable_unprepare(sensor->ext_clk);
> +
> +	rval = regulator_disable(sensor->vana);
> +	return rval;
> +}
> +
> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
> +{
> +	struct v4l2_subdev *subdev = &sensor->subdev;
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	unsigned int xclk_freq;
> +	int val, rval;
> +
> +	rval = regulator_enable(sensor->vana);
> +	if (rval) {
> +		dev_err(&client->dev, "failed to enable vana regulator\n");
> +		return rval;
> +	}
> +
> +	if (sensor->current_reglist)
> +		xclk_freq = sensor->current_reglist->mode.ext_clock;
> +	else
> +		xclk_freq = sensor->xclk_freq;
> +
> +	rval = clk_set_rate(sensor->ext_clk, xclk_freq);
> +	if (rval < 0) {
> +		dev_err(&client->dev, "unable to set extclk clock freq to %u\n",
> +			xclk_freq);
> +		goto out;
> +	}
> +	rval = clk_prepare_enable(sensor->ext_clk);
> +	if (rval < 0) {
> +		dev_err(&client->dev, "failed to enable extclk\n");
> +		goto out;
> +	}
> +
> +	if (rval)
> +		goto out;
> +
> +	udelay(10); /* I wish this is a good value */
> +
> +	gpiod_set_value(sensor->reset, 1);
> +
> +	msleep(5000 * 1000 / xclk_freq + 1); /* Wait 5000 cycles */
> +
> +	rval = et8ek8_i2c_reglist_find_write(client, &meta_reglist,
> +					     ET8EK8_REGLIST_POWERON);
> +	if (rval)
> +		goto out;
> +
> +#ifdef USE_CRC
> +	rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT, 0x1263, &val);
> +	if (rval)
> +		goto out;
> +#if USE_CRC /* TODO get crc setting from DT */
> +	val |= BIT(4);
> +#else
> +	val &= ~BIT(4);
> +#endif
> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1263, val);
> +	if (rval)
> +		goto out;
> +#endif
> +
> +out:
> +	if (rval)
> +		et8ek8_power_off(sensor);
> +
> +	return rval;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev video operations
> + */
> +#define MAX_FMTS 4
> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct et8ek8_reglist **list =
> +			et8ek8_reglist_first(&meta_reglist);
> +	u32 pixelformat[MAX_FMTS];
> +	int npixelformat = 0;
> +
> +	if (code->index >= MAX_FMTS)
> +		return -EINVAL;
> +
> +	for (; *list; list++) {
> +		struct et8ek8_mode *mode = &(*list)->mode;
> +		int i;
> +
> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
> +			continue;
> +
> +		for (i = 0; i < npixelformat; i++) {
> +			if (pixelformat[i] == mode->pixel_format)
> +				break;
> +		}
> +		if (i != npixelformat)
> +			continue;
> +
> +		if (code->index == npixelformat) {
> +			if (mode->pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
> +				code->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
> +			else
> +				code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
> +			return 0;
> +		}
> +
> +		pixelformat[npixelformat] = mode->pixel_format;
> +		npixelformat++;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct et8ek8_reglist **list =
> +			et8ek8_reglist_first(&meta_reglist);
> +	struct v4l2_mbus_framefmt format;
> +	int cmp_width = INT_MAX;
> +	int cmp_height = INT_MAX;
> +	int index = fse->index;
> +
> +	for (; *list; list++) {
> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
> +			continue;
> +
> +		et8ek8_reglist_to_mbus(*list, &format);
> +		if (fse->code != format.code)
> +			continue;
> +
> +		/* Assume that the modes are grouped by frame size. */
> +		if (format.width == cmp_width && format.height == cmp_height)
> +			continue;
> +
> +		cmp_width = format.width;
> +		cmp_height = format.height;
> +
> +		if (index-- == 0) {
> +			fse->min_width = format.width;
> +			fse->min_height = format.height;
> +			fse->max_width = format.width;
> +			fse->max_height = format.height;
> +			return 0;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_interval_enum *fie)
> +{
> +	struct et8ek8_reglist **list =
> +			et8ek8_reglist_first(&meta_reglist);
> +	struct v4l2_mbus_framefmt format;
> +	int index = fie->index;
> +
> +	for (; *list; list++) {
> +		struct et8ek8_mode *mode = &(*list)->mode;
> +
> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
> +			continue;
> +
> +		et8ek8_reglist_to_mbus(*list, &format);
> +		if (fie->code != format.code)
> +			continue;
> +
> +		if (fie->width != format.width || fie->height != format.height)
> +			continue;
> +
> +		if (index-- == 0) {
> +			fie->interval = mode->timeperframe;
> +			return 0;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
> +			struct v4l2_subdev_pad_config *cfg,
> +			unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &sensor->format;
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct v4l2_mbus_framefmt *format;
> +	struct et8ek8_reglist *reglist;
> +
> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	reglist = et8ek8_reglist_find_mode_fmt(&meta_reglist,
> +					     &fmt->format);
> +	et8ek8_reglist_to_mbus(reglist, &fmt->format);
> +	*format = fmt->format;
> +
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +		sensor->current_reglist = reglist;
> +		et8ek8_update_controls(sensor);
> +	}
> +
> +	return 0;
> +}
> +
> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	memset(fi, 0, sizeof(*fi));
> +	fi->interval = sensor->current_reglist->mode.timeperframe;
> +
> +	return 0;
> +}
> +
> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct et8ek8_reglist *reglist;
> +
> +	reglist = et8ek8_reglist_find_mode_ival(&meta_reglist,
> +					      sensor->current_reglist,
> +					      &fi->interval);
> +
> +	if (!reglist)
> +		return -EINVAL;
> +
> +	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
> +		return -EINVAL;
> +
> +	sensor->current_reglist = reglist;
> +	et8ek8_update_controls(sensor);
> +
> +	return 0;
> +}
> +
> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
> +	unsigned int offset = 0;
> +	u8 *ptr  = sensor->priv_mem;
> +	int rval = 0;
> +
> +	/* Read the EEPROM window-by-window, each window 8 bytes */
> +	do {
> +		u8 buffer[PRIV_MEM_WIN_SIZE];
> +		struct i2c_msg msg;
> +		int bytes, i;
> +		int ofs;
> +
> +		/* Set the current window */
> +		rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x0001,
> +					    0xe0 | (offset >> 3));
> +		if (rval < 0)
> +			goto out;
> +
> +		/* Wait for status bit */
> +		for (i = 0; i < 1000; ++i) {
> +			u32 status;
> +
> +			rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
> +						   0x0003, &status);
> +			if (rval < 0)
> +				goto out;
> +			if ((status & 0x08) == 0)
> +				break;
> +			usleep_range(1000, 2000);
> +		};
> +
> +		if (i == 1000) {
> +			rval = -EIO;
> +			goto out;
> +		}
> +
> +		/* Read window, 8 bytes at once, and copy to user space */
> +		ofs = offset & 0x07;	/* Offset within this window */
> +		bytes = length + ofs > 8 ? 8-ofs : length;
> +		msg.addr = client->addr;
> +		msg.flags = 0;
> +		msg.len = 2;
> +		msg.buf = buffer;
> +		ofs += PRIV_MEM_START_REG;
> +		buffer[0] = (u8)(ofs >> 8);
> +		buffer[1] = (u8)(ofs & 0xFF);
> +		rval = i2c_transfer(client->adapter, &msg, 1);
> +		if (rval < 0)
> +			goto out;
> +		mdelay(ET8EK8_I2C_DELAY);
> +		msg.addr = client->addr;
> +		msg.len = bytes;
> +		msg.flags = I2C_M_RD;
> +		msg.buf = buffer;
> +		memset(buffer, 0, sizeof(buffer));
> +		rval = i2c_transfer(client->adapter, &msg, 1);
> +		if (rval < 0)
> +			goto out;
> +		rval = 0;
> +		memcpy(ptr, buffer, bytes);
> +
> +		length -= bytes;
> +		offset += bytes;
> +		ptr    += bytes;
> +	} while (length > 0);
> +
> +out:
> +	return rval;
> +}
> +
> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	int rval, rev_l, rev_h;
> +
> +	rval = et8ek8_power_on(sensor);
> +	if (rval) {
> +		dev_err(&client->dev, "could not power on\n");
> +		return rval;
> +	}
> +
> +	if (et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
> +				REG_REVISION_NUMBER_L, &rev_l) != 0
> +	    || et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
> +				   REG_REVISION_NUMBER_H, &rev_h) != 0) {
> +		dev_err(&client->dev,
> +			"no et8ek8 sensor detected\n");
> +		rval = -ENODEV;
> +		goto out_poweroff;
> +	}
> +	sensor->version = (rev_h << 8) + rev_l;
> +	if (sensor->version != ET8EK8_REV_1
> +	    && sensor->version != ET8EK8_REV_2)
> +		dev_info(&client->dev,
> +			 "unknown version 0x%x detected, continuing anyway\n",
> +			 sensor->version);
> +
> +	rval = et8ek8_reglist_import(client, &meta_reglist);
> +	if (rval) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, import failed\n",
> +			ET8EK8_NAME);
> +		goto out_poweroff;
> +	}
> +
> +	sensor->current_reglist =
> +		et8ek8_reglist_find_type(&meta_reglist,
> +				       ET8EK8_REGLIST_MODE);
> +	if (!sensor->current_reglist) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, no mode found\n",
> +			ET8EK8_NAME);
> +		rval = -ENODEV;
> +		goto out_poweroff;
> +	}
> +
> +	et8ek8_reglist_to_mbus(sensor->current_reglist, &sensor->format);
> +
> +	rval = et8ek8_i2c_reglist_find_write(client,
> +					   &meta_reglist,
> +					   ET8EK8_REGLIST_POWERON);
> +	if (rval) {
> +		dev_err(&client->dev,
> +			"invalid register list %s, no POWERON mode found\n",
> +			ET8EK8_NAME);
> +		goto out_poweroff;
> +	}
> +	rval = et8ek8_stream_on(sensor); /* Needed to be able to read EEPROM */
> +	if (rval)
> +		goto out_poweroff;
> +	rval = et8ek8_g_priv_mem(subdev);
> +	if (rval)
> +		dev_warn(&client->dev,
> +			"can not read OTP (EEPROM) memory from sensor\n");
> +	rval = et8ek8_stream_off(sensor);
> +	if (rval)
> +		goto out_poweroff;
> +
> +	rval = et8ek8_power_off(sensor);
> +	if (rval)
> +		goto out_poweroff;
> +
> +	return 0;
> +
> +out_poweroff:
> +	et8ek8_power_off(sensor);
> +
> +	return rval;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * sysfs attributes
> + */
> +static ssize_t
> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
> +		     char *buf)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
> +#error PAGE_SIZE too small!
> +#endif
> +
> +	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
> +
> +	return ET8EK8_PRIV_MEM_SIZE;
> +}
> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
> +
> +/* --------------------------------------------------------------------------
> + * V4L2 subdev core operations
> + */
> +
> +static int
> +et8ek8_registered(struct v4l2_subdev *subdev)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +	struct v4l2_mbus_framefmt *format;
> +	int rval;
> +
> +	dev_dbg(&client->dev, "registered!");
> +
> +	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
> +		dev_err(&client->dev, "could not register sysfs entry\n");
> +		return -EBUSY;
> +	}
> +
> +	rval = et8ek8_dev_init(subdev);
> +	if (rval)
> +		return rval;
> +
> +	rval = et8ek8_init_controls(sensor);
> +	if (rval) {
> +		dev_err(&client->dev, "controls initialization failed\n");
> +		return rval;
> +	}
> +
> +	format = __et8ek8_get_pad_format(sensor, NULL, 0,
> +					 V4L2_SUBDEV_FORMAT_ACTIVE);
> +	return 0;
> +}
> +
> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
> +{
> +	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
> +}
> +
> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +	int ret = 0;
> +
> +	mutex_lock(&sensor->power_lock);
> +
> +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
> +	 * update the power state.
> +	 */
> +	if (sensor->power_count == !on) {
> +		ret = __et8ek8_set_power(sensor, !!on);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	/* Update the power count. */
> +	sensor->power_count += on ? 1 : -1;
> +	WARN_ON(sensor->power_count < 0);
> +
> +done:
> +	mutex_unlock(&sensor->power_lock);
> +	return ret;
> +}
> +
> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	struct et8ek8_reglist *reglist;
> +
> +	reglist = et8ek8_reglist_find_type(&meta_reglist, ET8EK8_REGLIST_MODE);
> +	format = __et8ek8_get_pad_format(sensor, fh->pad, 0,
> +					 V4L2_SUBDEV_FORMAT_TRY);
> +	et8ek8_reglist_to_mbus(reglist, format);
> +
> +	return et8ek8_set_power(sd, true);
> +}
> +
> +static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return et8ek8_set_power(sd, false);
> +}
> +
> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
> +	.s_stream = et8ek8_s_stream,
> +	.g_frame_interval = et8ek8_get_frame_interval,
> +	.s_frame_interval = et8ek8_set_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
> +	.s_power = et8ek8_set_power,
> +};
> +
> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
> +	.enum_mbus_code = et8ek8_enum_mbus_code,
> +	.enum_frame_size = et8ek8_enum_frame_size,
> +	.enum_frame_interval = et8ek8_enum_frame_ival,
> +	.get_fmt = et8ek8_get_pad_format,
> +	.set_fmt = et8ek8_set_pad_format,
> +};
> +
> +static const struct v4l2_subdev_ops et8ek8_ops = {
> +	.core = &et8ek8_core_ops,
> +	.video = &et8ek8_video_ops,
> +	.pad = &et8ek8_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
> +	.registered = et8ek8_registered,
> +	.open = et8ek8_open,
> +	.close = et8ek8_close,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * I2C driver
> + */
> +#ifdef CONFIG_PM
> +
> +static int et8ek8_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (!sensor->power_count)
> +		return 0;
> +
> +	return __et8ek8_set_power(sensor, false);
> +}
> +
> +static int et8ek8_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (!sensor->power_count)
> +		return 0;
> +
> +	return __et8ek8_set_power(sensor, true);
> +}
> +
> +static const struct dev_pm_ops et8ek8_pm_ops = {
> +	.suspend	= et8ek8_suspend,
> +	.resume		= et8ek8_resume,
> +};
> +
> +#else
> +
> +#define et8ek8_pm_ops	NULL
> +
> +#endif /* CONFIG_PM */
> +
> +static int et8ek8_probe(struct i2c_client *client,
> +			const struct i2c_device_id *devid)
> +{
> +	struct et8ek8_sensor *sensor;
> +	struct device *dev = &client->dev;
> +	int ret;
> +
> +	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
> +	if (!sensor)
> +		return -ENOMEM;
> +
> +	sensor->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(sensor->reset)) {
> +		dev_dbg(&client->dev, "could not request reset gpio\n");
> +		return PTR_ERR(sensor->reset);
> +	}
> +
> +	sensor->vana = devm_regulator_get(dev, "vana");
> +	if (IS_ERR(sensor->vana)) {
> +		dev_err(&client->dev, "could not get regulator for vana\n");
> +		return PTR_ERR(sensor->vana);
> +	}
> +
> +	sensor->ext_clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(sensor->ext_clk)) {
> +		dev_err(&client->dev, "could not get clock\n");
> +		return PTR_ERR(sensor->ext_clk);
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "clock-frequency",
> +				   &sensor->xclk_freq);
> +	if (ret) {
> +		dev_warn(dev, "can't get clock-frequency\n");
> +		return ret;
> +	}
> +
> +	mutex_init(&sensor->power_lock);
> +
> +	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
> +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	sensor->subdev.internal_ops = &et8ek8_internal_ops;
> +
> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "media entity init failed!\n");
> +		return ret;
> +	}
> +
> +	ret = v4l2_async_register_subdev(&sensor->subdev);
> +	if (ret < 0) {
> +		media_entity_cleanup(&sensor->subdev.entity);
> +		return ret;
> +	}
> +
> +	dev_dbg(dev, "initialized!\n");
> +
> +	return 0;
> +}
> +
> +static int __exit et8ek8_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
> +
> +	if (sensor->power_count) {
> +		gpiod_set_value(sensor->reset, 0);
> +		clk_disable_unprepare(sensor->ext_clk);
> +		sensor->power_count = 0;
> +	}
> +
> +	v4l2_device_unregister_subdev(&sensor->subdev);
> +	device_remove_file(&client->dev, &dev_attr_priv_mem);
> +	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
> +	media_entity_cleanup(&sensor->subdev.entity);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id et8ek8_of_table[] = {
> +	{ .compatible = "toshiba,et8ek8" },
> +	{ },
> +};
> +
> +static const struct i2c_device_id et8ek8_id_table[] = {
> +	{ ET8EK8_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
> +
> +static struct i2c_driver et8ek8_i2c_driver = {
> +	.driver		= {
> +		.name	= ET8EK8_NAME,
> +		.pm	= &et8ek8_pm_ops,
> +		.of_match_table	= et8ek8_of_table,
> +	},
> +	.probe		= et8ek8_probe,
> +	.remove		= __exit_p(et8ek8_remove),
> +	.id_table	= et8ek8_id_table,
> +};
> +
> +module_i2c_driver(et8ek8_i2c_driver);
> +
> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/i2c/et8ek8/et8ek8_mode.c b/drivers/media/i2c/et8ek8/et8ek8_mode.c
> new file mode 100644
> index 0000000..e5c367b
> --- /dev/null
> +++ b/drivers/media/i2c/et8ek8/et8ek8_mode.c
> @@ -0,0 +1,591 @@
> +/*
> + * et8ek8_mode.c
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +
> +#include "et8ek8_reg.h"
> +
> +/*
> + *
> + * Stingray sensor mode settings for Scooby
> + *
> + *
> + */
> +
> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
> +static struct et8ek8_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 640 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_POWERON,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 2016,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 1207
> +		},
> +		.max_exp = 2012,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		/* Need to set firstly */
> +		{ ET8EK8_REG_8BIT, 0x126C, 0xCC },
> +		/* Strobe and Data of CCP2 delay are minimized. */
> +		{ ET8EK8_REG_8BIT, 0x1269, 0x00 },
> +		/* Refined value of Min H_COUNT  */
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
> +		/* Frequency of SPCK setting (SPCK=MRCK) */
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
> +		{ ET8EK8_REG_8BIT, 0x1241, 0x94 },
> +		{ ET8EK8_REG_8BIT, 0x1242, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x124B, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1255, 0xFF },
> +		{ ET8EK8_REG_8BIT, 0x1256, 0x9F },
> +		{ ET8EK8_REG_8BIT, 0x1258, 0x00 },
> +		/* From parallel out to serial out */
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 },
> +		/* From w/ embeded data to w/o embeded data */
> +		{ ET8EK8_REG_8BIT, 0x125E, 0xC0 },
> +		/* CCP2 out is from STOP to ACTIVE */
> +		{ ET8EK8_REG_8BIT, 0x1263, 0x98 },
> +		{ ET8EK8_REG_8BIT, 0x1268, 0xC6 },
> +		{ ET8EK8_REG_8BIT, 0x1434, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1163, 0x44 },
> +		{ ET8EK8_REG_8BIT, 0x1166, 0x29 },
> +		{ ET8EK8_REG_8BIT, 0x1140, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x1011, 0x24 },
> +		{ ET8EK8_REG_8BIT, 0x1151, 0x80 },
> +		{ ET8EK8_REG_8BIT, 0x1152, 0x23 },
> +		/* Initial setting for improvement2 of lower frequency noise */
> +		{ ET8EK8_REG_8BIT, 0x1014, 0x05 },
> +		{ ET8EK8_REG_8BIT, 0x1033, 0x06 },
> +		{ ET8EK8_REG_8BIT, 0x1034, 0x79 },
> +		{ ET8EK8_REG_8BIT, 0x1423, 0x3F },
> +		{ ET8EK8_REG_8BIT, 0x1424, 0x3F },
> +		{ ET8EK8_REG_8BIT, 0x1426, 0x00 },
> +		/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
> +		{ ET8EK8_REG_8BIT, 0x1439, 0x00 },
> +		/* Switch of blemish correction (0d:disable / 1d:enable) */
> +		{ ET8EK8_REG_8BIT, 0x161F, 0x60 },
> +		/* Switch of auto noise correction (0d:disable / 1d:enable) */
> +		{ ET8EK8_REG_8BIT, 0x1634, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1646, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1648, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x113E, 0x01 },
> +		{ ET8EK8_REG_8BIT, 0x113F, 0x22 },
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
> +static struct et8ek8_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 560 MHz
> + * VCO        = 560 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 128 (3072)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 175
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 6
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3072,
> +		.height = 2016,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 1292
> +		},
> +		.max_exp = 2012,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x57 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x06 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x80 }, /* <-changed to v14 7E->80 */
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
> +static struct et8ek8_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {
> +/* (without the +1)
> + * SPCK       = 96.5333333333333 MHz
> + * CCP2       = 579.2 MHz
> + * VCO        = 579.2 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 133 (3192)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 181
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 5
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3192,
> +		.height = 1008,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 96533333,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 3000
> +		},
> +		.max_exp = 1004,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x5A },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode4_SVGA_864x656_29.88fps */
> +static struct et8ek8_reglist mode4_svga_864x656_29_88fps = {
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 320 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 166 (3984)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3984,
> +		.height = 672,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 864,
> +		.window_height = 656,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2988
> +		},
> +		.max_exp = 668,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x62 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x62 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0xA6 },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode5_VGA_648x492_29.93fps */
> +static struct et8ek8_reglist mode5_vga_648x492_29_93fps = {
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 320 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 221 (5304)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 5304,
> +		.height = 504,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 648,
> +		.window_height = 492,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2993
> +		},
> +		.max_exp = 500,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode2_16VGA_2592x1968_3.99fps */
> +static struct et8ek8_reglist mode2_16vga_2592x1968_3_99fps = {
> +/* (without the +1)
> + * SPCK       = 80 MHz
> + * CCP2       = 640 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 254 (6096)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 6096,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 2592,
> +		.window_height = 1968,
> +		.pixel_clock = 80000000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 399
> +		},
> +		.max_exp = 6092,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0xFE },
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode_648x492_5fps */
> +static struct et8ek8_reglist mode_648x492_5fps = {
> +/* (without the +1)
> + * SPCK       = 13.3333333333333 MHz
> + * CCP2       = 53.3333333333333 MHz
> + * VCO        = 640 MHz
> + * VCOUNT     = 84 (2016)
> + * HCOUNT     = 221 (5304)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 200
> + * VCO_DIV    = 5
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 1
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 5304,
> +		.height = 504,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 648,
> +		.window_height = 492,
> +		.pixel_clock = 13333333,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 499
> +		},
> +		.max_exp = 500,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x57 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode3_4VGA_1296x984_5fps */
> +static struct et8ek8_reglist mode3_4vga_1296x984_5fps = {
> +/* (without the +1)
> + * SPCK       = 49.4 MHz
> + * CCP2       = 395.2 MHz
> + * VCO        = 790.4 MHz
> + * VCOUNT     = 250 (6000)
> + * HCOUNT     = 137 (3288)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 247
> + * VCO_DIV    = 1
> + * SPCK_DIV   = 7
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3288,
> +		.height = 3000,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 49400000,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 501
> +		},
> +		.max_exp = 2996,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x7B },
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x17 },
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
> +		{ ET8EK8_REG_8BIT, 0x1222, 0xFA },
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
> +static struct et8ek8_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {
> +/* (without the +1)
> + * SPCK       = 84.2666666666667 MHz
> + * CCP2       = 505.6 MHz
> + * VCO        = 505.6 MHz
> + * VCOUNT     = 88 (2112)
> + * HCOUNT     = 133 (3192)
> + * CKREF_DIV  = 2
> + * CKVAR_DIV  = 158
> + * VCO_DIV    = 0
> + * SPCK_DIV   = 5
> + * MRCK_DIV   = 7
> + * LVDSCK_DIV = 0
> + */
> +	.type = ET8EK8_REGLIST_MODE,
> +	.mode = {
> +		.sensor_width = 2592,
> +		.sensor_height = 1968,
> +		.sensor_window_origin_x = 0,
> +		.sensor_window_origin_y = 0,
> +		.sensor_window_width = 2592,
> +		.sensor_window_height = 1968,
> +		.width = 3192,
> +		.height = 1056,
> +		.window_origin_x = 0,
> +		.window_origin_y = 0,
> +		.window_width = 1296,
> +		.window_height = 984,
> +		.pixel_clock = 84266667,
> +		.ext_clock = 9600000,
> +		.timeperframe = {
> +			.numerator = 100,
> +			.denominator = 2500
> +		},
> +		.max_exp = 1052,
> +		/* .max_gain = 0, */
> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.sensitivity = 65536
> +	},
> +	.regs = {
> +		{ ET8EK8_REG_8BIT, 0x1239, 0x4F },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x1222, 0x58 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },	/*        */
> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 },	/*        */
> +		{ ET8EK8_REG_TERM, 0, 0}
> +	}
> +};
> +
> +struct et8ek8_meta_reglist meta_reglist = {
> +	.magic   = ET8EK8_MAGIC,
> +	.version = "V14 03-June-2008",
> +	.reglist = {
> +		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
> +		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
> +		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
> +		{ .ptr = &mode4_svga_864x656_29_88fps },
> +		{ .ptr = &mode5_vga_648x492_29_93fps },
> +		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
> +		{ .ptr = &mode_648x492_5fps },
> +		{ .ptr = &mode3_4vga_1296x984_5fps },
> +		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
> +		{ .ptr = 0 }
> +	}
> +};
> diff --git a/drivers/media/i2c/et8ek8/et8ek8_reg.h b/drivers/media/i2c/et8ek8/et8ek8_reg.h
> new file mode 100644
> index 0000000..1f138b0
> --- /dev/null
> +++ b/drivers/media/i2c/et8ek8/et8ek8_reg.h
> @@ -0,0 +1,100 @@
> +/*
> + * et8ek8.h
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>

tuukkat76@gmail.com

> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +
> +#ifndef ET8EK8REGS_H
> +#define ET8EK8REGS_H
> +
> +#include <linux/i2c.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/v4l2-subdev.h>
> +
> +struct v4l2_mbus_framefmt;
> +struct v4l2_subdev_pad_mbus_code_enum;
> +
> +#define ET8EK8_MAGIC			0x531A0002
> +
> +struct et8ek8_mode {
> +	/* Physical sensor resolution and current image window */
> +	__u16 sensor_width;
> +	__u16 sensor_height;
> +	__u16 sensor_window_origin_x;
> +	__u16 sensor_window_origin_y;
> +	__u16 sensor_window_width;
> +	__u16 sensor_window_height;
> +
> +	/* Image data coming from sensor (after scaling) */
> +	__u16 width;
> +	__u16 height;
> +	__u16 window_origin_x;
> +	__u16 window_origin_y;
> +	__u16 window_width;
> +	__u16 window_height;
> +
> +	__u32 pixel_clock;		/* in Hz */
> +	__u32 ext_clock;		/* in Hz */
> +	struct v4l2_fract timeperframe;
> +	__u32 max_exp;			/* Maximum exposure value */
> +	__u32 pixel_format;		/* V4L2_PIX_FMT_xxx */
> +	__u32 sensitivity;		/* 16.16 fixed point */
> +};
> +
> +#define ET8EK8_REG_8BIT			1
> +#define ET8EK8_REG_16BIT		2
> +#define ET8EK8_REG_32BIT		4
> +#define ET8EK8_REG_DELAY		100
> +#define ET8EK8_REG_TERM			0xff
> +struct et8ek8_reg {
> +	u16 type;
> +	u16 reg;			/* 16-bit offset */
> +	u32 val;			/* 8/16/32-bit value */
> +};
> +
> +/* Possible struct smia_reglist types. */
> +#define ET8EK8_REGLIST_STANDBY		0
> +#define ET8EK8_REGLIST_POWERON		1
> +#define ET8EK8_REGLIST_RESUME		2
> +#define ET8EK8_REGLIST_STREAMON		3
> +#define ET8EK8_REGLIST_STREAMOFF	4
> +#define ET8EK8_REGLIST_DISABLED		5
> +
> +#define ET8EK8_REGLIST_MODE		10
> +
> +#define ET8EK8_REGLIST_LSC_ENABLE	100
> +#define ET8EK8_REGLIST_LSC_DISABLE	101
> +#define ET8EK8_REGLIST_ANR_ENABLE	102
> +#define ET8EK8_REGLIST_ANR_DISABLE	103
> +
> +struct et8ek8_reglist {
> +	u32 type;
> +	struct et8ek8_mode mode;
> +	struct et8ek8_reg regs[];
> +};
> +
> +#define ET8EK8_MAX_LEN			32
> +struct et8ek8_meta_reglist {
> +	u32 magic;

The field is useless as is ET8EK8_MAGIC. It was there to avoid loading
garbage by accident by using request_firmware(). Please remove.

> +	char version[ET8EK8_MAX_LEN];
> +	union {
> +		struct et8ek8_reglist *ptr;
> +	} reglist[];
> +};
> +
> +extern struct et8ek8_meta_reglist meta_reglist;
> +
> +#endif /* ET8EK8REGS */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index b6a357a..97a08d8 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -180,6 +180,11 @@ enum v4l2_colorfx {
>   * We reserve 16 controls for this driver. */
>  #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
>  
> +/* The base for the et8ek8 driver controls.
> + * We reserve 16 controls for this driver.
> + */
> +#define V4L2_CID_USER_ET8EK8_BASE		(V4L2_CID_USER_BASE + 0x1090)
> +
>  /* MPEG-class control IDs */
>  /* The MPEG controls are applicable to all codec controls
>   * and the 'MPEG' part of the define is historical */

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-24 11:19             ` Pavel Machek
@ 2016-06-04 19:16               ` Ivaylo Dimitrov
  2016-06-06  9:04               ` Sylwester Nawrocki
  1 sibling, 0 replies; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-06-04 19:16 UTC (permalink / raw)
  To: Pavel Machek; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi,

On 24.05.2016 14:19, Pavel Machek wrote:
> Hi!
>
>> The sensor is found in Nokia N900 main camera
>>
>> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
>
>> +/*
>> + * A buffered write method that puts the wanted register write
>> + * commands in a message list and passes the list to the i2c framework
>> + */
>> +static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
>> +					  const struct et8ek8_reg *wnext,
>> +					  int cnt)
>> +{
>> +	/* FIXME: check how big cnt is */
>> +	struct i2c_msg msg[cnt];
>> +	unsigned char data[cnt][6];
>
> Uff, no, variable length arrays in the kernel. No, I don't think that
> should be done. Rather allocate maximum length here and then check its
>> cnt?
>

ok

>> +/*
>> + * Write a list of registers to i2c device.
>> + *
>> + * The list of registers is terminated by ET8EK8_REG_TERM.
>> + * Returns zero if successful, or non-zero otherwise.
>> + */
>> +static int et8ek8_i2c_write_regs(struct i2c_client *client,
>> +				 const struct et8ek8_reg reglist[])
>> +{
>> +	int r, cnt = 0;
>> +	const struct et8ek8_reg *next, *wnext;
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +
>> +	if (reglist == NULL)
>
> (!reglist) ? :-). Actually, you can keep your preffered style there,
> but maybe ammount of if (something that can not happen) return
> ... should be reduced. Noone should ever call this without valid
> reglist or client->adapter, right?
>

Well, I always prefer if (!var) style, will change all the code to use it.

However, I prefer to keep the check here, I guess it compiles to two 
instructions not in some hot path.

>> +		return -EINVAL;
>> +
>> +	/* Initialize list pointers to the start of the list */
>> +	next = wnext = reglist;
>> +
>> +	do {
>> +		/*
>> +		 * We have to go through the list to figure out how
>> +		 * many regular writes we have in a row
>> +		 */
>> +		while (next->type != ET8EK8_REG_TERM
>> +		       && next->type != ET8EK8_REG_DELAY) {
>> +			/*
>> +			 * Here we check that the actual length fields
>> +			 * are valid
>> +			 */
>> +			if (next->type != ET8EK8_REG_8BIT
>> +			    &&  next->type != ET8EK8_REG_16BIT) {
>
> Extra space after &&
>

ok

>> +				dev_err(&client->dev,
>> +					"Invalid value on entry %d 0x%x\n",
>> +					cnt, next->type);
>> +				return -EINVAL;
>> +			}
>
> And maybe this could be just BUG_ON().
>
Yeah, makes sense, will change it.

>> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_fmt(
>> +		struct et8ek8_meta_reglist *meta,
>> +		struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
>> +	struct et8ek8_reglist *best_match = NULL;
>> +	struct et8ek8_reglist *best_other = NULL;
>> +	struct v4l2_mbus_framefmt format;
>> +	unsigned int max_dist_match = (unsigned int)-1;
>> +	unsigned int max_dist_other = (unsigned int)-1;
>> +
>> +	/* Find the mode with the closest image size. The distance between
>> +	 * image sizes is the size in pixels of the non-overlapping regions
>
> You may want to run checkpatch. I guess it will complain. I doubt it
> matters much :-).
>

I always run it before submitting :), but it didn't complain here. Or 
I've missed it. Anyway, going to fix that comment.

>> +	while (meta->reglist[nlists].ptr != NULL)
>> +		nlists++;
>
> ...!= NULL) can be removed. ... here and in other places.
>

ok

>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x111B,
>> +				    tp_mode << 4);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1121,
>> +				    cbh_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1124,
>> +				    cbv_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x112C, din_sw);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1420, r1420);
>> +	if (rval)
>> +		goto out;
>> +
>> +out:
>> +	return rval;
>> +}
>
> Goto out when all out does is return is a bit of overkill.
>

yeah :)

>> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	const struct et8ek8_mode *mode = &sensor->current_reglist->mode;
>> +
>> +	switch (ctrl->id) {
>> +	case ET8EK8_CID_USER_FRAME_WIDTH:
>> +		ctrl->cur.val = mode->width;
>> +		break;
>> +	case ET8EK8_CID_USER_FRAME_HEIGHT:
>> +		ctrl->cur.val = mode->height;
>> +		break;
>> +	case ET8EK8_CID_USER_VISIBLE_WIDTH:
>> +		ctrl->cur.val = mode->window_width;
>> +		break;
>> +	case ET8EK8_CID_USER_VISIBLE_HEIGHT:
>> +		ctrl->cur.val = mode->window_height;
>> +		break;
>> +	case ET8EK8_CID_USER_SENSITIVITY:
>> +		ctrl->cur.val = mode->sensitivity;
>> +		break;
>> +	}
>
> default: return -EINVAL ?
>

I checked a couple of other driver, nowhere there is such a case, I 
guess framework takes care of that.

>> +	/*
>> +	 * Calculate average pixel clock per line. Assume buffers can spread
>> +	 * the data over horizontal blanking time. Rounding upwards.
>> +	 * Formula taken from stock Nokia N900 kernel
>> +	 */
>
> "kernel."  ?
>

ok

>> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
>> +{
>> +	int rval;
>> +
>> +	gpiod_set_value(sensor->reset, 0);
>> +	udelay(1);
>> +
>> +	clk_disable_unprepare(sensor->ext_clk);
>> +
>> +	rval = regulator_disable(sensor->vana);
>> +	return rval;
>> +}
>
> get rid of rval, return directly?
>

ok

>> +	udelay(10); /* I wish this is a good value */
>
> Me too ;-).
>

Well, this is a comment from the original driver authors, I hope all 
their wishes to fulfil. I will leave that as it is and wait for Sakari's 
comment on it.

>> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
>> +	unsigned int offset = 0;
>> +	u8 *ptr  = sensor->priv_mem;
>> +	int rval = 0;
>> +
>> +	/* Read the EEPROM window-by-window, each window 8 bytes */
>> +	do {
>> +		u8 buffer[PRIV_MEM_WIN_SIZE];
>> +		struct i2c_msg msg;
>> +		int bytes, i;
>> +		int ofs;
>> +
>> +		/* Set the current window */
>> +		rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x0001,
>> +					    0xe0 | (offset >> 3));
>> +		if (rval < 0)
>> +			goto out;
>
> out: only does return, cleaning it up here will help readability.
>
>> +		/* Wait for status bit */
>> +		for (i = 0; i < 1000; ++i) {
>> +			u32 status;
>> +
>> +			rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
>> +						   0x0003, &status);
>> +			if (rval < 0)
>> +				goto out;
>> +			if ((status & 0x08) == 0)
>> +				break;
>> +			usleep_range(1000, 2000);
>> +		};
>> +
>> +		if (i == 1000) {
>> +			rval = -EIO;
>> +			goto out;
>> +		}
>
> Especially here.
>
>> +		if (rval < 0)
>> +			goto out;
>> +		rval = 0;
>
> And here.
>

ok

>
>> +#ifndef ET8EK8REGS_H
>> +#define ET8EK8REGS_H
>> +
>> +#include <linux/i2c.h>
>> +#include <linux/types.h>
>> +#include <linux/videodev2.h>
>> +#include <linux/v4l2-subdev.h>
>> +
>> +struct v4l2_mbus_framefmt;
>> +struct v4l2_subdev_pad_mbus_code_enum;
>> +
>> +#define ET8EK8_MAGIC			0x531A0002
>> +
>> +struct et8ek8_mode {
>> +	/* Physical sensor resolution and current image window */
>> +	__u16 sensor_width;
>> +	__u16 sensor_height;
>
> Is this exported to userspace?
>

afaik no, but see Sakari's reply.

Many thanks for the review.
Ivo

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-25 21:45             ` Sakari Ailus
@ 2016-06-04 19:54               ` Ivaylo Dimitrov
  2016-06-09 23:13                 ` Sakari Ailus
  0 siblings, 1 reply; 102+ messages in thread
From: Ivaylo Dimitrov @ 2016-06-04 19:54 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: sre, pali.rohar, pavel, linux-media

Hi,

On 26.05.2016 00:45, Sakari Ailus wrote:
> Hi Ivaylo,
>
> I've got some comments here but I haven't reviewed everything yet. What's
> missing is
>
> - the user space interface for selecting the sensor configuration "mode",
>
> - passing information on the sensor configuration to the user space.
>
> I'll try to take a look at those some time in the near future.
>

ok

>
> I very much appreciate your work towards finally upstreaming this! :-)
>
> On Tue, May 03, 2016 at 05:50:04PM +0300, Ivaylo Dimitrov wrote:
>> The sensor is found in Nokia N900 main camera
>>
>> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
>> ---
>>   .../bindings/media/i2c/toshiba,et8ek8.txt          |   53 +
>>   drivers/media/i2c/Kconfig                          |    1 +
>>   drivers/media/i2c/Makefile                         |    1 +
>>   drivers/media/i2c/et8ek8/Kconfig                   |    6 +
>>   drivers/media/i2c/et8ek8/Makefile                  |    2 +
>>   drivers/media/i2c/et8ek8/et8ek8_driver.c           | 1711 ++++++++++++++++++++
>>   drivers/media/i2c/et8ek8/et8ek8_mode.c             |  591 +++++++
>>   drivers/media/i2c/et8ek8/et8ek8_reg.h              |  100 ++
>>   include/uapi/linux/v4l2-controls.h                 |    5 +
>>   9 files changed, 2470 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
>>   create mode 100644 drivers/media/i2c/et8ek8/Kconfig
>>   create mode 100644 drivers/media/i2c/et8ek8/Makefile
>>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_driver.c
>>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_mode.c
>>   create mode 100644 drivers/media/i2c/et8ek8/et8ek8_reg.h
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
>> new file mode 100644
>> index 0000000..55f712c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
>> @@ -0,0 +1,53 @@
>> +Toshiba et8ek8 5MP sensor
>> +
>> +Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device
>> +
>> +More detailed documentation can be found in
>> +Documentation/devicetree/bindings/media/video-interfaces.txt .
>> +
>> +
>> +Mandatory properties
>> +--------------------
>> +
>> +- compatible: "toshiba,et8ek8"
>> +- reg: I2C address (0x3e, or an alternative address)
>> +- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor
>> +  dependent).
>
> As these are bindings for a particular sensor, 2,8 volts it is.
>
> The sensor has also a digital voltage supply but it might be controlled by
> the same GPIO which controls the CCP2 switch. Ugly stuff. Perhaps we could
> just omit that here.
>

ok

>> +- clocks: External clock to the sensor
>> +- clock-frequency: Frequency of the external clock to the sensor
>> +
>> +
>> +Optional properties
>> +-------------------
>> +
>> +- reset-gpios: XSHUTDOWN GPIO
>
> I guess this should be mandatory.
>

yeah. Also, I will change xxx-lanes to optional

>> +
>> +
>> +Endpoint node mandatory properties
>> +----------------------------------
>> +
>> +- clock-lanes: <0>
>> +- data-lanes: <1..n>
>> +- remote-endpoint: A phandle to the bus receiver's endpoint node.
>> +
>> +
>> +Example
>> +-------
>> +
>> +&i2c3 {
>> +	clock-frequency = <400000>;
>> +
>> +	cam1: camera@3e {
>> +		compatible = "toshiba,et8ek8";
>> +		reg = <0x3e>;
>> +		vana-supply = <&vaux4>;
>> +		clocks = <&isp 0>;
>> +		clock-frequency = <9600000>;
>> +		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
>> +		port {
>> +			csi_cam1: endpoint {
>> +				remote-endpoint = <&csi_out1>;
>> +			};
>> +		};
>> +	};
>> +};
>
> Please split the DT documentation from the driver.
>

Split it how? Send as series [patch 1] - driver, [patch 2] - doc?

> I remember having discussed showing the module in DT with Sebastian but I
> couldn't find the patches anywhere. We currently consider the lens and
> sensor entirely separate, the module has not been shown in software as
> there's been nothing to control it.
>

Not sure what am I supposed to do with that comment :)

> Sebastian: do you still have those patches around somewhere?
>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 993dc50..e964787 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -629,6 +629,7 @@ config VIDEO_S5K5BAF
>>   	  camera sensor with an embedded SoC image signal processor.
>>
>>   source "drivers/media/i2c/smiapp/Kconfig"
>> +source "drivers/media/i2c/et8ek8/Kconfig"
>>
>>   config VIDEO_S5C73M3
>>   	tristate "Samsung S5C73M3 sensor support"
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 94f2c99..907b180 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -2,6 +2,7 @@ msp3400-objs	:=	msp3400-driver.o msp3400-kthreads.o
>>   obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
>>
>>   obj-$(CONFIG_VIDEO_SMIAPP)	+= smiapp/
>> +obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8/
>>   obj-$(CONFIG_VIDEO_CX25840) += cx25840/
>>   obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
>>   obj-y				+= soc_camera/
>> diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig
>> new file mode 100644
>> index 0000000..1439936
>> --- /dev/null
>> +++ b/drivers/media/i2c/et8ek8/Kconfig
>> @@ -0,0 +1,6 @@
>> +config VIDEO_ET8EK8
>> +	tristate "ET8EK8 camera sensor support"
>> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	---help---
>> +	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
>> +	  It is used for example in Nokia N900 (RX-51).
>> diff --git a/drivers/media/i2c/et8ek8/Makefile b/drivers/media/i2c/et8ek8/Makefile
>> new file mode 100644
>> index 0000000..66d1b7d
>> --- /dev/null
>> +++ b/drivers/media/i2c/et8ek8/Makefile
>> @@ -0,0 +1,2 @@
>> +et8ek8-objs			+= et8ek8_mode.o et8ek8_driver.o
>> +obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8.o
>> diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
>> new file mode 100644
>> index 0000000..1eaef78
>> --- /dev/null
>> +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
>> @@ -0,0 +1,1711 @@
>> +/*
>> + * et8ek8_driver.c
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>
> tuukkat76@gmail.com
>

ok

>> + *
>> + * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
>> + *
>> + * This driver is based on the Micron MT9T012 camera imager driver
>> + * (C) Texas Instruments.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/gpio/consumer.h>
>
> Alphabetical order, please.
>

ok

>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <linux/sort.h>
>> +#include <linux/version.h>
>
> Is linux/version.h needed?
>

no, will remove it

>> +#include <linux/v4l2-mediabus.h>
>> +
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#include "et8ek8_reg.h"
>> +
>> +#define ET8EK8_NAME		"et8ek8"
>> +#define ET8EK8_PRIV_MEM_SIZE	128
>> +
>> +#define ET8EK8_CID_USER_FRAME_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 1)
>> +#define ET8EK8_CID_USER_FRAME_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 2)
>> +#define ET8EK8_CID_USER_VISIBLE_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 3)
>> +#define ET8EK8_CID_USER_VISIBLE_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 4)
>> +#define ET8EK8_CID_USER_SENSITIVITY	(V4L2_CID_USER_ET8EK8_BASE + 5)
>
> If you have custom controls,
>

hmm?

>> +
>> +struct et8ek8_sensor {
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pad;
>> +	struct v4l2_mbus_framefmt format;
>> +	struct gpio_desc *reset;
>> +	struct regulator *vana;
>> +	struct clk *ext_clk;
>> +	u32 xclk_freq;
>> +
>> +	u16 version;
>> +
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +	struct v4l2_ctrl *exposure;
>> +	struct v4l2_ctrl *pixel_rate;
>> +	struct et8ek8_reglist *current_reglist;
>> +
>> +	u8 priv_mem[ET8EK8_PRIV_MEM_SIZE];
>> +
>> +	struct mutex power_lock;
>> +	int power_count;
>> +};
>> +
>> +#define to_et8ek8_sensor(sd)	container_of(sd, struct et8ek8_sensor, subdev)
>> +
>> +enum et8ek8_versions {
>> +	ET8EK8_REV_1 = 0x0001,
>> +	ET8EK8_REV_2,
>> +};
>> +
>> +/*
>> + * This table describes what should be written to the sensor register
>> + * for each gain value. The gain(index in the table) is in terms of
>> + * 0.1EV, i.e. 10 indexes in the table give 2 time more gain [0] in
>> + * the *analog gain, [1] in the digital gain
>> + *
>> + * Analog gain [dB] = 20*log10(regvalue/32); 0x20..0x100
>> + */
>> +static struct et8ek8_gain {
>> +	u16 analog;
>> +	u16 digital;
>> +} const et8ek8_gain_table[] = {
>> +	{ 32,    0},  /* x1 */
>> +	{ 34,    0},
>> +	{ 37,    0},
>> +	{ 39,    0},
>> +	{ 42,    0},
>> +	{ 45,    0},
>> +	{ 49,    0},
>> +	{ 52,    0},
>> +	{ 56,    0},
>> +	{ 60,    0},
>> +	{ 64,    0},  /* x2 */
>> +	{ 69,    0},
>> +	{ 74,    0},
>> +	{ 79,    0},
>> +	{ 84,    0},
>> +	{ 91,    0},
>> +	{ 97,    0},
>> +	{104,    0},
>> +	{111,    0},
>> +	{119,    0},
>> +	{128,    0},  /* x4 */
>> +	{137,    0},
>> +	{147,    0},
>> +	{158,    0},
>> +	{169,    0},
>> +	{181,    0},
>> +	{194,    0},
>> +	{208,    0},
>> +	{223,    0},
>> +	{239,    0},
>> +	{256,    0},  /* x8 */
>> +	{256,   73},
>> +	{256,  152},
>> +	{256,  236},
>> +	{256,  327},
>> +	{256,  424},
>> +	{256,  528},
>> +	{256,  639},
>> +	{256,  758},
>> +	{256,  886},
>> +	{256, 1023},  /* x16 */
>> +};
>> +
>> +/* Register definitions */
>> +#define REG_REVISION_NUMBER_L	0x1200
>> +#define REG_REVISION_NUMBER_H	0x1201
>> +
>> +#define PRIV_MEM_START_REG	0x0008
>> +#define PRIV_MEM_WIN_SIZE	8
>> +
>> +#define ET8EK8_I2C_DELAY	3	/* msec delay b/w accesses */
>> +
>> +#define USE_CRC			1
>> +
>> +/*
>> + *
>> + * Register access helpers
>> + *
>> + */
>> +
>> +/*
>> + * Read a 8/16/32-bit i2c register.  The value is returned in 'val'.
>> + * Returns zero if successful, or non-zero otherwise.
>> + */
>> +static int et8ek8_i2c_read_reg(struct i2c_client *client, u16 data_length,
>> +			       u16 reg, u32 *val)
>> +{
>> +	int r;
>> +	struct i2c_msg msg[1];
>> +	unsigned char data[4];
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
>> +		return -EINVAL;
>> +
>> +	msg->addr = client->addr;
>> +	msg->flags = 0;
>> +	msg->len = 2;
>> +	msg->buf = data;
>> +
>> +	/* high byte goes out first */
>> +	data[0] = (u8) (reg >> 8);
>> +	data[1] = (u8) (reg & 0xff);
>> +	r = i2c_transfer(client->adapter, msg, 1);
>> +	if (r < 0)
>> +		goto err;
>> +
>> +	msg->len = data_length;
>> +	msg->flags = I2C_M_RD;
>> +	r = i2c_transfer(client->adapter, msg, 1);
>> +	if (r < 0)
>> +		goto err;
>> +
>> +	*val = 0;
>> +	/* high byte comes first */
>> +	if (data_length == ET8EK8_REG_8BIT)
>> +		*val = data[0];
>> +	else
>> +		*val = (data[0] << 8) + data[1];
>> +
>> +	return 0;
>> +
>> +err:
>> +	dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r);
>> +
>> +	return r;
>> +}
>> +
>> +static void et8ek8_i2c_create_msg(struct i2c_client *client, u16 len, u16 reg,
>> +				  u32 val, struct i2c_msg *msg,
>> +				  unsigned char *buf)
>> +{
>> +	msg->addr = client->addr;
>> +	msg->flags = 0; /* Write */
>> +	msg->len = 2 + len;
>> +	msg->buf = buf;
>> +
>> +	/* high byte goes out first */
>> +	buf[0] = (u8) (reg >> 8);
>> +	buf[1] = (u8) (reg & 0xff);
>> +
>> +	switch (len) {
>> +	case ET8EK8_REG_8BIT:
>> +		buf[2] = (u8) (val) & 0xff;
>> +		break;
>> +	case ET8EK8_REG_16BIT:
>> +		buf[2] = (u8) (val >> 8) & 0xff;
>> +		buf[3] = (u8) (val & 0xff);
>> +		break;
>> +	case ET8EK8_REG_32BIT:
>> +		buf[2] = (u8) (val >> 24) & 0xff;
>> +		buf[3] = (u8) (val >> 16) & 0xff;
>> +		buf[4] = (u8) (val >> 8) & 0xff;
>> +		buf[5] = (u8) (val & 0xff);
>> +		break;
>> +	default:
>> +		BUG();
>> +	}
>> +}
>> +
>> +/*
>> + * A buffered write method that puts the wanted register write
>> + * commands in a message list and passes the list to the i2c framework
>> + */
>> +static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
>> +					  const struct et8ek8_reg *wnext,
>> +					  int cnt)
>> +{
>> +	/* FIXME: check how big cnt is */
>> +	struct i2c_msg msg[cnt];
>> +	unsigned char data[cnt][6];
>> +	int wcnt = 0;
>> +	u16 reg, data_length;
>> +	u32 val;
>> +
>> +	/* Create new write messages for all writes */
>> +	while (wcnt < cnt) {
>> +		data_length = wnext->type;
>> +		reg = wnext->reg;
>> +		val = wnext->val;
>> +		wnext++;
>> +
>> +		et8ek8_i2c_create_msg(client, data_length, reg,
>> +				    val, &msg[wcnt], &data[wcnt][0]);
>> +
>> +		/* Update write count */
>> +		wcnt++;
>> +	}
>> +
>> +	/* Now we send everything ... */
>> +	return i2c_transfer(client->adapter, msg, wcnt);
>> +}
>> +
>> +/*
>> + * Write a list of registers to i2c device.
>> + *
>> + * The list of registers is terminated by ET8EK8_REG_TERM.
>> + * Returns zero if successful, or non-zero otherwise.
>> + */
>> +static int et8ek8_i2c_write_regs(struct i2c_client *client,
>> +				 const struct et8ek8_reg reglist[])
>> +{
>> +	int r, cnt = 0;
>> +	const struct et8ek8_reg *next, *wnext;
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +
>> +	if (reglist == NULL)
>> +		return -EINVAL;
>> +
>> +	/* Initialize list pointers to the start of the list */
>> +	next = wnext = reglist;
>> +
>> +	do {
>> +		/*
>> +		 * We have to go through the list to figure out how
>> +		 * many regular writes we have in a row
>> +		 */
>> +		while (next->type != ET8EK8_REG_TERM
>> +		       && next->type != ET8EK8_REG_DELAY) {
>> +			/*
>> +			 * Here we check that the actual length fields
>> +			 * are valid
>> +			 */
>> +			if (next->type != ET8EK8_REG_8BIT
>> +			    &&  next->type != ET8EK8_REG_16BIT) {
>> +				dev_err(&client->dev,
>> +					"Invalid value on entry %d 0x%x\n",
>> +					cnt, next->type);
>> +				return -EINVAL;
>> +			}
>> +
>> +			/*
>> +			 * Increment count of successive writes and
>> +			 * read pointer
>> +			 */
>> +			cnt++;
>> +			next++;
>> +		}
>> +
>> +		/* Now we start writing ... */
>> +		r = et8ek8_i2c_buffered_write_regs(client, wnext, cnt);
>> +
>> +		/* ... and then check that everything was OK */
>> +		if (r < 0) {
>> +			dev_err(&client->dev, "i2c transfer error !!!\n");
>> +			return r;
>> +		}
>> +
>> +		/*
>> +		 * If we ran into a sleep statement when going through
>> +		 * the list, this is where we snooze for the required time
>> +		 */
>> +		if (next->type == ET8EK8_REG_DELAY) {
>> +			set_current_state(TASK_UNINTERRUPTIBLE);
>> +			schedule_timeout(msecs_to_jiffies(next->val));
>> +			/*
>> +			 * ZZZ ...
>> +			 * Update list pointers and cnt and start over ...
>> +			 */
>> +			next++;
>> +			wnext = next;
>> +			cnt = 0;
>> +		}
>> +	} while (next->type != ET8EK8_REG_TERM);
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Write to a 8/16-bit register.
>> + * Returns zero if successful, or non-zero otherwise.
>> + */
>> +static int et8ek8_i2c_write_reg(struct i2c_client *client, u16 data_length,
>> +				u16 reg, u32 val)
>> +{
>> +	int r;
>> +	struct i2c_msg msg[1];
>> +	unsigned char data[6];
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +	if (data_length != ET8EK8_REG_8BIT && data_length != ET8EK8_REG_16BIT)
>> +		return -EINVAL;
>> +
>> +	et8ek8_i2c_create_msg(client, data_length, reg, val, msg, data);
>> +
>> +	r = i2c_transfer(client->adapter, msg, 1);
>> +	if (r < 0)
>> +		dev_err(&client->dev,
>> +			"wrote 0x%x to offset 0x%x error %d\n", val, reg, r);
>> +	else
>> +		r = 0; /* on success i2c_transfer() return messages trasfered */
>> +
>> +	return r;
>> +}
>> +
>> +static struct et8ek8_reglist *et8ek8_reglist_find_type(
>> +		struct et8ek8_meta_reglist *meta,
>> +		u16 type)
>> +{
>> +	struct et8ek8_reglist **next = &meta->reglist[0].ptr;
>> +
>> +	while (*next) {
>> +		if ((*next)->type == type)
>> +			return *next;
>> +
>> +		next++;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static int et8ek8_i2c_reglist_find_write(struct i2c_client *client,
>> +					 struct et8ek8_meta_reglist *meta,
>> +					 u16 type)
>> +{
>> +	struct et8ek8_reglist *reglist;
>> +
>> +	reglist = et8ek8_reglist_find_type(meta, type);
>> +	if (IS_ERR(reglist))
>> +		return PTR_ERR(reglist);
>> +
>> +	return et8ek8_i2c_write_regs(client, reglist->regs);
>> +}
>> +
>> +static struct et8ek8_reglist **et8ek8_reglist_first(
>> +		struct et8ek8_meta_reglist *meta)
>> +{
>> +	return &meta->reglist[0].ptr;
>> +}
>> +
>> +static void et8ek8_reglist_to_mbus(const struct et8ek8_reglist *reglist,
>> +				   struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	fmt->width = reglist->mode.window_width;
>> +	fmt->height = reglist->mode.window_height;
>> +
>> +	if (reglist->mode.pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
>> +		fmt->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
>> +	else
>> +		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
>> +}
>> +
>> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_fmt(
>> +		struct et8ek8_meta_reglist *meta,
>> +		struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
>> +	struct et8ek8_reglist *best_match = NULL;
>> +	struct et8ek8_reglist *best_other = NULL;
>> +	struct v4l2_mbus_framefmt format;
>> +	unsigned int max_dist_match = (unsigned int)-1;
>> +	unsigned int max_dist_other = (unsigned int)-1;
>> +
>> +	/* Find the mode with the closest image size. The distance between
>> +	 * image sizes is the size in pixels of the non-overlapping regions
>> +	 * between the requested size and the frame-specified size.
>> +	 *
>> +	 * Store both the closest mode that matches the requested format, and
>> +	 * the closest mode for all other formats. The best match is returned
>> +	 * if found, otherwise the best mode with a non-matching format is
>> +	 * returned.
>> +	 */
>> +	for (; *list; list++) {
>> +		unsigned int dist;
>> +
>> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
>> +			continue;
>> +
>> +		et8ek8_reglist_to_mbus(*list, &format);
>> +
>> +		dist = min(fmt->width, format.width)
>> +		     * min(fmt->height, format.height);
>> +		dist = format.width * format.height
>> +		     + fmt->width * fmt->height - 2 * dist;
>> +
>> +
>> +		if (fmt->code == format.code) {
>> +			if (dist < max_dist_match || best_match == NULL) {
>> +				best_match = *list;
>> +				max_dist_match = dist;
>> +			}
>> +		} else {
>> +			if (dist < max_dist_other || best_other == NULL) {
>> +				best_other = *list;
>> +				max_dist_other = dist;
>> +			}
>> +		}
>> +	}
>> +
>> +	return best_match ? best_match : best_other;
>> +}
>> +
>> +#define TIMEPERFRAME_AVG_FPS(t)						\
>> +	(((t).denominator + ((t).numerator >> 1)) / (t).numerator)
>> +
>> +static struct et8ek8_reglist *et8ek8_reglist_find_mode_ival(
>> +		struct et8ek8_meta_reglist *meta,
>> +		struct et8ek8_reglist *current_reglist,
>> +		struct v4l2_fract *timeperframe)
>> +{
>> +	int fps = TIMEPERFRAME_AVG_FPS(*timeperframe);
>> +	struct et8ek8_reglist **list = et8ek8_reglist_first(meta);
>> +	struct et8ek8_mode *current_mode = &current_reglist->mode;
>> +
>> +	for (; *list; list++) {
>> +		struct et8ek8_mode *mode = &(*list)->mode;
>> +
>> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
>> +			continue;
>> +
>> +		if (mode->window_width != current_mode->window_width
>> +		    || mode->window_height != current_mode->window_height)
>> +			continue;
>> +
>> +		if (TIMEPERFRAME_AVG_FPS(mode->timeperframe) == fps)
>> +			return *list;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static int et8ek8_reglist_cmp(const void *a, const void *b)
>> +{
>> +	const struct et8ek8_reglist **list1 = (const struct et8ek8_reglist **)a,
>> +		**list2 = (const struct et8ek8_reglist **)b;
>> +
>> +	/* Put real modes in the beginning. */
>> +	if ((*list1)->type == ET8EK8_REGLIST_MODE &&
>> +	    (*list2)->type != ET8EK8_REGLIST_MODE)
>> +		return -1;
>> +	if ((*list1)->type != ET8EK8_REGLIST_MODE &&
>> +	    (*list2)->type == ET8EK8_REGLIST_MODE)
>> +		return 1;
>> +
>> +	/* Descending width. */
>> +	if ((*list1)->mode.window_width > (*list2)->mode.window_width)
>> +		return -1;
>> +	if ((*list1)->mode.window_width < (*list2)->mode.window_width)
>> +		return 1;
>> +
>> +	if ((*list1)->mode.window_height > (*list2)->mode.window_height)
>> +		return -1;
>> +	if ((*list1)->mode.window_height < (*list2)->mode.window_height)
>> +		return 1;
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_reglist_import(struct i2c_client *client,
>> +				 struct et8ek8_meta_reglist *meta)
>> +{
>> +	uintptr_t nlists = 0;
>> +
>> +	if (meta->magic != ET8EK8_MAGIC) {
>
> No need to check this anymore.
>

ok. will remove the struct member as well.

>> +		dev_err(&client->dev,
>> +			"invalid camera sensor firmware (0x%08X)\n",
>> +			meta->magic);
>> +		return -EILSEQ;
>> +	}
>> +
>> +	dev_info(&client->dev, "meta_reglist version %s\n", meta->version);
>> +
>> +	while (meta->reglist[nlists].ptr != NULL)
>> +		nlists++;
>> +
>> +	if (!nlists)
>> +		return -EINVAL;
>> +
>> +	sort(&meta->reglist[0].ptr, nlists, sizeof(meta->reglist[0].ptr),
>> +	     et8ek8_reglist_cmp, NULL);
>> +
>> +	nlists = 0;
>> +	while (meta->reglist[nlists].ptr != NULL) {
>
> I guess you could just loop over nlists. You just counted the entries above.
>

Yeah. Also, I wonder why is nlists uintptr_t. Will fix it.

>> +		struct et8ek8_reglist *list;
>> +
>> +		list = meta->reglist[nlists].ptr;
>> +
>> +		dev_dbg(&client->dev,
>> +		       "%s: type %d\tw %d\th %d\tfmt %x\tival %d/%d\tptr %p\n",
>> +		       __func__,
>> +		       list->type,
>> +		       list->mode.window_width, list->mode.window_height,
>> +		       list->mode.pixel_format,
>> +		       list->mode.timeperframe.numerator,
>> +		       list->mode.timeperframe.denominator,
>> +		       (void *)meta->reglist[nlists].ptr);
>> +
>> +		nlists++;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Return time of one row in microseconds, .8 fixed point format.
>> + * If the sensor is not set to any mode, return zero.
>> + */
>> +static int et8ek8_get_row_time(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	if (!sensor->current_reglist)
>> +		return 0;
>> +
>> +	clock = sensor->current_reglist->mode.pixel_clock;
>> +	clock = (clock + (1 << 9)) >> 10;
>> +	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
>> +	rt = (rt + (clock >> 1)) / clock;
>> +
>> +	return rt;
>> +}
>> +
>> +/*
>> + * Convert exposure time `us' to rows. Modify `us' to make it to
>> + * correspond to the actual exposure time.
>> + */
>> +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
>> +{
>> +	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
>> +	unsigned int rt;	/* Row time in .8 fixed point */
>> +
>> +	/* Assume that the maximum exposure time is at most ~8 s,
>> +	 * and the maximum width (with blanking) ~8000 pixels.
>> +	 * The formula here is in principle as simple as
>> +	 *    rows = exptime / 1e6 / width * pixel_clock
>> +	 * but to get accurate results while coping with value ranges,
>> +	 * have to do some fixed point math.
>> +	 */
>> +
>> +	rt = et8ek8_get_row_time(sensor);
>> +	rows = ((*us << 8) + (rt >> 1)) / rt;
>> +
>> +	if (rows > sensor->current_reglist->mode.max_exp)
>> +		rows = sensor->current_reglist->mode.max_exp;
>> +
>> +	/* Set the exposure time to the rounded value */
>> +	*us = (rt * rows + (1 << 7)) >> 8;
>> +
>> +	return rows;
>> +}
>> +
>> +/*
>> + * Convert exposure time in rows to microseconds
>> + */
>> +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
>> +{
>> +	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
>> +}
>> +
>> +/* Called to change the V4L2 gain control value. This function
>> + * rounds and clamps the given value and updates the V4L2 control value.
>> + * If power is on, also updates the sensor analog and digital gains.
>> + * gain is in 0.1 EV (exposure value) units.
>> + */
>> +static int et8ek8_set_gain(struct et8ek8_sensor *sensor, s32 gain)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	struct et8ek8_gain new;
>> +	int r;
>> +
>> +	new = et8ek8_gain_table[gain];
>> +
>> +	/* FIXME: optimise I2C writes! */
>> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
>> +				0x124a, new.analog >> 8);
>> +	if (r)
>> +		return r;
>> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
>> +				0x1249, new.analog & 0xff);
>> +	if (r)
>> +		return r;
>> +
>> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
>> +				0x124d, new.digital >> 8);
>> +	if (r)
>> +		return r;
>> +	r = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT,
>> +				0x124c, new.digital & 0xff);
>> +
>> +	return r;
>> +}
>> +
>> +static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
>> +
>> +	/* Values for normal mode */
>> +	cbh_mode = 0;
>> +	cbv_mode = 0;
>> +	tp_mode  = 0;
>> +	din_sw   = 0x00;
>> +	r1420    = 0xF0;
>> +
>> +	if (mode != 0) {
>> +		/* Test pattern mode */
>> +		if (mode < 5) {
>> +			cbh_mode = 1;
>> +			cbv_mode = 1;
>> +			tp_mode  = mode + 3;
>> +		} else {
>> +			cbh_mode = 0;
>> +			cbv_mode = 0;
>> +			tp_mode  = mode - 4 + 3;
>> +		}
>> +		din_sw   = 0x01;
>> +		r1420    = 0xE0;
>> +	}
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x111B,
>> +				    tp_mode << 4);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1121,
>> +				    cbh_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1124,
>> +				    cbv_mode << 7);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x112C, din_sw);
>> +	if (rval)
>> +		goto out;
>> +
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1420, r1420);
>> +	if (rval)
>> +		goto out;
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 controls
>> + */
>> +
>> +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	const struct et8ek8_mode *mode = &sensor->current_reglist->mode;
>> +
>> +	switch (ctrl->id) {
>> +	case ET8EK8_CID_USER_FRAME_WIDTH:
>> +		ctrl->cur.val = mode->width;
>> +		break;
>> +	case ET8EK8_CID_USER_FRAME_HEIGHT:
>> +		ctrl->cur.val = mode->height;
>> +		break;
>> +	case ET8EK8_CID_USER_VISIBLE_WIDTH:
>> +		ctrl->cur.val = mode->window_width;
>> +		break;
>> +	case ET8EK8_CID_USER_VISIBLE_HEIGHT:
>> +		ctrl->cur.val = mode->window_height;
>> +		break;
>> +	case ET8EK8_CID_USER_SENSITIVITY:
>> +		ctrl->cur.val = mode->sensitivity;
>
> All the values use for this control are 65536. Thus it provides no
> information to the user space nor it controls anything. Please drop it.
>

ok

>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct et8ek8_sensor *sensor =
>> +		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +	int uninitialized_var(rows);
>> +
>> +	if (ctrl->id == V4L2_CID_EXPOSURE)
>> +		rows = et8ek8_exposure_us_to_rows(sensor, (u32 *)&ctrl->val);
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_GAIN:
>> +		return et8ek8_set_gain(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_EXPOSURE:
>> +		return et8ek8_i2c_write_reg(client, ET8EK8_REG_16BIT, 0x1243,
>> +					    swab16(rows));
>> +
>> +	case V4L2_CID_TEST_PATTERN:
>> +		return et8ek8_set_test_pattern(sensor, ctrl->val);
>> +
>> +	case V4L2_CID_PIXEL_RATE:
>> +		/* For v4l2_ctrl_s_ctrl_int64() used internally. */
>> +		return 0;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
>> +	.g_volatile_ctrl = et8ek8_get_ctrl,
>> +	.s_ctrl = et8ek8_set_ctrl,
>> +};
>> +
>> +static const char * const et8ek8_test_pattern_menu[] = {
>> +	"Normal",
>> +	"Vertical colorbar",
>> +	"Horizontal colorbar",
>> +	"Scale",
>> +	"Ramp",
>> +	"Small vertical colorbar",
>> +	"Small horizontal colorbar",
>> +	"Small scale",
>> +	"Small ramp",
>> +};
>> +
>> +static const struct v4l2_ctrl_config et8ek8_ctrls[] = {
>> +	{
>> +		.id		= V4L2_CID_USER_ET8EK8_BASE,
>> +		.type		= V4L2_CTRL_TYPE_CTRL_CLASS,
>> +		.name		= "et8ek8 driver controls",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_WRITE_ONLY,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= ET8EK8_CID_USER_FRAME_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= ET8EK8_CID_USER_FRAME_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Frame height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= ET8EK8_CID_USER_VISIBLE_WIDTH,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible width",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= ET8EK8_CID_USER_VISIBLE_HEIGHT,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Visible height",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +	{
>> +		.ops		= &et8ek8_ctrl_ops,
>> +		.id		= ET8EK8_CID_USER_SENSITIVITY,
>> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>> +		.name		= "Sensitivity",
>> +		.min		= 0,
>> +		.max		= 0,
>> +		.step		= 1,
>> +		.def		= 0,
>> +		.flags		= V4L2_CTRL_FLAG_READ_ONLY |
>> +				  V4L2_CTRL_FLAG_VOLATILE,
>> +	},
>> +};
>> +
>> +static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	unsigned int i;
>> +	u32 min, max;
>> +
>> +	v4l2_ctrl_handler_init(&sensor->ctrl_handler,
>> +			       ARRAY_SIZE(et8ek8_ctrls) + 2);
>> +
>> +	/* V4L2_CID_GAIN */
>> +	v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +			  V4L2_CID_GAIN, 0, ARRAY_SIZE(et8ek8_gain_table) - 1,
>> +			  1, 0);
>> +
>> +	/* V4L2_CID_EXPOSURE */
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor,
>> +				sensor->current_reglist->mode.max_exp);
>> +	sensor->exposure =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +				  V4L2_CID_EXPOSURE, min, max, min, max);
>> +
>> +	/* V4L2_CID_PIXEL_RATE */
>> +	sensor->pixel_rate =
>> +		v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
>> +		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
>> +
>> +	/* V4L2_CID_TEST_PATTERN */
>> +	v4l2_ctrl_new_std_menu_items(&sensor->ctrl_handler,
>> +				     &et8ek8_ctrl_ops, V4L2_CID_TEST_PATTERN,
>> +				     ARRAY_SIZE(et8ek8_test_pattern_menu) - 1,
>> +				     0, 0, et8ek8_test_pattern_menu);
>> +
>> +	/* V4L2_CID_USER_ET8EK8_BASE */
>> +	for (i = 0; i < ARRAY_SIZE(et8ek8_ctrls); ++i)
>> +		v4l2_ctrl_new_custom(&sensor->ctrl_handler, &et8ek8_ctrls[i],
>> +				     NULL);
>> +
>> +	if (sensor->ctrl_handler.error)
>> +		return sensor->ctrl_handler.error;
>> +
>> +	sensor->subdev.ctrl_handler = &sensor->ctrl_handler;
>> +	return 0;
>> +}
>> +
>> +static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_ctrl *ctrl = sensor->exposure;
>> +	struct et8ek8_mode *mode = &sensor->current_reglist->mode;
>> +	u32 min, max, pixel_rate;
>> +	static const int S = 8;
>> +
>> +	min = et8ek8_exposure_rows_to_us(sensor, 1);
>> +	max = et8ek8_exposure_rows_to_us(sensor, mode->max_exp);
>> +
>> +	/*
>> +	 * Calculate average pixel clock per line. Assume buffers can spread
>> +	 * the data over horizontal blanking time. Rounding upwards.
>> +	 * Formula taken from stock Nokia N900 kernel
>> +	 */
>> +	pixel_rate = ((mode->pixel_clock + (1 << S) - 1) >> S) + mode->width;
>> +	pixel_rate = mode->window_width * (pixel_rate - 1) / mode->width;
>> +
>> +	v4l2_ctrl_lock(ctrl);
>> +	ctrl->minimum = min;
>> +	ctrl->maximum = max;
>> +	ctrl->step = min;
>> +	ctrl->default_value = max;
>> +	ctrl->val = max;
>> +	ctrl->cur.val = max;
>> +	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, pixel_rate << S);
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +
>> +static int et8ek8_configure(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval;
>> +
>> +	rval = et8ek8_i2c_write_regs(client, sensor->current_reglist->regs);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	/* Controls set while the power to the sensor is turned off are saved
>> +	 * but not applied to the hardware. Now that we're about to start
>> +	 * streaming apply all the current values to the hardware.
>> +	 */
>> +	rval = v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
>> +	if (rval)
>> +		goto fail;
>> +
>> +	return 0;
>> +
>> +fail:
>> +	dev_err(&client->dev, "sensor configuration failed\n");
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_stream_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0xb0);
>> +}
>> +
>> +static int et8ek8_stream_off(struct et8ek8_sensor *sensor)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
>> +
>> +	return et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1252, 0x30);
>> +}
>> +
>> +static int et8ek8_s_stream(struct v4l2_subdev *subdev, int streaming)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret;
>> +
>> +	if (!streaming)
>> +		return et8ek8_stream_off(sensor);
>> +
>> +	ret = et8ek8_configure(sensor);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return et8ek8_stream_on(sensor);
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev operations
>> + */
>> +
>> +static int et8ek8_power_off(struct et8ek8_sensor *sensor)
>> +{
>> +	int rval;
>> +
>> +	gpiod_set_value(sensor->reset, 0);
>> +	udelay(1);
>> +
>> +	clk_disable_unprepare(sensor->ext_clk);
>> +
>> +	rval = regulator_disable(sensor->vana);
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_power_on(struct et8ek8_sensor *sensor)
>> +{
>> +	struct v4l2_subdev *subdev = &sensor->subdev;
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int xclk_freq;
>> +	int val, rval;
>> +
>> +	rval = regulator_enable(sensor->vana);
>> +	if (rval) {
>> +		dev_err(&client->dev, "failed to enable vana regulator\n");
>> +		return rval;
>> +	}
>> +
>> +	if (sensor->current_reglist)
>> +		xclk_freq = sensor->current_reglist->mode.ext_clock;
>> +	else
>> +		xclk_freq = sensor->xclk_freq;
>> +
>> +	rval = clk_set_rate(sensor->ext_clk, xclk_freq);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev, "unable to set extclk clock freq to %u\n",
>> +			xclk_freq);
>> +		goto out;
>> +	}
>> +	rval = clk_prepare_enable(sensor->ext_clk);
>> +	if (rval < 0) {
>> +		dev_err(&client->dev, "failed to enable extclk\n");
>> +		goto out;
>> +	}
>> +
>> +	if (rval)
>> +		goto out;
>> +
>> +	udelay(10); /* I wish this is a good value */
>> +
>> +	gpiod_set_value(sensor->reset, 1);
>> +
>> +	msleep(5000 * 1000 / xclk_freq + 1); /* Wait 5000 cycles */
>> +
>> +	rval = et8ek8_i2c_reglist_find_write(client, &meta_reglist,
>> +					     ET8EK8_REGLIST_POWERON);
>> +	if (rval)
>> +		goto out;
>> +
>> +#ifdef USE_CRC
>> +	rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT, 0x1263, &val);
>> +	if (rval)
>> +		goto out;
>> +#if USE_CRC /* TODO get crc setting from DT */
>> +	val |= BIT(4);
>> +#else
>> +	val &= ~BIT(4);
>> +#endif
>> +	rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x1263, val);
>> +	if (rval)
>> +		goto out;
>> +#endif
>> +
>> +out:
>> +	if (rval)
>> +		et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev video operations
>> + */
>> +#define MAX_FMTS 4
>> +static int et8ek8_enum_mbus_code(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	struct et8ek8_reglist **list =
>> +			et8ek8_reglist_first(&meta_reglist);
>> +	u32 pixelformat[MAX_FMTS];
>> +	int npixelformat = 0;
>> +
>> +	if (code->index >= MAX_FMTS)
>> +		return -EINVAL;
>> +
>> +	for (; *list; list++) {
>> +		struct et8ek8_mode *mode = &(*list)->mode;
>> +		int i;
>> +
>> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
>> +			continue;
>> +
>> +		for (i = 0; i < npixelformat; i++) {
>> +			if (pixelformat[i] == mode->pixel_format)
>> +				break;
>> +		}
>> +		if (i != npixelformat)
>> +			continue;
>> +
>> +		if (code->index == npixelformat) {
>> +			if (mode->pixel_format == V4L2_PIX_FMT_SGRBG10DPCM8)
>> +				code->code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8;
>> +			else
>> +				code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
>> +			return 0;
>> +		}
>> +
>> +		pixelformat[npixelformat] = mode->pixel_format;
>> +		npixelformat++;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int et8ek8_enum_frame_size(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	struct et8ek8_reglist **list =
>> +			et8ek8_reglist_first(&meta_reglist);
>> +	struct v4l2_mbus_framefmt format;
>> +	int cmp_width = INT_MAX;
>> +	int cmp_height = INT_MAX;
>> +	int index = fse->index;
>> +
>> +	for (; *list; list++) {
>> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
>> +			continue;
>> +
>> +		et8ek8_reglist_to_mbus(*list, &format);
>> +		if (fse->code != format.code)
>> +			continue;
>> +
>> +		/* Assume that the modes are grouped by frame size. */
>> +		if (format.width == cmp_width && format.height == cmp_height)
>> +			continue;
>> +
>> +		cmp_width = format.width;
>> +		cmp_height = format.height;
>> +
>> +		if (index-- == 0) {
>> +			fse->min_width = format.width;
>> +			fse->min_height = format.height;
>> +			fse->max_width = format.width;
>> +			fse->max_height = format.height;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int et8ek8_enum_frame_ival(struct v4l2_subdev *subdev,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_frame_interval_enum *fie)
>> +{
>> +	struct et8ek8_reglist **list =
>> +			et8ek8_reglist_first(&meta_reglist);
>> +	struct v4l2_mbus_framefmt format;
>> +	int index = fie->index;
>> +
>> +	for (; *list; list++) {
>> +		struct et8ek8_mode *mode = &(*list)->mode;
>> +
>> +		if ((*list)->type != ET8EK8_REGLIST_MODE)
>> +			continue;
>> +
>> +		et8ek8_reglist_to_mbus(*list, &format);
>> +		if (fie->code != format.code)
>> +			continue;
>> +
>> +		if (fie->width != format.width || fie->height != format.height)
>> +			continue;
>> +
>> +		if (index-- == 0) {
>> +			fie->interval = mode->timeperframe;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static struct v4l2_mbus_framefmt *
>> +__et8ek8_get_pad_format(struct et8ek8_sensor *sensor,
>> +			struct v4l2_subdev_pad_config *cfg,
>> +			unsigned int pad, enum v4l2_subdev_format_whence which)
>> +{
>> +	switch (which) {
>> +	case V4L2_SUBDEV_FORMAT_TRY:
>> +		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
>> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
>> +		return &sensor->format;
>> +	default:
>> +		return NULL;
>> +	}
>> +}
>> +
>> +static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	fmt->format = *format;
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_format *fmt)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +	struct et8ek8_reglist *reglist;
>> +
>> +	format = __et8ek8_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
>> +	if (format == NULL)
>> +		return -EINVAL;
>> +
>> +	reglist = et8ek8_reglist_find_mode_fmt(&meta_reglist,
>> +					     &fmt->format);
>> +	et8ek8_reglist_to_mbus(reglist, &fmt->format);
>> +	*format = fmt->format;
>> +
>> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> +		sensor->current_reglist = reglist;
>> +		et8ek8_update_controls(sensor);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	memset(fi, 0, sizeof(*fi));
>> +	fi->interval = sensor->current_reglist->mode.timeperframe;
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev,
>> +				     struct v4l2_subdev_frame_interval *fi)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct et8ek8_reglist *reglist;
>> +
>> +	reglist = et8ek8_reglist_find_mode_ival(&meta_reglist,
>> +					      sensor->current_reglist,
>> +					      &fi->interval);
>> +
>> +	if (!reglist)
>> +		return -EINVAL;
>> +
>> +	if (sensor->current_reglist->mode.ext_clock != reglist->mode.ext_clock)
>> +		return -EINVAL;
>> +
>> +	sensor->current_reglist = reglist;
>> +	et8ek8_update_controls(sensor);
>> +
>> +	return 0;
>> +}
>> +
>> +static int et8ek8_g_priv_mem(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	unsigned int length = ET8EK8_PRIV_MEM_SIZE;
>> +	unsigned int offset = 0;
>> +	u8 *ptr  = sensor->priv_mem;
>> +	int rval = 0;
>> +
>> +	/* Read the EEPROM window-by-window, each window 8 bytes */
>> +	do {
>> +		u8 buffer[PRIV_MEM_WIN_SIZE];
>> +		struct i2c_msg msg;
>> +		int bytes, i;
>> +		int ofs;
>> +
>> +		/* Set the current window */
>> +		rval = et8ek8_i2c_write_reg(client, ET8EK8_REG_8BIT, 0x0001,
>> +					    0xe0 | (offset >> 3));
>> +		if (rval < 0)
>> +			goto out;
>> +
>> +		/* Wait for status bit */
>> +		for (i = 0; i < 1000; ++i) {
>> +			u32 status;
>> +
>> +			rval = et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
>> +						   0x0003, &status);
>> +			if (rval < 0)
>> +				goto out;
>> +			if ((status & 0x08) == 0)
>> +				break;
>> +			usleep_range(1000, 2000);
>> +		};
>> +
>> +		if (i == 1000) {
>> +			rval = -EIO;
>> +			goto out;
>> +		}
>> +
>> +		/* Read window, 8 bytes at once, and copy to user space */
>> +		ofs = offset & 0x07;	/* Offset within this window */
>> +		bytes = length + ofs > 8 ? 8-ofs : length;
>> +		msg.addr = client->addr;
>> +		msg.flags = 0;
>> +		msg.len = 2;
>> +		msg.buf = buffer;
>> +		ofs += PRIV_MEM_START_REG;
>> +		buffer[0] = (u8)(ofs >> 8);
>> +		buffer[1] = (u8)(ofs & 0xFF);
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		mdelay(ET8EK8_I2C_DELAY);
>> +		msg.addr = client->addr;
>> +		msg.len = bytes;
>> +		msg.flags = I2C_M_RD;
>> +		msg.buf = buffer;
>> +		memset(buffer, 0, sizeof(buffer));
>> +		rval = i2c_transfer(client->adapter, &msg, 1);
>> +		if (rval < 0)
>> +			goto out;
>> +		rval = 0;
>> +		memcpy(ptr, buffer, bytes);
>> +
>> +		length -= bytes;
>> +		offset += bytes;
>> +		ptr    += bytes;
>> +	} while (length > 0);
>> +
>> +out:
>> +	return rval;
>> +}
>> +
>> +static int et8ek8_dev_init(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	int rval, rev_l, rev_h;
>> +
>> +	rval = et8ek8_power_on(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "could not power on\n");
>> +		return rval;
>> +	}
>> +
>> +	if (et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
>> +				REG_REVISION_NUMBER_L, &rev_l) != 0
>> +	    || et8ek8_i2c_read_reg(client, ET8EK8_REG_8BIT,
>> +				   REG_REVISION_NUMBER_H, &rev_h) != 0) {
>> +		dev_err(&client->dev,
>> +			"no et8ek8 sensor detected\n");
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +	sensor->version = (rev_h << 8) + rev_l;
>> +	if (sensor->version != ET8EK8_REV_1
>> +	    && sensor->version != ET8EK8_REV_2)
>> +		dev_info(&client->dev,
>> +			 "unknown version 0x%x detected, continuing anyway\n",
>> +			 sensor->version);
>> +
>> +	rval = et8ek8_reglist_import(client, &meta_reglist);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, import failed\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +
>> +	sensor->current_reglist =
>> +		et8ek8_reglist_find_type(&meta_reglist,
>> +				       ET8EK8_REGLIST_MODE);
>> +	if (!sensor->current_reglist) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no mode found\n",
>> +			ET8EK8_NAME);
>> +		rval = -ENODEV;
>> +		goto out_poweroff;
>> +	}
>> +
>> +	et8ek8_reglist_to_mbus(sensor->current_reglist, &sensor->format);
>> +
>> +	rval = et8ek8_i2c_reglist_find_write(client,
>> +					   &meta_reglist,
>> +					   ET8EK8_REGLIST_POWERON);
>> +	if (rval) {
>> +		dev_err(&client->dev,
>> +			"invalid register list %s, no POWERON mode found\n",
>> +			ET8EK8_NAME);
>> +		goto out_poweroff;
>> +	}
>> +	rval = et8ek8_stream_on(sensor); /* Needed to be able to read EEPROM */
>> +	if (rval)
>> +		goto out_poweroff;
>> +	rval = et8ek8_g_priv_mem(subdev);
>> +	if (rval)
>> +		dev_warn(&client->dev,
>> +			"can not read OTP (EEPROM) memory from sensor\n");
>> +	rval = et8ek8_stream_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	rval = et8ek8_power_off(sensor);
>> +	if (rval)
>> +		goto out_poweroff;
>> +
>> +	return 0;
>> +
>> +out_poweroff:
>> +	et8ek8_power_off(sensor);
>> +
>> +	return rval;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * sysfs attributes
>> + */
>> +static ssize_t
>> +et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
>> +		     char *buf)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +#if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE
>> +#error PAGE_SIZE too small!
>> +#endif
>> +
>> +	memcpy(buf, sensor->priv_mem, ET8EK8_PRIV_MEM_SIZE);
>> +
>> +	return ET8EK8_PRIV_MEM_SIZE;
>> +}
>> +static DEVICE_ATTR(priv_mem, S_IRUGO, et8ek8_priv_mem_read, NULL);
>> +
>> +/* --------------------------------------------------------------------------
>> + * V4L2 subdev core operations
>> + */
>> +
>> +static int
>> +et8ek8_registered(struct v4l2_subdev *subdev)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +	struct v4l2_mbus_framefmt *format;
>> +	int rval;
>> +
>> +	dev_dbg(&client->dev, "registered!");
>> +
>> +	if (device_create_file(&client->dev, &dev_attr_priv_mem) != 0) {
>> +		dev_err(&client->dev, "could not register sysfs entry\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	rval = et8ek8_dev_init(subdev);
>> +	if (rval)
>> +		return rval;
>> +
>> +	rval = et8ek8_init_controls(sensor);
>> +	if (rval) {
>> +		dev_err(&client->dev, "controls initialization failed\n");
>> +		return rval;
>> +	}
>> +
>> +	format = __et8ek8_get_pad_format(sensor, NULL, 0,
>> +					 V4L2_SUBDEV_FORMAT_ACTIVE);
>> +	return 0;
>> +}
>> +
>> +static int __et8ek8_set_power(struct et8ek8_sensor *sensor, bool on)
>> +{
>> +	return on ? et8ek8_power_on(sensor) : et8ek8_power_off(sensor);
>> +}
>> +
>> +static int et8ek8_set_power(struct v4l2_subdev *subdev, int on)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +	int ret = 0;
>> +
>> +	mutex_lock(&sensor->power_lock);
>> +
>> +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> +	 * update the power state.
>> +	 */
>> +	if (sensor->power_count == !on) {
>> +		ret = __et8ek8_set_power(sensor, !!on);
>> +		if (ret < 0)
>> +			goto done;
>> +	}
>> +
>> +	/* Update the power count. */
>> +	sensor->power_count += on ? 1 : -1;
>> +	WARN_ON(sensor->power_count < 0);
>> +
>> +done:
>> +	mutex_unlock(&sensor->power_lock);
>> +	return ret;
>> +}
>> +
>> +static int et8ek8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(sd);
>> +	struct v4l2_mbus_framefmt *format;
>> +	struct et8ek8_reglist *reglist;
>> +
>> +	reglist = et8ek8_reglist_find_type(&meta_reglist, ET8EK8_REGLIST_MODE);
>> +	format = __et8ek8_get_pad_format(sensor, fh->pad, 0,
>> +					 V4L2_SUBDEV_FORMAT_TRY);
>> +	et8ek8_reglist_to_mbus(reglist, format);
>> +
>> +	return et8ek8_set_power(sd, true);
>> +}
>> +
>> +static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	return et8ek8_set_power(sd, false);
>> +}
>> +
>> +static const struct v4l2_subdev_video_ops et8ek8_video_ops = {
>> +	.s_stream = et8ek8_s_stream,
>> +	.g_frame_interval = et8ek8_get_frame_interval,
>> +	.s_frame_interval = et8ek8_set_frame_interval,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops et8ek8_core_ops = {
>> +	.s_power = et8ek8_set_power,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = {
>> +	.enum_mbus_code = et8ek8_enum_mbus_code,
>> +	.enum_frame_size = et8ek8_enum_frame_size,
>> +	.enum_frame_interval = et8ek8_enum_frame_ival,
>> +	.get_fmt = et8ek8_get_pad_format,
>> +	.set_fmt = et8ek8_set_pad_format,
>> +};
>> +
>> +static const struct v4l2_subdev_ops et8ek8_ops = {
>> +	.core = &et8ek8_core_ops,
>> +	.video = &et8ek8_video_ops,
>> +	.pad = &et8ek8_pad_ops,
>> +};
>> +
>> +static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = {
>> +	.registered = et8ek8_registered,
>> +	.open = et8ek8_open,
>> +	.close = et8ek8_close,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * I2C driver
>> + */
>> +#ifdef CONFIG_PM
>> +
>> +static int et8ek8_suspend(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, false);
>> +}
>> +
>> +static int et8ek8_resume(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (!sensor->power_count)
>> +		return 0;
>> +
>> +	return __et8ek8_set_power(sensor, true);
>> +}
>> +
>> +static const struct dev_pm_ops et8ek8_pm_ops = {
>> +	.suspend	= et8ek8_suspend,
>> +	.resume		= et8ek8_resume,
>> +};
>> +
>> +#else
>> +
>> +#define et8ek8_pm_ops	NULL
>> +
>> +#endif /* CONFIG_PM */
>> +
>> +static int et8ek8_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *devid)
>> +{
>> +	struct et8ek8_sensor *sensor;
>> +	struct device *dev = &client->dev;
>> +	int ret;
>> +
>> +	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
>> +	if (!sensor)
>> +		return -ENOMEM;
>> +
>> +	sensor->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(sensor->reset)) {
>> +		dev_dbg(&client->dev, "could not request reset gpio\n");
>> +		return PTR_ERR(sensor->reset);
>> +	}
>> +
>> +	sensor->vana = devm_regulator_get(dev, "vana");
>> +	if (IS_ERR(sensor->vana)) {
>> +		dev_err(&client->dev, "could not get regulator for vana\n");
>> +		return PTR_ERR(sensor->vana);
>> +	}
>> +
>> +	sensor->ext_clk = devm_clk_get(dev, NULL);
>> +	if (IS_ERR(sensor->ext_clk)) {
>> +		dev_err(&client->dev, "could not get clock\n");
>> +		return PTR_ERR(sensor->ext_clk);
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "clock-frequency",
>> +				   &sensor->xclk_freq);
>> +	if (ret) {
>> +		dev_warn(dev, "can't get clock-frequency\n");
>> +		return ret;
>> +	}
>> +
>> +	mutex_init(&sensor->power_lock);
>> +
>> +	v4l2_i2c_subdev_init(&sensor->subdev, client, &et8ek8_ops);
>> +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	sensor->subdev.internal_ops = &et8ek8_internal_ops;
>> +
>> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "media entity init failed!\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = v4l2_async_register_subdev(&sensor->subdev);
>> +	if (ret < 0) {
>> +		media_entity_cleanup(&sensor->subdev.entity);
>> +		return ret;
>> +	}
>> +
>> +	dev_dbg(dev, "initialized!\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static int __exit et8ek8_remove(struct i2c_client *client)
>> +{
>> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
>> +	struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
>> +
>> +	if (sensor->power_count) {
>> +		gpiod_set_value(sensor->reset, 0);
>> +		clk_disable_unprepare(sensor->ext_clk);
>> +		sensor->power_count = 0;
>> +	}
>> +
>> +	v4l2_device_unregister_subdev(&sensor->subdev);
>> +	device_remove_file(&client->dev, &dev_attr_priv_mem);
>> +	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
>> +	media_entity_cleanup(&sensor->subdev.entity);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id et8ek8_of_table[] = {
>> +	{ .compatible = "toshiba,et8ek8" },
>> +	{ },
>> +};
>> +
>> +static const struct i2c_device_id et8ek8_id_table[] = {
>> +	{ ET8EK8_NAME, 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
>> +
>> +static struct i2c_driver et8ek8_i2c_driver = {
>> +	.driver		= {
>> +		.name	= ET8EK8_NAME,
>> +		.pm	= &et8ek8_pm_ops,
>> +		.of_match_table	= et8ek8_of_table,
>> +	},
>> +	.probe		= et8ek8_probe,
>> +	.remove		= __exit_p(et8ek8_remove),
>> +	.id_table	= et8ek8_id_table,
>> +};
>> +
>> +module_i2c_driver(et8ek8_i2c_driver);
>> +
>> +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
>> +MODULE_DESCRIPTION("Toshiba ET8EK8 camera sensor driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/i2c/et8ek8/et8ek8_mode.c b/drivers/media/i2c/et8ek8/et8ek8_mode.c
>> new file mode 100644
>> index 0000000..e5c367b
>> --- /dev/null
>> +++ b/drivers/media/i2c/et8ek8/et8ek8_mode.c
>> @@ -0,0 +1,591 @@
>> +/*
>> + * et8ek8_mode.c
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +
>> +#include "et8ek8_reg.h"
>> +
>> +/*
>> + *
>> + * Stingray sensor mode settings for Scooby
>> + *
>> + *
>> + */
>> +
>> +/* Mode1_poweron_Mode2_16VGA_2592x1968_12.07fps */
>> +static struct et8ek8_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_POWERON,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1207
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		/* Need to set firstly */
>> +		{ ET8EK8_REG_8BIT, 0x126C, 0xCC },
>> +		/* Strobe and Data of CCP2 delay are minimized. */
>> +		{ ET8EK8_REG_8BIT, 0x1269, 0x00 },
>> +		/* Refined value of Min H_COUNT  */
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
>> +		/* Frequency of SPCK setting (SPCK=MRCK) */
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
>> +		{ ET8EK8_REG_8BIT, 0x1241, 0x94 },
>> +		{ ET8EK8_REG_8BIT, 0x1242, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x124B, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1255, 0xFF },
>> +		{ ET8EK8_REG_8BIT, 0x1256, 0x9F },
>> +		{ ET8EK8_REG_8BIT, 0x1258, 0x00 },
>> +		/* From parallel out to serial out */
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 },
>> +		/* From w/ embeded data to w/o embeded data */
>> +		{ ET8EK8_REG_8BIT, 0x125E, 0xC0 },
>> +		/* CCP2 out is from STOP to ACTIVE */
>> +		{ ET8EK8_REG_8BIT, 0x1263, 0x98 },
>> +		{ ET8EK8_REG_8BIT, 0x1268, 0xC6 },
>> +		{ ET8EK8_REG_8BIT, 0x1434, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1163, 0x44 },
>> +		{ ET8EK8_REG_8BIT, 0x1166, 0x29 },
>> +		{ ET8EK8_REG_8BIT, 0x1140, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x1011, 0x24 },
>> +		{ ET8EK8_REG_8BIT, 0x1151, 0x80 },
>> +		{ ET8EK8_REG_8BIT, 0x1152, 0x23 },
>> +		/* Initial setting for improvement2 of lower frequency noise */
>> +		{ ET8EK8_REG_8BIT, 0x1014, 0x05 },
>> +		{ ET8EK8_REG_8BIT, 0x1033, 0x06 },
>> +		{ ET8EK8_REG_8BIT, 0x1034, 0x79 },
>> +		{ ET8EK8_REG_8BIT, 0x1423, 0x3F },
>> +		{ ET8EK8_REG_8BIT, 0x1424, 0x3F },
>> +		{ ET8EK8_REG_8BIT, 0x1426, 0x00 },
>> +		/* Switch of Preset-White-balance (0d:disable / 1d:enable) */
>> +		{ ET8EK8_REG_8BIT, 0x1439, 0x00 },
>> +		/* Switch of blemish correction (0d:disable / 1d:enable) */
>> +		{ ET8EK8_REG_8BIT, 0x161F, 0x60 },
>> +		/* Switch of auto noise correction (0d:disable / 1d:enable) */
>> +		{ ET8EK8_REG_8BIT, 0x1634, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1646, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1648, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x113E, 0x01 },
>> +		{ ET8EK8_REG_8BIT, 0x113F, 0x22 },
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode1_16VGA_2592x1968_13.12fps_DPCM10-8 */
>> +static struct et8ek8_reglist mode1_16vga_2592x1968_13_12fps_dpcm10_8 = {
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 560 MHz
>> + * VCO        = 560 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 128 (3072)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 175
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 6
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3072,
>> +		.height = 2016,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 1292
>> +		},
>> +		.max_exp = 2012,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x57 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x06 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x80 }, /* <-changed to v14 7E->80 */
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_29.99fps_DPCM10-8 */
>> +static struct et8ek8_reglist mode3_4vga_1296x984_29_99fps_dpcm10_8 = {
>> +/* (without the +1)
>> + * SPCK       = 96.5333333333333 MHz
>> + * CCP2       = 579.2 MHz
>> + * VCO        = 579.2 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 181
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1008,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 96533333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 3000
>> +		},
>> +		.max_exp = 1004,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x5A },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode4_SVGA_864x656_29.88fps */
>> +static struct et8ek8_reglist mode4_svga_864x656_29_88fps = {
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 166 (3984)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3984,
>> +		.height = 672,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 864,
>> +		.window_height = 656,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2988
>> +		},
>> +		.max_exp = 668,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x62 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x62 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0xA6 },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode5_VGA_648x492_29.93fps */
>> +static struct et8ek8_reglist mode5_vga_648x492_29_93fps = {
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 320 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2993
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode2_16VGA_2592x1968_3.99fps */
>> +static struct et8ek8_reglist mode2_16vga_2592x1968_3_99fps = {
>> +/* (without the +1)
>> + * SPCK       = 80 MHz
>> + * CCP2       = 640 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 254 (6096)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 6096,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 2592,
>> +		.window_height = 1968,
>> +		.pixel_clock = 80000000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 399
>> +		},
>> +		.max_exp = 6092,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x07 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0xFE },
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_648x492_5fps */
>> +static struct et8ek8_reglist mode_648x492_5fps = {
>> +/* (without the +1)
>> + * SPCK       = 13.3333333333333 MHz
>> + * CCP2       = 53.3333333333333 MHz
>> + * VCO        = 640 MHz
>> + * VCOUNT     = 84 (2016)
>> + * HCOUNT     = 221 (5304)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 200
>> + * VCO_DIV    = 5
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 1
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 5304,
>> +		.height = 504,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 648,
>> +		.window_height = 492,
>> +		.pixel_clock = 13333333,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 499
>> +		},
>> +		.max_exp = 500,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x64 },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x71 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x57 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x61 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x61 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0xDD },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x54 },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode3_4VGA_1296x984_5fps */
>> +static struct et8ek8_reglist mode3_4vga_1296x984_5fps = {
>> +/* (without the +1)
>> + * SPCK       = 49.4 MHz
>> + * CCP2       = 395.2 MHz
>> + * VCO        = 790.4 MHz
>> + * VCOUNT     = 250 (6000)
>> + * HCOUNT     = 137 (3288)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 247
>> + * VCO_DIV    = 1
>> + * SPCK_DIV   = 7
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3288,
>> +		.height = 3000,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 49400000,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 501
>> +		},
>> +		.max_exp = 2996,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x7B },
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x82 },
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x17 },
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x89 },
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0xFA },
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x88 }, /* CCP_LVDS_MODE/  */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +/* Mode_4VGA_1296x984_25fps_DPCM10-8 */
>> +static struct et8ek8_reglist mode_4vga_1296x984_25fps_dpcm10_8 = {
>> +/* (without the +1)
>> + * SPCK       = 84.2666666666667 MHz
>> + * CCP2       = 505.6 MHz
>> + * VCO        = 505.6 MHz
>> + * VCOUNT     = 88 (2112)
>> + * HCOUNT     = 133 (3192)
>> + * CKREF_DIV  = 2
>> + * CKVAR_DIV  = 158
>> + * VCO_DIV    = 0
>> + * SPCK_DIV   = 5
>> + * MRCK_DIV   = 7
>> + * LVDSCK_DIV = 0
>> + */
>> +	.type = ET8EK8_REGLIST_MODE,
>> +	.mode = {
>> +		.sensor_width = 2592,
>> +		.sensor_height = 1968,
>> +		.sensor_window_origin_x = 0,
>> +		.sensor_window_origin_y = 0,
>> +		.sensor_window_width = 2592,
>> +		.sensor_window_height = 1968,
>> +		.width = 3192,
>> +		.height = 1056,
>> +		.window_origin_x = 0,
>> +		.window_origin_y = 0,
>> +		.window_width = 1296,
>> +		.window_height = 984,
>> +		.pixel_clock = 84266667,
>> +		.ext_clock = 9600000,
>> +		.timeperframe = {
>> +			.numerator = 100,
>> +			.denominator = 2500
>> +		},
>> +		.max_exp = 1052,
>> +		/* .max_gain = 0, */
>> +		.pixel_format = V4L2_PIX_FMT_SGRBG10DPCM8,
>> +		.sensitivity = 65536
>> +	},
>> +	.regs = {
>> +		{ ET8EK8_REG_8BIT, 0x1239, 0x4F },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x1238, 0x02 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x123B, 0x70 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x123A, 0x05 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x121B, 0x63 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x1220, 0x85 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x1221, 0x00 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x1222, 0x58 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x1223, 0x00 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x121D, 0x63 },	/*        */
>> +		{ ET8EK8_REG_8BIT, 0x125D, 0x83 },	/*        */
>> +		{ ET8EK8_REG_TERM, 0, 0}
>> +	}
>> +};
>> +
>> +struct et8ek8_meta_reglist meta_reglist = {
>> +	.magic   = ET8EK8_MAGIC,
>> +	.version = "V14 03-June-2008",
>> +	.reglist = {
>> +		{ .ptr = &mode1_poweron_mode2_16vga_2592x1968_12_07fps },
>> +		{ .ptr = &mode1_16vga_2592x1968_13_12fps_dpcm10_8 },
>> +		{ .ptr = &mode3_4vga_1296x984_29_99fps_dpcm10_8 },
>> +		{ .ptr = &mode4_svga_864x656_29_88fps },
>> +		{ .ptr = &mode5_vga_648x492_29_93fps },
>> +		{ .ptr = &mode2_16vga_2592x1968_3_99fps },
>> +		{ .ptr = &mode_648x492_5fps },
>> +		{ .ptr = &mode3_4vga_1296x984_5fps },
>> +		{ .ptr = &mode_4vga_1296x984_25fps_dpcm10_8 },
>> +		{ .ptr = 0 }
>> +	}
>> +};
>> diff --git a/drivers/media/i2c/et8ek8/et8ek8_reg.h b/drivers/media/i2c/et8ek8/et8ek8_reg.h
>> new file mode 100644
>> index 0000000..1f138b0
>> --- /dev/null
>> +++ b/drivers/media/i2c/et8ek8/et8ek8_reg.h
>> @@ -0,0 +1,100 @@
>> +/*
>> + * et8ek8.h
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@iki.fi>
>> + *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
>
> tuukkat76@gmail.com
>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +
>> +#ifndef ET8EK8REGS_H
>> +#define ET8EK8REGS_H
>> +
>> +#include <linux/i2c.h>
>> +#include <linux/types.h>
>> +#include <linux/videodev2.h>
>> +#include <linux/v4l2-subdev.h>
>> +
>> +struct v4l2_mbus_framefmt;
>> +struct v4l2_subdev_pad_mbus_code_enum;
>> +
>> +#define ET8EK8_MAGIC			0x531A0002
>> +
>> +struct et8ek8_mode {
>> +	/* Physical sensor resolution and current image window */
>> +	__u16 sensor_width;
>> +	__u16 sensor_height;
>> +	__u16 sensor_window_origin_x;
>> +	__u16 sensor_window_origin_y;
>> +	__u16 sensor_window_width;
>> +	__u16 sensor_window_height;
>> +
>> +	/* Image data coming from sensor (after scaling) */
>> +	__u16 width;
>> +	__u16 height;
>> +	__u16 window_origin_x;
>> +	__u16 window_origin_y;
>> +	__u16 window_width;
>> +	__u16 window_height;
>> +
>> +	__u32 pixel_clock;		/* in Hz */
>> +	__u32 ext_clock;		/* in Hz */
>> +	struct v4l2_fract timeperframe;
>> +	__u32 max_exp;			/* Maximum exposure value */
>> +	__u32 pixel_format;		/* V4L2_PIX_FMT_xxx */
>> +	__u32 sensitivity;		/* 16.16 fixed point */
>> +};
>> +
>> +#define ET8EK8_REG_8BIT			1
>> +#define ET8EK8_REG_16BIT		2
>> +#define ET8EK8_REG_32BIT		4
>> +#define ET8EK8_REG_DELAY		100
>> +#define ET8EK8_REG_TERM			0xff
>> +struct et8ek8_reg {
>> +	u16 type;
>> +	u16 reg;			/* 16-bit offset */
>> +	u32 val;			/* 8/16/32-bit value */
>> +};
>> +
>> +/* Possible struct smia_reglist types. */
>> +#define ET8EK8_REGLIST_STANDBY		0
>> +#define ET8EK8_REGLIST_POWERON		1
>> +#define ET8EK8_REGLIST_RESUME		2
>> +#define ET8EK8_REGLIST_STREAMON		3
>> +#define ET8EK8_REGLIST_STREAMOFF	4
>> +#define ET8EK8_REGLIST_DISABLED		5
>> +
>> +#define ET8EK8_REGLIST_MODE		10
>> +
>> +#define ET8EK8_REGLIST_LSC_ENABLE	100
>> +#define ET8EK8_REGLIST_LSC_DISABLE	101
>> +#define ET8EK8_REGLIST_ANR_ENABLE	102
>> +#define ET8EK8_REGLIST_ANR_DISABLE	103
>> +
>> +struct et8ek8_reglist {
>> +	u32 type;
>> +	struct et8ek8_mode mode;
>> +	struct et8ek8_reg regs[];
>> +};
>> +
>> +#define ET8EK8_MAX_LEN			32
>> +struct et8ek8_meta_reglist {
>> +	u32 magic;
>
> The field is useless as is ET8EK8_MAGIC. It was there to avoid loading
> garbage by accident by using request_firmware(). Please remove.
>

ok

>> +	char version[ET8EK8_MAX_LEN];
>> +	union {
>> +		struct et8ek8_reglist *ptr;
>> +	} reglist[];
>> +};
>> +
>> +extern struct et8ek8_meta_reglist meta_reglist;
>> +
>> +#endif /* ET8EK8REGS */
>> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
>> index b6a357a..97a08d8 100644
>> --- a/include/uapi/linux/v4l2-controls.h
>> +++ b/include/uapi/linux/v4l2-controls.h
>> @@ -180,6 +180,11 @@ enum v4l2_colorfx {
>>    * We reserve 16 controls for this driver. */
>>   #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
>>
>> +/* The base for the et8ek8 driver controls.
>> + * We reserve 16 controls for this driver.
>> + */
>> +#define V4L2_CID_USER_ET8EK8_BASE		(V4L2_CID_USER_BASE + 0x1090)
>> +
>>   /* MPEG-class control IDs */
>>   /* The MPEG controls are applicable to all codec controls
>>    * and the 'MPEG' part of the define is historical */
>

Please elaborate on "If you have custom controls,", see above, so I will 
be able to fix it and send a new version.

Thanks,
Ivo

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-05-24 11:19             ` Pavel Machek
  2016-06-04 19:16               ` Ivaylo Dimitrov
@ 2016-06-06  9:04               ` Sylwester Nawrocki
  1 sibling, 0 replies; 102+ messages in thread
From: Sylwester Nawrocki @ 2016-06-06  9:04 UTC (permalink / raw)
  To: Pavel Machek, Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

On 05/24/2016 01:19 PM, Pavel Machek wrote:
>> +/*
>> > + * Write a list of registers to i2c device.
>> > + *
>> > + * The list of registers is terminated by ET8EK8_REG_TERM.
>> > + * Returns zero if successful, or non-zero otherwise.
>> > + */
>> > +static int et8ek8_i2c_write_regs(struct i2c_client *client,
>> > +				 const struct et8ek8_reg reglist[])
>> > +{
>> > +	int r, cnt = 0;
>> > +	const struct et8ek8_reg *next, *wnext;
>> > +
>> > +	if (!client->adapter)
>> > +		return -ENODEV;
>> > +
>> > +	if (reglist == NULL)
>
> (!reglist) ? :-). Actually, you can keep your preffered style there,
> but maybe ammount of if (something that can not happen) return
> ... should be reduced. Noone should ever call this without valid
> reglist or client->adapter, right?
> 
>> > +		return -EINVAL;
>> > +
>> > +	/* Initialize list pointers to the start of the list */
>> > +	next = wnext = reglist;
>> > +
>> > +	do {
>> > +		/*
>> > +		 * We have to go through the list to figure out how
>> > +		 * many regular writes we have in a row
>> > +		 */
>> > +		while (next->type != ET8EK8_REG_TERM
>> > +		       && next->type != ET8EK8_REG_DELAY) {
>> > +			/*
>> > +			 * Here we check that the actual length fields
>> > +			 * are valid
>> > +			 */
>> > +			if (next->type != ET8EK8_REG_8BIT
>> > +			    &&  next->type != ET8EK8_REG_16BIT) {
> Extra space after &&
> 
>> > +				dev_err(&client->dev,
>> > +					"Invalid value on entry %d 0x%x\n",
>> > +					cnt, next->type);
>> > +				return -EINVAL;
>> > +			}
>
> And maybe this could be just BUG_ON(). 

It definitively doesn't look like a BUG_ON() would be appropriate here,
it's just an unexpected condition in some I2C write function of a rather
not critical device from the whole system operation stability point
of view. Perhaps you just meant WARN_ON()?

BUG_ON() should be used with care, when the condition is not recoverable,
otherwise we are just making debugging unnecessarily harder.

http://lkml.iu.edu/hypermail/linux/kernel/1506.1/00062.html
http://yarchive.net/comp/linux/BUG.html

-- 
Thanks,
Sylwester

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

* Re: [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor
  2016-06-04 19:54               ` Ivaylo Dimitrov
@ 2016-06-09 23:13                 ` Sakari Ailus
  0 siblings, 0 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-06-09 23:13 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sre, pali.rohar, pavel, linux-media

Hi Ivaylo,

On Sat, Jun 04, 2016 at 10:54:58PM +0300, Ivaylo Dimitrov wrote:
> Hi,
> 
> On 26.05.2016 00:45, Sakari Ailus wrote:
> >Hi Ivaylo,
> >
> >I've got some comments here but I haven't reviewed everything yet. What's
> >missing is
> >
> >- the user space interface for selecting the sensor configuration "mode",
> >
> >- passing information on the sensor configuration to the user space.
> >
> >I'll try to take a look at those some time in the near future.
> >
> 
> ok
> 
> >
> >I very much appreciate your work towards finally upstreaming this! :-)
> >
> >On Tue, May 03, 2016 at 05:50:04PM +0300, Ivaylo Dimitrov wrote:
> >>The sensor is found in Nokia N900 main camera
> >>
> >>Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
> >>---
> >>  .../bindings/media/i2c/toshiba,et8ek8.txt          |   53 +
> >>  drivers/media/i2c/Kconfig                          |    1 +
> >>  drivers/media/i2c/Makefile                         |    1 +
> >>  drivers/media/i2c/et8ek8/Kconfig                   |    6 +
> >>  drivers/media/i2c/et8ek8/Makefile                  |    2 +
> >>  drivers/media/i2c/et8ek8/et8ek8_driver.c           | 1711 ++++++++++++++++++++
> >>  drivers/media/i2c/et8ek8/et8ek8_mode.c             |  591 +++++++
> >>  drivers/media/i2c/et8ek8/et8ek8_reg.h              |  100 ++
> >>  include/uapi/linux/v4l2-controls.h                 |    5 +
> >>  9 files changed, 2470 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
> >>  create mode 100644 drivers/media/i2c/et8ek8/Kconfig
> >>  create mode 100644 drivers/media/i2c/et8ek8/Makefile
> >>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_driver.c
> >>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_mode.c
> >>  create mode 100644 drivers/media/i2c/et8ek8/et8ek8_reg.h
> >>
> >>diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
> >>new file mode 100644
> >>index 0000000..55f712c
> >>--- /dev/null
> >>+++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt
> >>@@ -0,0 +1,53 @@
> >>+Toshiba et8ek8 5MP sensor
> >>+
> >>+Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device
> >>+
> >>+More detailed documentation can be found in
> >>+Documentation/devicetree/bindings/media/video-interfaces.txt .
> >>+
> >>+
> >>+Mandatory properties
> >>+--------------------
> >>+
> >>+- compatible: "toshiba,et8ek8"
> >>+- reg: I2C address (0x3e, or an alternative address)
> >>+- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor
> >>+  dependent).
> >
> >As these are bindings for a particular sensor, 2,8 volts it is.
> >
> >The sensor has also a digital voltage supply but it might be controlled by
> >the same GPIO which controls the CCP2 switch. Ugly stuff. Perhaps we could
> >just omit that here.
> >
> 
> ok
> 
> >>+- clocks: External clock to the sensor
> >>+- clock-frequency: Frequency of the external clock to the sensor
> >>+
> >>+
> >>+Optional properties
> >>+-------------------
> >>+
> >>+- reset-gpios: XSHUTDOWN GPIO
> >
> >I guess this should be mandatory.
> >
> 
> yeah. Also, I will change xxx-lanes to optional
> 
> >>+
> >>+
> >>+Endpoint node mandatory properties
> >>+----------------------------------
> >>+
> >>+- clock-lanes: <0>
> >>+- data-lanes: <1..n>
> >>+- remote-endpoint: A phandle to the bus receiver's endpoint node.
> >>+
> >>+
> >>+Example
> >>+-------
> >>+
> >>+&i2c3 {
> >>+	clock-frequency = <400000>;
> >>+
> >>+	cam1: camera@3e {
> >>+		compatible = "toshiba,et8ek8";
> >>+		reg = <0x3e>;
> >>+		vana-supply = <&vaux4>;
> >>+		clocks = <&isp 0>;
> >>+		clock-frequency = <9600000>;
> >>+		reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
> >>+		port {
> >>+			csi_cam1: endpoint {
> >>+				remote-endpoint = <&csi_out1>;
> >>+			};
> >>+		};
> >>+	};
> >>+};
> >
> >Please split the DT documentation from the driver.
> >
> 
> Split it how? Send as series [patch 1] - driver, [patch 2] - doc?

Yes, please. As the ad5820, for instance.

> 
> >I remember having discussed showing the module in DT with Sebastian but I
> >couldn't find the patches anywhere. We currently consider the lens and
> >sensor entirely separate, the module has not been shown in software as
> >there's been nothing to control it.
> >
> 
> Not sure what am I supposed to do with that comment :)

Not necessarily anything. But I'd like to continue the discussion on the
topic. :-)

> 
> >Sebastian: do you still have those patches around somewhere?
> >
> >>diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> >>index 993dc50..e964787 100644
> >>--- a/drivers/media/i2c/Kconfig
> >>+++ b/drivers/media/i2c/Kconfig
> >>@@ -629,6 +629,7 @@ config VIDEO_S5K5BAF
> >>  	  camera sensor with an embedded SoC image signal processor.
> >>
> >>  source "drivers/media/i2c/smiapp/Kconfig"
> >>+source "drivers/media/i2c/et8ek8/Kconfig"
> >>
> >>  config VIDEO_S5C73M3
> >>  	tristate "Samsung S5C73M3 sensor support"
> >>diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> >>index 94f2c99..907b180 100644
> >>--- a/drivers/media/i2c/Makefile
> >>+++ b/drivers/media/i2c/Makefile
> >>@@ -2,6 +2,7 @@ msp3400-objs	:=	msp3400-driver.o msp3400-kthreads.o
> >>  obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
> >>
> >>  obj-$(CONFIG_VIDEO_SMIAPP)	+= smiapp/
> >>+obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8/
> >>  obj-$(CONFIG_VIDEO_CX25840) += cx25840/
> >>  obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
> >>  obj-y				+= soc_camera/
> >>diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig
> >>new file mode 100644
> >>index 0000000..1439936
> >>--- /dev/null
> >>+++ b/drivers/media/i2c/et8ek8/Kconfig
> >>@@ -0,0 +1,6 @@
> >>+config VIDEO_ET8EK8
> >>+	tristate "ET8EK8 camera sensor support"
> >>+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> >>+	---help---
> >>+	  This is a driver for the Toshiba ET8EK8 5 MP camera sensor.
> >>+	  It is used for example in Nokia N900 (RX-51).
> >>diff --git a/drivers/media/i2c/et8ek8/Makefile b/drivers/media/i2c/et8ek8/Makefile
> >>new file mode 100644
> >>index 0000000..66d1b7d
> >>--- /dev/null
> >>+++ b/drivers/media/i2c/et8ek8/Makefile
> >>@@ -0,0 +1,2 @@
> >>+et8ek8-objs			+= et8ek8_mode.o et8ek8_driver.o
> >>+obj-$(CONFIG_VIDEO_ET8EK8)	+= et8ek8.o
> >>diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
> >>new file mode 100644
> >>index 0000000..1eaef78
> >>--- /dev/null
> >>+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
> >>@@ -0,0 +1,1711 @@
> >>+/*
> >>+ * et8ek8_driver.c
> >>+ *
> >>+ * Copyright (C) 2008 Nokia Corporation
> >>+ *
> >>+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
> >>+ *          Tuukka Toivonen <tuukka.o.toivonen@nokia.com>
> >
> >tuukkat76@gmail.com
> >
> 
> ok
> 
> >>+ *
> >>+ * Based on code from Toni Leinonen <toni.leinonen@offcode.fi>.
> >>+ *
> >>+ * This driver is based on the Micron MT9T012 camera imager driver
> >>+ * (C) Texas Instruments.
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or
> >>+ * modify it under the terms of the GNU General Public License
> >>+ * version 2 as published by the Free Software Foundation.
> >>+ *
> >>+ * This program is distributed in the hope that it will be useful, but
> >>+ * WITHOUT ANY WARRANTY; without even the implied warranty of
> >>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>+ * General Public License for more details.
> >>+ */
> >>+
> >>+#include <linux/clk.h>
> >>+#include <linux/delay.h>
> >>+#include <linux/i2c.h>
> >>+#include <linux/kernel.h>
> >>+#include <linux/module.h>
> >>+#include <linux/mutex.h>
> >>+#include <linux/gpio/consumer.h>
> >
> >Alphabetical order, please.
> >
> 
> ok
> 
> >>+#include <linux/regulator/consumer.h>
> >>+#include <linux/slab.h>
> >>+#include <linux/sort.h>
> >>+#include <linux/version.h>
> >
> >Is linux/version.h needed?
> >
> 
> no, will remove it
> 
> >>+#include <linux/v4l2-mediabus.h>
> >>+
> >>+#include <media/media-entity.h>
> >>+#include <media/v4l2-ctrls.h>
> >>+#include <media/v4l2-device.h>
> >>+#include <media/v4l2-subdev.h>
> >>+
> >>+#include "et8ek8_reg.h"
> >>+
> >>+#define ET8EK8_NAME		"et8ek8"
> >>+#define ET8EK8_PRIV_MEM_SIZE	128
> >>+
> >>+#define ET8EK8_CID_USER_FRAME_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 1)
> >>+#define ET8EK8_CID_USER_FRAME_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 2)
> >>+#define ET8EK8_CID_USER_VISIBLE_WIDTH	(V4L2_CID_USER_ET8EK8_BASE + 3)
> >>+#define ET8EK8_CID_USER_VISIBLE_HEIGHT	(V4L2_CID_USER_ET8EK8_BASE + 4)
> >>+#define ET8EK8_CID_USER_SENSITIVITY	(V4L2_CID_USER_ET8EK8_BASE + 5)
> >
> >If you have custom controls,
> >
> 
> hmm?

Yes, this is apparently left unfinished and it's a bit hard to grasp what
was the real meaning behind that comment. I agree.

What I meant to say that the controls would be better to be defined in a
header file. However, the interface to access the information shouldn't be
controls.

In this case, I believe the information is already provided to the user:
VIDIOC_SUBDEV_S_FMT is used to set the output format, and as none of the
modes use cropping.

In order to resolve this, I suggest dropping these controls.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-04-27  3:08   ` Sebastian Reichel
  2016-04-27  5:05     ` Ivaylo Dimitrov
@ 2016-06-17 16:42     ` Pavel Machek
  2016-06-17 17:12       ` Pavel Machek
  1 sibling, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-06-17 16:42 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

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

Hi!

First, I re-did pipeline setup in python, it seems slightly less hacky
then in shell.

I tried to modify fcam-dev to work with the new interface, but was not
successful so far. I can post patches if someone is interested
(mplayer works for me, but that's not too suitable for taking photos).

I tried to get gstreamer to work, with something like:

class Camera:
    gst="/usr/bin/gst-launch"
    def __init__(m):
        pass

    def run(m):
        if 0 != subprocess.call(
                [m.gst, "-v", "--gst-debug-level=2",
#                 "v4l2src", "device=/dev/video2", "num-buffers=3", "!",                     
#                 "video/x-raw-yuv,width=864,height=656", "!",                               
                 "v4l2src", "device=/dev/video6", "num-buffers=3", "!",
                 "video/x-raw-yuv,width=800,height=600,format=(fourcc)UYVY", "!",
# ,format=(fourcc)YU12                                                                       
                 "ffmpegcolorspace", "!",
                 "jpegenc", "!",
                 "filesink", "location=delme.jpg" ]):


But could not get it to work so far.

Best regards,

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: camera.py --]
[-- Type: text/x-python, Size: 1652 bytes --]

#!/usr/bin/python3

import subprocess
import os

class Camera:
    mc="/my/v4l-utils/utils/media-ctl/media-ctl"
    def __init__(m):
        m.win_x, m.win_y = 800, 600
        m.cap_x, m.cap_y = 864, 656
        #m.cap_x, m.cap_y = 2592, 1968

    def media_ctl(m, s):
        if 0 != subprocess.call(['sudo', m.mc] + s):
            print("Call ", s, " failed?")

    def media_l(m, s):
        m.media_ctl(['-l', s])

    def media_v(m, s):
        m.media_ctl(['-V', s])

    def back(m):
        m.media_ctl(['-r'])

        m.media_l('"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]')
        m.media_l('"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]')
        m.media_l('"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]')
        m.media_l('"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]')
        m.media_l('"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]')
        m.media_l('"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]')

        size = "%dx%d" % (m.cap_x, m.cap_y)
        m.media_v('"et8ek8 3-003e":0 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCP2":0 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCP2":1 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCDC":2 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP preview":1 [UYVY %s]' % size)
        m.media_v('"OMAP3 ISP resizer":1 [UYVY %dx%d]' % (m.win_x, m.win_y))

    def perms(m):
        os.system("sudo chmod 666 /dev/video? /dev/v4l-subdev*")

    def run(m):
        os.system("mplayer -tv driver=v4l2:width=%d:height=%d:outfmt=uyvy:device=/dev/video6 -vo x11 -vf screenshot tv://" % (m.win_x, m.win_y))

c = Camera()
c.back()
c.perms()
c.run()


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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-17 16:42     ` Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working) Pavel Machek
@ 2016-06-17 17:12       ` Pavel Machek
  2016-06-20 17:00         ` Pavel Machek
  2016-06-20 20:59         ` Sakari Ailus
  0 siblings, 2 replies; 102+ messages in thread
From: Pavel Machek @ 2016-06-17 17:12 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

Hi!

> First, I re-did pipeline setup in python, it seems slightly less hacky
> then in shell.
> 
> I tried to modify fcam-dev to work with the new interface, but was not
> successful so far. I can post patches if someone is interested
> (mplayer works for me, but that's not too suitable for taking photos).
> 
> I tried to get gstreamer to work, with something like:

While trying to debug gstreamer, I ran v4l2-compliance, and it seems
to suggest that QUERYCAP is required... but it is not present on
/dev/video2 or video6.

Any ideas? (Kernel is based on Ivaylo 's github tree, IIRC).

Thanks,
									Pavel

pavel@n900:/my/v4l-utils/utils$ ./v4l2-compliance/v4l2-compliance -d /dev/video6
Driver Info:
	Driver name   : ispvideo
	Card type     : OMAP3 ISP resizer output
	Bus info      : media
	Driver version: 4.6.0
	Capabilities  : 0x84200003
		Video Capture
		Video Output
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format

Compliance test for device /dev/video6 (not using libv4l2):

Required ioctls:
		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
	test VIDIOC_QUERYCAP: FAIL

Allow for multiple opens:
	test second video open: OK
		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
	test VIDIOC_QUERYCAP: FAIL
	test VIDIOC_G/S_PRIORITY: OK

pavel@n900:/my/v4l-utils/utils$ sudo ./v4l2-compliance/v4l2-compliance -d /dev/video2
Driver Info:
	Driver name   : ispvideo
	Card type     : OMAP3 ISP CCDC output
...
Compliance test for device /dev/video2 (not using libv4l2):

Required ioctls:
		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
	test VIDIOC_QUERYCAP: FAIL

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-17 17:12       ` Pavel Machek
@ 2016-06-20 17:00         ` Pavel Machek
  2016-06-20 20:59         ` Sakari Ailus
  1 sibling, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-06-20 17:00 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Ivaylo Dimitrov, sakari.ailus, pali.rohar, linux-media

On Fri 2016-06-17 19:12:14, Pavel Machek wrote:
> Hi!
> 
> > First, I re-did pipeline setup in python, it seems slightly less hacky
> > then in shell.
> > 
> > I tried to modify fcam-dev to work with the new interface, but was not
> > successful so far. I can post patches if someone is interested
> > (mplayer works for me, but that's not too suitable for taking photos).
> > 
> > I tried to get gstreamer to work, with something like:
> 
> While trying to debug gstreamer, I ran v4l2-compliance, and it seems
> to suggest that QUERYCAP is required... but it is not present on
> /dev/video2 or video6.
> 
> Any ideas? (Kernel is based on Ivaylo 's github tree, IIRC).

I got fcam-dev to grab jpeg-s from the camera. Unfortunately, 800x600,
no autogain, no autofocus. But lot of fun with memory management :-).

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-17 17:12       ` Pavel Machek
  2016-06-20 17:00         ` Pavel Machek
@ 2016-06-20 20:59         ` Sakari Ailus
  2016-06-21 18:05           ` Pavel Machek
                             ` (2 more replies)
  1 sibling, 3 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-06-20 20:59 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi Pavel,

On Fri, Jun 17, 2016 at 07:12:14PM +0200, Pavel Machek wrote:
> Hi!
> 
> > First, I re-did pipeline setup in python, it seems slightly less hacky
> > then in shell.
> > 
> > I tried to modify fcam-dev to work with the new interface, but was not
> > successful so far. I can post patches if someone is interested
> > (mplayer works for me, but that's not too suitable for taking photos).
> > 
> > I tried to get gstreamer to work, with something like:
> 
> While trying to debug gstreamer, I ran v4l2-compliance, and it seems
> to suggest that QUERYCAP is required... but it is not present on
> /dev/video2 or video6.

It's not saying that it wouldn't be present, but the content appears wrong.
It should have the real bus information there rather than just "media".

See e.g. drivers/media/platform/vsp1/vsp1_drv.c . I suppose that should be
right.

Feel free to submit a patch. :-)

> 
> Any ideas? (Kernel is based on Ivaylo 's github tree, IIRC).
> 
> Thanks,
> 									Pavel
> 
> pavel@n900:/my/v4l-utils/utils$ ./v4l2-compliance/v4l2-compliance -d /dev/video6
> Driver Info:
> 	Driver name   : ispvideo
> 	Card type     : OMAP3 ISP resizer output
> 	Bus info      : media
> 	Driver version: 4.6.0
> 	Capabilities  : 0x84200003
> 		Video Capture
> 		Video Output
> 		Streaming
> 		Extended Pix Format
> 		Device Capabilities
> 	Device Caps   : 0x04200001
> 		Video Capture
> 		Streaming
> 		Extended Pix Format
> 
> Compliance test for device /dev/video6 (not using libv4l2):
> 
> Required ioctls:
> 		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
> 	test VIDIOC_QUERYCAP: FAIL
> 
> Allow for multiple opens:
> 	test second video open: OK
> 		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
> 	test VIDIOC_QUERYCAP: FAIL
> 	test VIDIOC_G/S_PRIORITY: OK
> 
> pavel@n900:/my/v4l-utils/utils$ sudo ./v4l2-compliance/v4l2-compliance -d /dev/video2
> Driver Info:
> 	Driver name   : ispvideo
> 	Card type     : OMAP3 ISP CCDC output
> ...
> Compliance test for device /dev/video2 (not using libv4l2):
> 
> Required ioctls:
> 		fail: v4l2-compliance.cpp(537): missing bus_info prefix ('media')
> 	test VIDIOC_QUERYCAP: FAIL
> 

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-20 20:59         ` Sakari Ailus
@ 2016-06-21 18:05           ` Pavel Machek
  2016-06-22  7:22             ` Sakari Ailus
  2016-06-22 11:18           ` Pavel Machek
  2016-07-01  7:31           ` square-only image on Nokia N900 camera " Pavel Machek
  2 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-06-21 18:05 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi!

> > > First, I re-did pipeline setup in python, it seems slightly less hacky
> > > then in shell.
> > > 
> > > I tried to modify fcam-dev to work with the new interface, but was not
> > > successful so far. I can post patches if someone is interested
> > > (mplayer works for me, but that's not too suitable for taking photos).
> > > 
> > > I tried to get gstreamer to work, with something like:
> > 
> > While trying to debug gstreamer, I ran v4l2-compliance, and it seems
> > to suggest that QUERYCAP is required... but it is not present on
> > /dev/video2 or video6.
> 
> It's not saying that it wouldn't be present, but the content appears wrong.
> It should have the real bus information there rather than just "media".
> 
> See e.g. drivers/media/platform/vsp1/vsp1_drv.c . I suppose that should be
> right.
> 
> Feel free to submit a patch. :-)

For now I'd not know what to change, sorry :-(. Perhaps we can debug
it after the support is merged into mainline.

Another weirdness:

yavta, on v4l-subdev12 :

control 0x00a40906 `Sensivity' min 0 max 0 step 1 default 0 current 65536.

Min and max being the same, I don't think I can control the
sensitivity. I guess I'll have to provid  more light for the tests for
now...

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-21 18:05           ` Pavel Machek
@ 2016-06-22  7:22             ` Sakari Ailus
  0 siblings, 0 replies; 102+ messages in thread
From: Sakari Ailus @ 2016-06-22  7:22 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi Pavel,

On Tue, Jun 21, 2016 at 08:05:49PM +0200, Pavel Machek wrote:
> Hi!
> 
> > > > First, I re-did pipeline setup in python, it seems slightly less hacky
> > > > then in shell.
> > > > 
> > > > I tried to modify fcam-dev to work with the new interface, but was not
> > > > successful so far. I can post patches if someone is interested
> > > > (mplayer works for me, but that's not too suitable for taking photos).
> > > > 
> > > > I tried to get gstreamer to work, with something like:
> > > 
> > > While trying to debug gstreamer, I ran v4l2-compliance, and it seems
> > > to suggest that QUERYCAP is required... but it is not present on
> > > /dev/video2 or video6.
> > 
> > It's not saying that it wouldn't be present, but the content appears wrong.
> > It should have the real bus information there rather than just "media".
> > 
> > See e.g. drivers/media/platform/vsp1/vsp1_drv.c . I suppose that should be
> > right.
> > 
> > Feel free to submit a patch. :-)
> 
> For now I'd not know what to change, sorry :-(. Perhaps we can debug
> it after the support is merged into mainline.

A single line change to change the bus field contents to the actual bus
address.

	grep -A1 platform: drivers/media/platform/vsp1/vsp1_drv.c

> 
> Another weirdness:
> 
> yavta, on v4l-subdev12 :
> 
> control 0x00a40906 `Sensivity' min 0 max 0 step 1 default 0 current 65536.
> 
> Min and max being the same, I don't think I can control the
> sensitivity. I guess I'll have to provid  more light for the tests for
> now...

That control should be removed. I think I concluded its value is the same
for all the modes...

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-20 20:59         ` Sakari Ailus
  2016-06-21 18:05           ` Pavel Machek
@ 2016-06-22 11:18           ` Pavel Machek
  2016-07-01  7:31           ` square-only image on Nokia N900 camera " Pavel Machek
  2 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-06-22 11:18 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi!

I tried to capture 1.2Mpix images, then scale them down to 800x600
using hardware... and results were  kernel dying.

[12552.400146] [<c062561c>] (isp_video_start_streaming) from
[<c061c6e8>] (vb2_start_streaming+0x5c/0x154)
[12552.400146] [<c061c6e8>] (vb2_start_streaming) from [<c061e5f8>]
(vb2_core_streamon+0x104/0x160)
[12552.400177] [<c061e5f8>] (vb2_core_streamon) from [<c0625260>]
(isp_video_streamon+0x17c/0x27c)
[12552.400207] [<c0625260>] (isp_video_streamon) from [<c0607a28>]
(v4l_streamon+0x18/0x1c)
[12552.400238] [<c0607a28>] (v4l_streamon) from [<c06096ac>]
(__video_do_ioctl+0x24c/0x2e8)
[12552.400268] [<c06096ac>] (__video_do_ioctl) from [<c060b2b0>]
(video_usercopy+0x110/0x600)
[12552.400299] [<c060b2b0>] (video_usercopy) from [<c060645c>]
(v4l2_ioctl+0x98/0xb8)
[12552.400329] [<c060645c>] (v4l2_ioctl) from [<c0269d28>]
(do_vfs_ioctl+0x80/0x948)
[12552.400329] [<c0269d28>] (do_vfs_ioctl) from [<c026a654>]
(SyS_ioctl+0x64/0x74)
[12552.400360] [<c026a654>] (SyS_ioctl) from [<c0107860>]
(ret_fast_syscall+0x0/0x3c)
[12552.400390] ---[ end trace b4627b34449829a7 ]---
[12552.400421] In-band Error seen by MPU  at address 0
[12552.400421] ------------[ cut here ]------------
[12552.400451] WARNING: CPU: 0 PID: 3936 at
drivers/bus/omap_l3_smx.c:166 omap3_l3_app_irq+0xcc/0x124
[12552.400482] Modules linked in:
[12552.400482] CPU: 0 PID: 3936 Comm: mplayer Tainted: G        W
4.6.0-177572-g501bb64-dirty #360
[12552.400512] Hardware name: Nokia RX-51 board

I did more experiments before, and usually it does not end like
this. Usually, when I set up capture with greater resolution than cca
800x600, I get 4 copies of image above each other.

If you know how to get images with greater resolution, let me know.

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (27 preceding siblings ...)
  2016-04-27  3:08   ` Sebastian Reichel
@ 2016-06-24 16:21   ` Pavel Machek
  2016-08-27 13:48   ` fcam-dev support for new kernels -- " Pavel Machek
  29 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-06-24 16:21 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!

> As omap3isp driver supports only one endpoint on ccp2 interface,
> but cameras on N900 require different strobe settings, so far
> it is not possible to have both cameras correctly working with
> the same board DTS. DTS patch in the series has the correct
> settings for the front camera. This is a problem still to be
> solved.
> 
> The needed pipeline could be made with:
> 
> media-ctl -r
> media-ctl -l '"vs6555 binner 2-0010":1 -> "video-bus-switch":2 [1]'
> media-ctl -l '"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]'
> media-ctl -l '"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]'
> media-ctl -l '"OMAP3 ISP CCDC":2 -> "OMAP3 ISP preview":0 [1]'
> media-ctl -l '"OMAP3 ISP preview":1 -> "OMAP3 ISP resizer":0 [1]'
> media-ctl -l '"OMAP3 ISP resizer":1 -> "OMAP3 ISP resizer output":0 [1]'
> media-ctl -V '"vs6555 pixel array 2-0010":0 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"vs6555 binner 2-0010":1 [SGRBG10/648x488 (0,0)/648x488 (0,0)/648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":0 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCP2":1 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP CCDC":2 [SGRBG10 648x488]'
> media-ctl -V '"OMAP3 ISP preview":1 [UYVY 648x488]'
> media-ctl -V '"OMAP3 ISP resizer":1 [UYVY 656x488]'
> 
> and tested with:
> 
> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://
>

Ok, I played with the back sensor, and can get it to work in 1Mpix
mode, but not in 5Mpix mode. 1MPix mode works ok both in previewer
mode and in capture mode. 5Mpix mode does 4 copies of the frame in
previewer (thus unusable), and when I try it in capture mode, I get

[  222.952514] omap3isp 480bc000.isp: CCDC won't become idle!
[  224.515716] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP CCDC

(script below can do it).

Any ideas? Thanks,
								Pavel

import subprocess
import time
import os

# TODO: Camera gain does not work                                                               
# LED indicator mode is not usable                                                              
# LED torch mode only has one brightness setting                                                

# sudo /my/v4l-utils/utils/media-ctl/media-ctl -p                                               
# This seems to have good description:                                                          
# https://www.compulab.co.il/workspace/mediawiki/index.php5/CM-T3730:_Linux:_Camera             

# aptitude install ufraw ?                                                                      

class Camera:
    mc="/my/v4l-utils/utils/media-ctl/media-ctl"
    ya="/my/tui/yavta/yavta"
    def __init__(m):
        if 1:
            # 4 copies in preview mode, "CCDC won't become idle!" in raw mode.                  
            m.win_x, m.win_y = 1200, 900
            m.cap_x, m.cap_y = 2592, 1968
...
    def media_ctl(m, s):
        if 0 != subprocess.call(['sudo', m.mc] + s):
            print("Call ", s, " failed?")

    def media_l(m, s):
        m.media_ctl(['-l', s])

    def media_v(m, s):
        m.media_ctl(['-V', s])

    def back_full(m):
        m.media_ctl(['-r'])

        m.media_l('"et8ek8 3-003e":0 -> "video-bus-switch":1 [1]')
        m.media_l('"video-bus-switch":0 -> "OMAP3 ISP CCP2":0 [1]')
        m.media_l('"OMAP3 ISP CCP2":1 -> "OMAP3 ISP CCDC":0 [1]')
        m.media_l('"OMAP3 ISP CCDC":1 -> "OMAP3 ISP CCDC output":0 [1]')

        size = "%dx%d" % (m.cap_x, m.cap_y)
        m.media_v('"et8ek8 3-003e":0 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCP2":0 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCP2":1 [SGRBG10 %s]' % size)
        m.media_v('"OMAP3 ISP CCDC":1 [SGRBG10 %s]' % size)
...
    def run_full(m):
	os.system(m.ya+" --capture=8 --pause --skip 0 --format SGRBG10 --size %dx%d /dev/video2\
 --file=/tmp/delme#" % (m.cap_x, m.cap_y))


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* square-only image on Nokia N900 camera -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-06-20 20:59         ` Sakari Ailus
  2016-06-21 18:05           ` Pavel Machek
  2016-06-22 11:18           ` Pavel Machek
@ 2016-07-01  7:31           ` Pavel Machek
  2016-07-01  8:50             ` Pavel Machek
  2 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-07-01  7:31 UTC (permalink / raw)
  To: Sakari Ailus, laurent.pinchart
  Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi!

On gitlab is the latest version of pipeline setup if python. I also
got fcam to work (slowly) on the camera, with autofocus and
autogain. Capturing from preview modes works fine, but image quality
is not good, as expected. Capturing raw GRBG10 images works, but
images are square, with values being outside square being 0.

Same problem is there with yavta and fcam-dev capture, so I suspect
there's something in kernel. If you have an idea what could be wrong /
what to try, let me know. If omap3isp works for you in v4.6, and
produces expected rectangular images, that would be useful to know,
too.

Python capture script is at

https://gitlab.com/tui/tui/commit/266b6eb302dcf1481e3e90a05bf98180e5759168

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: square-only image on Nokia N900 camera -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-07-01  7:31           ` square-only image on Nokia N900 camera " Pavel Machek
@ 2016-07-01  8:50             ` Pavel Machek
  2016-07-01 11:01               ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-07-01  8:50 UTC (permalink / raw)
  To: Sakari Ailus, laurent.pinchart
  Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

Hi!

> On gitlab is the latest version of pipeline setup if python. I also
> got fcam to work (slowly) on the camera, with autofocus and
> autogain. Capturing from preview modes works fine, but image quality
> is not good, as expected. Capturing raw GRBG10 images works, but
> images are square, with values being outside square being 0.
> 
> Same problem is there with yavta and fcam-dev capture, so I suspect
> there's something in kernel. If you have an idea what could be wrong /
> what to try, let me know. If omap3isp works for you in v4.6, and
> produces expected rectangular images, that would be useful to know,
> too.
> 
> Python capture script is at
> 
> https://gitlab.com/tui/tui/commit/266b6eb302dcf1481e3e90a05bf98180e5759168

I switched to the front camera (vs6555 pixel array 2-0010 + vs6555
binner 2-0010) and got same effect: preview image works fine, raw
image is square. Still kernel v4.6.

Ideas welcome,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: square-only image on Nokia N900 camera -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-07-01  8:50             ` Pavel Machek
@ 2016-07-01 11:01               ` Pavel Machek
  2016-07-01 19:40                 ` Pavel Machek
  0 siblings, 1 reply; 102+ messages in thread
From: Pavel Machek @ 2016-07-01 11:01 UTC (permalink / raw)
  To: Sakari Ailus, laurent.pinchart
  Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

On Fri 2016-07-01 10:50:25, Pavel Machek wrote:
> Hi!
> 
> > On gitlab is the latest version of pipeline setup if python. I also
> > got fcam to work (slowly) on the camera, with autofocus and
> > autogain. Capturing from preview modes works fine, but image quality
> > is not good, as expected. Capturing raw GRBG10 images works, but
> > images are square, with values being outside square being 0.
> > 
> > Same problem is there with yavta and fcam-dev capture, so I suspect
> > there's something in kernel. If you have an idea what could be wrong /
> > what to try, let me know. If omap3isp works for you in v4.6, and
> > produces expected rectangular images, that would be useful to know,
> > too.
> > 
> > Python capture script is at
> > 
> > https://gitlab.com/tui/tui/commit/266b6eb302dcf1481e3e90a05bf98180e5759168
> 
> I switched to the front camera (vs6555 pixel array 2-0010 + vs6555
> binner 2-0010) and got same effect: preview image works fine, raw
> image is square. Still kernel v4.6.

Same issue with kernel v4.7-rc5.

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: square-only image on Nokia N900 camera -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working)
  2016-07-01 11:01               ` Pavel Machek
@ 2016-07-01 19:40                 ` Pavel Machek
  0 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-07-01 19:40 UTC (permalink / raw)
  To: Sakari Ailus, laurent.pinchart
  Cc: Sebastian Reichel, Ivaylo Dimitrov, pali.rohar, linux-media

On Fri 2016-07-01 13:01:54, Pavel Machek wrote:
> On Fri 2016-07-01 10:50:25, Pavel Machek wrote:
> > Hi!
> > 
> > > On gitlab is the latest version of pipeline setup if python. I also
> > > got fcam to work (slowly) on the camera, with autofocus and
> > > autogain. Capturing from preview modes works fine, but image quality
> > > is not good, as expected. Capturing raw GRBG10 images works, but
> > > images are square, with values being outside square being 0.
> > > 
> > > Same problem is there with yavta and fcam-dev capture, so I suspect
> > > there's something in kernel. If you have an idea what could be wrong /
> > > what to try, let me know. If omap3isp works for you in v4.6, and
> > > produces expected rectangular images, that would be useful to know,
> > > too.
> > > 
> > > Python capture script is at
> > > 
> > > https://gitlab.com/tui/tui/commit/266b6eb302dcf1481e3e90a05bf98180e5759168
> > 
> > I switched to the front camera (vs6555 pixel array 2-0010 + vs6555
> > binner 2-0010) and got same effect: preview image works fine, raw
> > image is square. Still kernel v4.6.
> 
> Same issue with kernel v4.7-rc5.

And this seems to fix it. Now image fills whole frame.

But I still can't get 5MP format to work, it fails with:

[  497.929016] check_format checking px 808534338 808534338, h 1968
1968, w 2592 2592, bpline 5184 5184, size 10202112 10202112 field 1 1
[  497.929107] configuring for 2592(5184)x1968
[  497.936248] stream on success
[  498.020233] omap3isp 480bc000.isp: CCDC won't become idle!
[  525.563476] omap3isp 480bc000.isp: Unable to stop OMAP3 ISP CCDC


commit 5268a954cd6af4853ad8e05f32ff4741c245e65e
Author: Pavel <pavel@ucw.cz>
Date:   Fri Jul 1 21:34:35 2016 +0200

    This seems to fix stuff for me -- now square limitation of images is gone.

diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 5c52ae8..0e052e6 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1185,7 +1185,8 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
 	/* Use the raw, unprocessed data when writing to memory. The H3A and
 	 * histogram modules are still fed with lens shading corrected data.
 	 */
-	syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
+//	syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
+	syn_mode |= ISPCCDC_SYN_MODE_VP2SDR;
 
 	if (ccdc->output & CCDC_OUTPUT_MEMORY)
 		syn_mode |= ISPCCDC_SYN_MODE_WEN;

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* fcam-dev support for new kernels -- Re: [RFC PATCH 00/24] Make Nokia N900 cameras working
  2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
                     ` (28 preceding siblings ...)
  2016-06-24 16:21   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
@ 2016-08-27 13:48   ` Pavel Machek
  29 siblings, 0 replies; 102+ messages in thread
From: Pavel Machek @ 2016-08-27 13:48 UTC (permalink / raw)
  To: Ivaylo Dimitrov; +Cc: sakari.ailus, sre, pali.rohar, linux-media

Hi!


> 
> mplayer -tv driver=v4l2:width=656:height=488:outfmt=uyvy:device=/dev/video6 -vo xv -vf screenshot tv://

I modified fcam-dev to work with new kernel interface. It can now display
picture preview using SDL, and has somehow working autogain and autofocus.
It even includes very simple gui for manual focus and manual gain/exposure.

gitlab.com fcam-dev branch good.

Due to missing timestamping support, everything is quite slow. Auto white
balance does not work. 5 Mpix mode does not work (probably kernel problem),
and I do have hack in my kernel so that capture interface works.

Best regards,

								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

end of thread, other threads:[~2016-08-27 13:48 UTC | newest]

Thread overview: 102+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20160420081427.GZ32125@valkosipuli.retiisi.org.uk>
2016-04-24 21:08 ` [RFC PATCH 00/24] Make Nokia N900 cameras working Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 01/24] V4L fixes Ivaylo Dimitrov
2016-04-24 22:05     ` Pavel Machek
2016-04-25  7:29     ` Hans Verkuil
2016-04-25 13:25     ` Sakari Ailus
2016-04-25 16:32       ` Ivaylo Dimitrov
2016-04-29  7:41         ` Sakari Ailus
2016-04-24 21:08   ` [RFC PATCH 02/24] smiaregs: Generic i2c register writing Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 03/24] et8ek8: Toshiba 5MP sensor driver Ivaylo Dimitrov
2016-05-01 10:44     ` Sakari Ailus
2016-05-01 12:31       ` Ivaylo Dimitrov
2016-05-01 12:32         ` Ivaylo Dimitrov
2016-05-01 12:50       ` Ivaylo Dimitrov
2016-05-01 13:41         ` Sakari Ailus
2016-05-03 14:50           ` [PATCH] [media]: Driver for Toshiba et8ek8 5MP sensor Ivaylo Dimitrov
2016-05-22 10:07             ` Ivaylo Dimitrov
2016-05-24 11:19             ` Pavel Machek
2016-06-04 19:16               ` Ivaylo Dimitrov
2016-06-06  9:04               ` Sylwester Nawrocki
2016-05-25 21:45             ` Sakari Ailus
2016-06-04 19:54               ` Ivaylo Dimitrov
2016-06-09 23:13                 ` Sakari Ailus
2016-04-24 21:08   ` [RFC PATCH 04/24] smiapp-pll: Take existing divisor into account in minimum divisor check Ivaylo Dimitrov
2016-05-01 10:45     ` Sakari Ailus
2016-05-03 18:25       ` Ivaylo Dimitrov
2016-05-24  9:09       ` Pali Rohár
2016-05-24 10:17     ` Pavel Machek
2016-04-24 21:08   ` [RFC PATCH 05/24] smiapp: Add smiapp_has_quirk() to tell whether a quirk is implemented Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 06/24] smiapp: Add quirk control support Ivaylo Dimitrov
2016-05-01 10:46     ` Sakari Ailus
2016-05-03 18:32       ` Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 07/24] v4l: of: Call CSI2 bus csi2, not csi Ivaylo Dimitrov
2016-04-29 13:22     ` Pavel Machek
2016-04-24 21:08   ` [RFC PATCH 08/24] v4l: of: Obtain data bus type from bus-type property Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 09/24] v4l: Add CSI1 and CCP2 bus type to enum v4l2_mbus_type Ivaylo Dimitrov
2016-04-29 13:27     ` Pavel Machek
2016-04-24 21:08   ` [RFC PATCH 10/24] v4l: of: Separate lane parsing from CSI-2 bus parameter parsing Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 11/24] dt: bindings: v4l: Add bus-type video interface property Ivaylo Dimitrov
2016-04-29 13:28     ` Pavel Machek
2016-04-24 21:08   ` [RFC PATCH 12/24] dt: bindings: Add CSI1/CCP2 related properties to video-interfaces.txt Ivaylo Dimitrov
2016-04-29 13:39     ` Pavel Machek
2016-04-24 21:08   ` [RFC PATCH 13/24] v4l: of: Support CSI-1 and CCP2 busses Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 14/24] media: et8ek8: add device tree binding document Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 15/24] media: add subdev type for bus switch Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 16/24] media: video-bus-switch: new driver Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 17/24] smiapp: add CCP2 support Ivaylo Dimitrov
2016-05-01 10:57     ` Sakari Ailus
2016-04-24 21:08   ` [RFC PATCH 18/24] v4l2-async: per notifier locking Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 19/24] v4l2_device_register_subdev_nodes: allow calling multiple times Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 20/24] ARM: dts: omap3-n900: enable cameras Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 21/24] omap3isp: dt: Add support for CSI1/CCP2 busses Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 22/24] [media] omap3isp: Correctly set IO_OUT_SEL and VP_CLK_POL for CCP2 mode Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 23/24] [media] omap3isp: Make sure CSI1 interface is enabled in CPP2 mode Ivaylo Dimitrov
2016-04-24 21:08   ` [RFC PATCH 24/24] ARM: dts: omap3-n900: enable cameras - remove invalid entry Ivaylo Dimitrov
2016-04-24 21:55   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
2016-04-25  6:33     ` Ivaylo Dimitrov
2016-04-25 17:09       ` Pavel Machek
2016-04-25 17:21         ` Ivaylo Dimitrov
2016-04-27 21:07           ` Pavel Machek
2016-04-25 10:40   ` Pali Rohár
2016-04-25 14:06     ` Pavel Machek
2016-04-25 14:09       ` Hans Verkuil
2016-04-27 21:09         ` Pavel Machek
2016-04-25 14:14       ` Pali Rohár
2016-04-25 17:14         ` Pali Rohár
2016-04-25 16:58   ` Pavel Machek
2016-04-25 17:17     ` Ivaylo Dimitrov
2016-04-25 18:40       ` Pavel Machek
2016-04-25 19:17         ` Ivaylo Dimitrov
2016-04-25 20:41           ` Pavel Machek
2016-04-25 20:53             ` Ivaylo Dimitrov
2016-04-25 22:07               ` Pavel Machek
2016-04-26  4:21                 ` Ivaylo Dimitrov
2016-04-27  8:30                   ` Pavel Machek
2016-04-27  3:08   ` Sebastian Reichel
2016-04-27  5:05     ` Ivaylo Dimitrov
2016-04-27  6:57       ` Ivaylo Dimitrov
2016-04-27 16:42         ` Sebastian Reichel
2016-04-27 16:45           ` Pavel Machek
2016-04-27 16:59             ` Sebastian Reichel
2016-05-02  7:06               ` Pavel Machek
2016-04-27 17:12           ` Ивайло Димитров
2016-04-27 19:05             ` Pavel Machek
2016-04-29  0:05             ` Sebastian Reichel
2016-04-29 17:45               ` Sebastian Reichel
2016-04-29 18:44                 ` Ivaylo Dimitrov
2016-05-01 10:37                   ` Sakari Ailus
2016-05-01  9:03                 ` Pavel Machek
2016-04-27 20:30           ` Pavel Machek
2016-06-17 16:42     ` Nokia N900 cameras -- pipeline setup in python (was Re: [RFC PATCH 00/24] Make Nokia N900 cameras working) Pavel Machek
2016-06-17 17:12       ` Pavel Machek
2016-06-20 17:00         ` Pavel Machek
2016-06-20 20:59         ` Sakari Ailus
2016-06-21 18:05           ` Pavel Machek
2016-06-22  7:22             ` Sakari Ailus
2016-06-22 11:18           ` Pavel Machek
2016-07-01  7:31           ` square-only image on Nokia N900 camera " Pavel Machek
2016-07-01  8:50             ` Pavel Machek
2016-07-01 11:01               ` Pavel Machek
2016-07-01 19:40                 ` Pavel Machek
2016-06-24 16:21   ` [RFC PATCH 00/24] Make Nokia N900 cameras working Pavel Machek
2016-08-27 13:48   ` fcam-dev support for new kernels -- " Pavel Machek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.