diff mbox series

[v4,4/4] media: i2c: Add RDACM20 driver

Message ID 20181102154723.23662-5-kieran.bingham@ideasonboard.com (mailing list archive)
State New, archived
Headers show
Series MAX9286 GMSL Support | expand

Commit Message

Kieran Bingham Nov. 2, 2018, 3:47 p.m. UTC
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

The RDACM20 is a GMSL camera supporting 1280x800 resolution images
developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
GMSL serializer.

The GMSL link carries power, control (I2C) and video data over a
single coax cable.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

---
v2:
 - Fix MAINTAINERS entry

v3:
 - Use new V4L2_MBUS_CSI2_DPHY bus type
 - Remove 'always zero' error print
 - Fix module description
---
 MAINTAINERS                         |  10 +
 drivers/media/i2c/Kconfig           |  11 +
 drivers/media/i2c/Makefile          |   1 +
 drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
 drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
 5 files changed, 1610 insertions(+)
 create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
 create mode 100644 drivers/media/i2c/rdacm20.c

Comments

Sakari Ailus Nov. 20, 2018, 8:34 a.m. UTC | #1
Hi Kieran,

On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> The RDACM20 is a GMSL camera supporting 1280x800 resolution images
> developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
> GMSL serializer.
> 
> The GMSL link carries power, control (I2C) and video data over a
> single coax cable.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> ---
> v2:
>  - Fix MAINTAINERS entry
> 
> v3:
>  - Use new V4L2_MBUS_CSI2_DPHY bus type
>  - Remove 'always zero' error print
>  - Fix module description
> ---
>  MAINTAINERS                         |  10 +
>  drivers/media/i2c/Kconfig           |  11 +
>  drivers/media/i2c/Makefile          |   1 +
>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
>  5 files changed, 1610 insertions(+)
>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
>  create mode 100644 drivers/media/i2c/rdacm20.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 745f0fd1fff1..26ef20087a43 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12230,6 +12230,16 @@ S:	Supported
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>  F:	tools/testing/selftests/rcutorture
>  
> +RDACM20 Camera Sensor
> +M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
> +M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> +M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> +M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

I'm happy to see this will be well maintained. :-)

> +L:	linux-media@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
> +F:	drivers/media/i2c/rdacm20*
> +
>  RDC R-321X SoC
>  M:	Florian Fainelli <florian@openwrt.org>
>  S:	Maintained
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index eadc00bdd3bf..5eded5e337ec 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
>  	  This is a V4L2 sensor driver for Samsung S5C73M3
>  	  8 Mpixel camera.
>  
> +config VIDEO_RDACM20
> +	tristate "IMI RDACM20 camera support"
> +	depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> +	select V4L2_FWNODE
> +	help
> +	  This driver supports the IMI RDACM20 GMSL camera, used in
> +	  ADAS systems.
> +
> +	  This camera should be used in conjunction with a GMSL
> +	  deserialiser such as the MAX9286.
> +
>  comment "Flash devices"
>  
>  config VIDEO_ADP1653
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 4de7fe62b179..121d28283d45 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>  obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> +obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>  
>  obj-$(CONFIG_SDR_MAX2175) += max2175.o
> diff --git a/drivers/media/i2c/rdacm20-ov10635.h b/drivers/media/i2c/rdacm20-ov10635.h
> new file mode 100644
> index 000000000000..3c53a3262ee2
> --- /dev/null
> +++ b/drivers/media/i2c/rdacm20-ov10635.h
> @@ -0,0 +1,953 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * IMI RDACM20 camera OV10635 sensor register initialization values
> + *
> + * Copyright (C) 2017-2018 Jacopo Mondi
> + * Copyright (C) 2017-2018 Kieran Bingham
> + * Copyright (C) 2017-2018 Laurent Pinchart
> + * Copyright (C) 2017-2018 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + */
> +
> +/*
> + * Generated by the OmniVision ov10635 sensor camera wizard for
> + * 1280x800@30/UYVY/BT601/8bit.
> + */
> +
> +#ifndef __RDACM20_OV10635_H__
> +#define __RDACM20_OV10635_H__
> +
> +#define OV10635_SENSOR_WIDTH		1312
> +#define OV10635_SENSOR_HEIGHT		814
> +
> +#define OV10635_MAX_WIDTH		1280
> +#define OV10635_MAX_HEIGHT		800
> +
> +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
> +#define OV10635_HTS			1572
> +/* FPS = 29,9998 */
> +#define OV10635_VTS			933

A part of this driver looks like a driver for an OV camera sensor. Would
there be something that prevents separating the camera sensor driver from
this one?

> +
> +struct ov10635_reg {
> +	u16	reg;
> +	u8	val;
> +};
> +
> +static const struct ov10635_reg ov10635_regs_wizard[] = {
> +	{ 0x301b, 0xff },
> +	{ 0x301c, 0xff },
> +	{ 0x301a, 0xff },
> +	{ 0x3011, 0x42 },
> +	{ 0x6900, 0x0c },
> +	{ 0x6901, 0x19 },
> +	{ 0x3503, 0x10 },
> +	{ 0x3025, 0x03 },
> +	{ 0x3003, 0x16 },
> +	{ 0x3004, 0x30 },
> +	{ 0x3005, 0x40 },
> +	{ 0x3006, 0x91 },
> +	{ 0x3600, 0x74 },
> +	{ 0x3601, 0x2b },
> +	{ 0x3612, 0x00 },
> +	{ 0x3611, 0x67 },
> +	{ 0x3633, 0xca },
> +	{ 0x3602, 0xaf },
> +	{ 0x3603, 0x04 },
> +	{ 0x3630, 0x28 },
> +	{ 0x3631, 0x16 },
> +	{ 0x3714, 0x10 },
> +	{ 0x371d, 0x01 },
> +	{ 0x4300, 0x3a },
> +	{ 0x3007, 0x01 },
> +	{ 0x3024, 0x03 },
> +	{ 0x3020, 0x0a },
> +	{ 0x3702, 0x0d },
> +	{ 0x3703, 0x20 },
> +	{ 0x3704, 0x15 },
> +	{ 0x3709, 0xa8 },
> +	{ 0x370c, 0xc7 },
> +	{ 0x370d, 0x80 },
> +	{ 0x3712, 0x00 },
> +	{ 0x3713, 0x20 },
> +	{ 0x3715, 0x04 },
> +	{ 0x381d, 0x40 },
> +	{ 0x381c, 0x00 },
> +	{ 0x3822, 0x50 },
> +	{ 0x3824, 0x10 },
> +	{ 0x3815, 0x8c },
> +	{ 0x3804, 0x05 },
> +	{ 0x3805, 0x1f },
> +	{ 0x3800, 0x00 },
> +	{ 0x3801, 0x00 },
> +	{ 0x3806, 0x03 },
> +	{ 0x3807, 0x28 },
> +	{ 0x3802, 0x00 },
> +	{ 0x3803, 0x07 },
> +	{ 0x3808, 0x05 },
> +	{ 0x3809, 0x00 },
> +	{ 0x380a, 0x03 },
> +	{ 0x380b, 0x20 },
> +	{ 0x380c, OV10635_HTS >> 8 },
> +	{ 0x380d, OV10635_HTS & 0xff },
> +	{ 0x380e, OV10635_VTS >> 8 },
> +	{ 0x380f, OV10635_VTS & 0xff },
> +	{ 0x3813, 0x02 },
> +	{ 0x3811, 0x08 },
> +	{ 0x381f, 0x0c },
> +	{ 0x3819, 0x04 },
> +	{ 0x3804, 0x01 },
> +	{ 0x3805, 0x00 },
> +	{ 0x3828, 0x03 },
> +	{ 0x3829, 0x10 },
> +	{ 0x382a, 0x10 },
> +	{ 0x3621, 0x63 },
> +	{ 0x5005, 0x08 },
> +	{ 0x56d5, 0x00 },
> +	{ 0x56d6, 0x80 },
> +	{ 0x56d7, 0x00 },
> +	{ 0x56d8, 0x00 },
> +	{ 0x56d9, 0x00 },
> +	{ 0x56da, 0x80 },
> +	{ 0x56db, 0x00 },
> +	{ 0x56dc, 0x00 },
> +	{ 0x56e8, 0x00 },
> +	{ 0x56e9, 0x7f },
> +	{ 0x56ea, 0x00 },
> +	{ 0x56eb, 0x7f },
> +	{ 0x5100, 0x00 },
> +	{ 0x5101, 0x80 },
> +	{ 0x5102, 0x00 },
> +	{ 0x5103, 0x80 },
> +	{ 0x5104, 0x00 },
> +	{ 0x5105, 0x80 },
> +	{ 0x5106, 0x00 },
> +	{ 0x5107, 0x80 },
> +	{ 0x5108, 0x00 },
> +	{ 0x5109, 0x00 },
> +	{ 0x510a, 0x00 },
> +	{ 0x510b, 0x00 },
> +	{ 0x510c, 0x00 },
> +	{ 0x510d, 0x00 },
> +	{ 0x510e, 0x00 },
> +	{ 0x510f, 0x00 },
> +	{ 0x5110, 0x00 },
> +	{ 0x5111, 0x80 },
> +	{ 0x5112, 0x00 },
> +	{ 0x5113, 0x80 },
> +	{ 0x5114, 0x00 },
> +	{ 0x5115, 0x80 },
> +	{ 0x5116, 0x00 },
> +	{ 0x5117, 0x80 },
> +	{ 0x5118, 0x00 },
> +	{ 0x5119, 0x00 },
> +	{ 0x511a, 0x00 },
> +	{ 0x511b, 0x00 },
> +	{ 0x511c, 0x00 },
> +	{ 0x511d, 0x00 },
> +	{ 0x511e, 0x00 },
> +	{ 0x511f, 0x00 },
> +	{ 0x56d0, 0x00 },
> +	{ 0x5006, 0x04 },
> +	{ 0x5608, 0x05 },
> +	{ 0x52d7, 0x06 },
> +	{ 0x528d, 0x08 },
> +	{ 0x5293, 0x12 },
> +	{ 0x52d3, 0x12 },
> +	{ 0x5288, 0x06 },
> +	{ 0x5289, 0x20 },
> +	{ 0x52c8, 0x06 },
> +	{ 0x52c9, 0x20 },
> +	{ 0x52cd, 0x04 },
> +	{ 0x5381, 0x00 },
> +	{ 0x5382, 0xff },
> +	{ 0x5589, 0x76 },
> +	{ 0x558a, 0x47 },
> +	{ 0x558b, 0xef },
> +	{ 0x558c, 0xc9 },
> +	{ 0x558d, 0x49 },
> +	{ 0x558e, 0x30 },
> +	{ 0x558f, 0x67 },
> +	{ 0x5590, 0x3f },
> +	{ 0x5591, 0xf0 },
> +	{ 0x5592, 0x10 },
> +	{ 0x55a2, 0x6d },
> +	{ 0x55a3, 0x55 },
> +	{ 0x55a4, 0xc3 },
> +	{ 0x55a5, 0xb5 },
> +	{ 0x55a6, 0x43 },
> +	{ 0x55a7, 0x38 },
> +	{ 0x55a8, 0x5f },
> +	{ 0x55a9, 0x4b },
> +	{ 0x55aa, 0xf0 },
> +	{ 0x55ab, 0x10 },
> +	{ 0x5581, 0x52 },
> +	{ 0x5300, 0x01 },
> +	{ 0x5301, 0x00 },
> +	{ 0x5302, 0x00 },
> +	{ 0x5303, 0x0e },
> +	{ 0x5304, 0x00 },
> +	{ 0x5305, 0x0e },
> +	{ 0x5306, 0x00 },
> +	{ 0x5307, 0x36 },
> +	{ 0x5308, 0x00 },
> +	{ 0x5309, 0xd9 },
> +	{ 0x530a, 0x00 },
> +	{ 0x530b, 0x0f },
> +	{ 0x530c, 0x00 },
> +	{ 0x530d, 0x2c },
> +	{ 0x530e, 0x00 },
> +	{ 0x530f, 0x59 },
> +	{ 0x5310, 0x00 },
> +	{ 0x5311, 0x7b },
> +	{ 0x5312, 0x00 },
> +	{ 0x5313, 0x22 },
> +	{ 0x5314, 0x00 },
> +	{ 0x5315, 0xd5 },
> +	{ 0x5316, 0x00 },
> +	{ 0x5317, 0x13 },
> +	{ 0x5318, 0x00 },
> +	{ 0x5319, 0x18 },
> +	{ 0x531a, 0x00 },
> +	{ 0x531b, 0x26 },
> +	{ 0x531c, 0x00 },
> +	{ 0x531d, 0xdc },
> +	{ 0x531e, 0x00 },
> +	{ 0x531f, 0x02 },
> +	{ 0x5320, 0x00 },
> +	{ 0x5321, 0x24 },
> +	{ 0x5322, 0x00 },
> +	{ 0x5323, 0x56 },
> +	{ 0x5324, 0x00 },
> +	{ 0x5325, 0x85 },
> +	{ 0x5326, 0x00 },
> +	{ 0x5327, 0x20 },
> +	{ 0x5609, 0x01 },
> +	{ 0x560a, 0x40 },
> +	{ 0x560b, 0x01 },
> +	{ 0x560c, 0x40 },
> +	{ 0x560d, 0x00 },
> +	{ 0x560e, 0xfa },
> +	{ 0x560f, 0x00 },
> +	{ 0x5610, 0xfa },
> +	{ 0x5611, 0x02 },
> +	{ 0x5612, 0x80 },
> +	{ 0x5613, 0x02 },
> +	{ 0x5614, 0x80 },
> +	{ 0x5615, 0x01 },
> +	{ 0x5616, 0x2c },
> +	{ 0x5617, 0x01 },
> +	{ 0x5618, 0x2c },
> +	{ 0x563b, 0x01 },
> +	{ 0x563c, 0x01 },
> +	{ 0x563d, 0x01 },
> +	{ 0x563e, 0x01 },
> +	{ 0x563f, 0x03 },
> +	{ 0x5640, 0x03 },
> +	{ 0x5641, 0x03 },
> +	{ 0x5642, 0x05 },
> +	{ 0x5643, 0x09 },
> +	{ 0x5644, 0x05 },
> +	{ 0x5645, 0x05 },
> +	{ 0x5646, 0x05 },
> +	{ 0x5647, 0x05 },
> +	{ 0x5651, 0x00 },
> +	{ 0x5652, 0x80 },
> +	{ 0x521a, 0x01 },
> +	{ 0x521b, 0x03 },
> +	{ 0x521c, 0x06 },
> +	{ 0x521d, 0x0a },
> +	{ 0x521e, 0x0e },
> +	{ 0x521f, 0x12 },
> +	{ 0x5220, 0x16 },
> +	{ 0x5223, 0x02 },
> +	{ 0x5225, 0x04 },
> +	{ 0x5227, 0x08 },
> +	{ 0x5229, 0x0c },
> +	{ 0x522b, 0x12 },
> +	{ 0x522d, 0x18 },
> +	{ 0x522f, 0x1e },
> +	{ 0x5241, 0x04 },
> +	{ 0x5242, 0x01 },
> +	{ 0x5243, 0x03 },
> +	{ 0x5244, 0x06 },
> +	{ 0x5245, 0x0a },
> +	{ 0x5246, 0x0e },
> +	{ 0x5247, 0x12 },
> +	{ 0x5248, 0x16 },
> +	{ 0x524a, 0x03 },
> +	{ 0x524c, 0x04 },
> +	{ 0x524e, 0x08 },
> +	{ 0x5250, 0x0c },
> +	{ 0x5252, 0x12 },
> +	{ 0x5254, 0x18 },
> +	{ 0x5256, 0x1e },
> +	/* fifo_line_length = 2*hts */
> +	{ 0x4606, (2 * OV10635_HTS) >> 8 },
> +	{ 0x4607, (2 * OV10635_HTS) & 0xff },
> +	/* fifo_hsync_start = 2*(hts - xres) */
> +	{ 0x460a, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) >> 8 },
> +	{ 0x460b, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) & 0xff },
> +	{ 0x460c, 0x00 },
> +	{ 0x4620, 0x0e },
> +	/* BT601: 0x08 is also acceptable as HS/VS mode */
> +	{ 0x4700, 0x04 },
> +	{ 0x4701, 0x00 },
> +	{ 0x4702, 0x01 },
> +	{ 0x4004, 0x04 },
> +	{ 0x4005, 0x18 },
> +	{ 0x4001, 0x06 },
> +	{ 0x4050, 0x22 },
> +	{ 0x4051, 0x24 },
> +	{ 0x4052, 0x02 },
> +	{ 0x4057, 0x9c },
> +	{ 0x405a, 0x00 },
> +	{ 0x4202, 0x02 },
> +	{ 0x3023, 0x10 },
> +	{ 0x0100, 0x01 },
> +	{ 0x0100, 0x01 },
> +	{ 0x6f10, 0x07 },
> +	{ 0x6f11, 0x82 },
> +	{ 0x6f12, 0x04 },
> +	{ 0x6f13, 0x00 },
> +	{ 0xd000, 0x19 },
> +	{ 0xd001, 0xa0 },
> +	{ 0xd002, 0x00 },
> +	{ 0xd003, 0x01 },
> +	{ 0xd004, 0xa9 },
> +	{ 0xd005, 0xad },
> +	{ 0xd006, 0x10 },
> +	{ 0xd007, 0x40 },
> +	{ 0xd008, 0x44 },
> +	{ 0xd009, 0x00 },
> +	{ 0xd00a, 0x68 },
> +	{ 0xd00b, 0x00 },
> +	{ 0xd00c, 0x15 },
> +	{ 0xd00d, 0x00 },
> +	{ 0xd00e, 0x00 },
> +	{ 0xd00f, 0x00 },
> +	{ 0xd040, 0x9c },
> +	{ 0xd041, 0x21 },
> +	{ 0xd042, 0xff },
> +	{ 0xd043, 0xf8 },
> +	{ 0xd044, 0xd4 },
> +	{ 0xd045, 0x01 },
> +	{ 0xd046, 0x48 },
> +	{ 0xd047, 0x00 },
> +	{ 0xd048, 0xd4 },
> +	{ 0xd049, 0x01 },
> +	{ 0xd04a, 0x50 },
> +	{ 0xd04b, 0x04 },
> +	{ 0xd04c, 0x18 },
> +	{ 0xd04d, 0x60 },
> +	{ 0xd04e, 0x00 },
> +	{ 0xd04f, 0x01 },
> +	{ 0xd050, 0xa8 },
> +	{ 0xd051, 0x63 },
> +	{ 0xd052, 0x02 },
> +	{ 0xd053, 0xa4 },
> +	{ 0xd054, 0x85 },
> +	{ 0xd055, 0x43 },
> +	{ 0xd056, 0x00 },
> +	{ 0xd057, 0x00 },
> +	{ 0xd058, 0x18 },
> +	{ 0xd059, 0x60 },
> +	{ 0xd05a, 0x00 },
> +	{ 0xd05b, 0x01 },
> +	{ 0xd05c, 0xa8 },
> +	{ 0xd05d, 0x63 },
> +	{ 0xd05e, 0x03 },
> +	{ 0xd05f, 0xf0 },
> +	{ 0xd060, 0x98 },
> +	{ 0xd061, 0xa3 },
> +	{ 0xd062, 0x00 },
> +	{ 0xd063, 0x00 },
> +	{ 0xd064, 0x8c },
> +	{ 0xd065, 0x6a },
> +	{ 0xd066, 0x00 },
> +	{ 0xd067, 0x6e },
> +	{ 0xd068, 0xe5 },
> +	{ 0xd069, 0x85 },
> +	{ 0xd06a, 0x18 },
> +	{ 0xd06b, 0x00 },
> +	{ 0xd06c, 0x10 },
> +	{ 0xd06d, 0x00 },
> +	{ 0xd06e, 0x00 },
> +	{ 0xd06f, 0x10 },
> +	{ 0xd070, 0x9c },
> +	{ 0xd071, 0x80 },
> +	{ 0xd072, 0x00 },
> +	{ 0xd073, 0x03 },
> +	{ 0xd074, 0x18 },
> +	{ 0xd075, 0x60 },
> +	{ 0xd076, 0x00 },
> +	{ 0xd077, 0x01 },
> +	{ 0xd078, 0xa8 },
> +	{ 0xd079, 0x63 },
> +	{ 0xd07a, 0x07 },
> +	{ 0xd07b, 0x80 },
> +	{ 0xd07c, 0x07 },
> +	{ 0xd07d, 0xff },
> +	{ 0xd07e, 0xf9 },
> +	{ 0xd07f, 0x03 },
> +	{ 0xd080, 0x8c },
> +	{ 0xd081, 0x63 },
> +	{ 0xd082, 0x00 },
> +	{ 0xd083, 0x00 },
> +	{ 0xd084, 0xa5 },
> +	{ 0xd085, 0x6b },
> +	{ 0xd086, 0x00 },
> +	{ 0xd087, 0xff },
> +	{ 0xd088, 0x18 },
> +	{ 0xd089, 0x80 },
> +	{ 0xd08a, 0x00 },
> +	{ 0xd08b, 0x01 },
> +	{ 0xd08c, 0xa8 },
> +	{ 0xd08d, 0x84 },
> +	{ 0xd08e, 0x01 },
> +	{ 0xd08f, 0x04 },
> +	{ 0xd090, 0xe1 },
> +	{ 0xd091, 0x6b },
> +	{ 0xd092, 0x58 },
> +	{ 0xd093, 0x00 },
> +	{ 0xd094, 0x94 },
> +	{ 0xd095, 0x6a },
> +	{ 0xd096, 0x00 },
> +	{ 0xd097, 0x70 },
> +	{ 0xd098, 0xe1 },
> +	{ 0xd099, 0x6b },
> +	{ 0xd09a, 0x20 },
> +	{ 0xd09b, 0x00 },
> +	{ 0xd09c, 0x95 },
> +	{ 0xd09d, 0x6b },
> +	{ 0xd09e, 0x00 },
> +	{ 0xd09f, 0x00 },
> +	{ 0xd0a0, 0xe4 },
> +	{ 0xd0a1, 0x8b },
> +	{ 0xd0a2, 0x18 },
> +	{ 0xd0a3, 0x00 },
> +	{ 0xd0a4, 0x0c },
> +	{ 0xd0a5, 0x00 },
> +	{ 0xd0a6, 0x00 },
> +	{ 0xd0a7, 0x23 },
> +	{ 0xd0a8, 0x15 },
> +	{ 0xd0a9, 0x00 },
> +	{ 0xd0aa, 0x00 },
> +	{ 0xd0ab, 0x00 },
> +	{ 0xd0ac, 0x18 },
> +	{ 0xd0ad, 0x60 },
> +	{ 0xd0ae, 0x80 },
> +	{ 0xd0af, 0x06 },
> +	{ 0xd0b0, 0xa8 },
> +	{ 0xd0b1, 0x83 },
> +	{ 0xd0b2, 0x40 },
> +	{ 0xd0b3, 0x08 },
> +	{ 0xd0b4, 0xa8 },
> +	{ 0xd0b5, 0xe3 },
> +	{ 0xd0b6, 0x38 },
> +	{ 0xd0b7, 0x2a },
> +	{ 0xd0b8, 0xa8 },
> +	{ 0xd0b9, 0xc3 },
> +	{ 0xd0ba, 0x40 },
> +	{ 0xd0bb, 0x09 },
> +	{ 0xd0bc, 0xa8 },
> +	{ 0xd0bd, 0xa3 },
> +	{ 0xd0be, 0x38 },
> +	{ 0xd0bf, 0x29 },
> +	{ 0xd0c0, 0x8c },
> +	{ 0xd0c1, 0x65 },
> +	{ 0xd0c2, 0x00 },
> +	{ 0xd0c3, 0x00 },
> +	{ 0xd0c4, 0xd8 },
> +	{ 0xd0c5, 0x04 },
> +	{ 0xd0c6, 0x18 },
> +	{ 0xd0c7, 0x00 },
> +	{ 0xd0c8, 0x8c },
> +	{ 0xd0c9, 0x67 },
> +	{ 0xd0ca, 0x00 },
> +	{ 0xd0cb, 0x00 },
> +	{ 0xd0cc, 0xd8 },
> +	{ 0xd0cd, 0x06 },
> +	{ 0xd0ce, 0x18 },
> +	{ 0xd0cf, 0x00 },
> +	{ 0xd0d0, 0x18 },
> +	{ 0xd0d1, 0x60 },
> +	{ 0xd0d2, 0x80 },
> +	{ 0xd0d3, 0x06 },
> +	{ 0xd0d4, 0xa8 },
> +	{ 0xd0d5, 0xe3 },
> +	{ 0xd0d6, 0x67 },
> +	{ 0xd0d7, 0x02 },
> +	{ 0xd0d8, 0xa9 },
> +	{ 0xd0d9, 0x03 },
> +	{ 0xd0da, 0x67 },
> +	{ 0xd0db, 0x03 },
> +	{ 0xd0dc, 0xa8 },
> +	{ 0xd0dd, 0xc3 },
> +	{ 0xd0de, 0x3d },
> +	{ 0xd0df, 0x05 },
> +	{ 0xd0e0, 0x8c },
> +	{ 0xd0e1, 0x66 },
> +	{ 0xd0e2, 0x00 },
> +	{ 0xd0e3, 0x00 },
> +	{ 0xd0e4, 0xb8 },
> +	{ 0xd0e5, 0x63 },
> +	{ 0xd0e6, 0x00 },
> +	{ 0xd0e7, 0x18 },
> +	{ 0xd0e8, 0xb8 },
> +	{ 0xd0e9, 0x63 },
> +	{ 0xd0ea, 0x00 },
> +	{ 0xd0eb, 0x98 },
> +	{ 0xd0ec, 0xbc },
> +	{ 0xd0ed, 0x03 },
> +	{ 0xd0ee, 0x00 },
> +	{ 0xd0ef, 0x00 },
> +	{ 0xd0f0, 0x10 },
> +	{ 0xd0f1, 0x00 },
> +	{ 0xd0f2, 0x00 },
> +	{ 0xd0f3, 0x16 },
> +	{ 0xd0f4, 0xb8 },
> +	{ 0xd0f5, 0x83 },
> +	{ 0xd0f6, 0x00 },
> +	{ 0xd0f7, 0x19 },
> +	{ 0xd0f8, 0x8c },
> +	{ 0xd0f9, 0x67 },
> +	{ 0xd0fa, 0x00 },
> +	{ 0xd0fb, 0x00 },
> +	{ 0xd0fc, 0xb8 },
> +	{ 0xd0fd, 0xa4 },
> +	{ 0xd0fe, 0x00 },
> +	{ 0xd0ff, 0x98 },
> +	{ 0xd100, 0xb8 },
> +	{ 0xd101, 0x83 },
> +	{ 0xd102, 0x00 },
> +	{ 0xd103, 0x08 },
> +	{ 0xd104, 0x8c },
> +	{ 0xd105, 0x68 },
> +	{ 0xd106, 0x00 },
> +	{ 0xd107, 0x00 },
> +	{ 0xd108, 0xe0 },
> +	{ 0xd109, 0x63 },
> +	{ 0xd10a, 0x20 },
> +	{ 0xd10b, 0x04 },
> +	{ 0xd10c, 0xe0 },
> +	{ 0xd10d, 0x65 },
> +	{ 0xd10e, 0x18 },
> +	{ 0xd10f, 0x00 },
> +	{ 0xd110, 0xa4 },
> +	{ 0xd111, 0x83 },
> +	{ 0xd112, 0xff },
> +	{ 0xd113, 0xff },
> +	{ 0xd114, 0xb8 },
> +	{ 0xd115, 0x64 },
> +	{ 0xd116, 0x00 },
> +	{ 0xd117, 0x48 },
> +	{ 0xd118, 0xd8 },
> +	{ 0xd119, 0x07 },
> +	{ 0xd11a, 0x18 },
> +	{ 0xd11b, 0x00 },
> +	{ 0xd11c, 0xd8 },
> +	{ 0xd11d, 0x08 },
> +	{ 0xd11e, 0x20 },
> +	{ 0xd11f, 0x00 },
> +	{ 0xd120, 0x9c },
> +	{ 0xd121, 0x60 },
> +	{ 0xd122, 0x00 },
> +	{ 0xd123, 0x00 },
> +	{ 0xd124, 0xd8 },
> +	{ 0xd125, 0x06 },
> +	{ 0xd126, 0x18 },
> +	{ 0xd127, 0x00 },
> +	{ 0xd128, 0x00 },
> +	{ 0xd129, 0x00 },
> +	{ 0xd12a, 0x00 },
> +	{ 0xd12b, 0x08 },
> +	{ 0xd12c, 0x15 },
> +	{ 0xd12d, 0x00 },
> +	{ 0xd12e, 0x00 },
> +	{ 0xd12f, 0x00 },
> +	{ 0xd130, 0x8c },
> +	{ 0xd131, 0x6a },
> +	{ 0xd132, 0x00 },
> +	{ 0xd133, 0x76 },
> +	{ 0xd134, 0xbc },
> +	{ 0xd135, 0x23 },
> +	{ 0xd136, 0x00 },
> +	{ 0xd137, 0x00 },
> +	{ 0xd138, 0x13 },
> +	{ 0xd139, 0xff },
> +	{ 0xd13a, 0xff },
> +	{ 0xd13b, 0xe6 },
> +	{ 0xd13c, 0x18 },
> +	{ 0xd13d, 0x60 },
> +	{ 0xd13e, 0x80 },
> +	{ 0xd13f, 0x06 },
> +	{ 0xd140, 0x03 },
> +	{ 0xd141, 0xff },
> +	{ 0xd142, 0xff },
> +	{ 0xd143, 0xdd },
> +	{ 0xd144, 0xa8 },
> +	{ 0xd145, 0x83 },
> +	{ 0xd146, 0x40 },
> +	{ 0xd147, 0x08 },
> +	{ 0xd148, 0x85 },
> +	{ 0xd149, 0x21 },
> +	{ 0xd14a, 0x00 },
> +	{ 0xd14b, 0x00 },
> +	{ 0xd14c, 0x85 },
> +	{ 0xd14d, 0x41 },
> +	{ 0xd14e, 0x00 },
> +	{ 0xd14f, 0x04 },
> +	{ 0xd150, 0x44 },
> +	{ 0xd151, 0x00 },
> +	{ 0xd152, 0x48 },
> +	{ 0xd153, 0x00 },
> +	{ 0xd154, 0x9c },
> +	{ 0xd155, 0x21 },
> +	{ 0xd156, 0x00 },
> +	{ 0xd157, 0x08 },
> +	{ 0x6f0e, 0x03 },
> +	{ 0x6f0f, 0x00 },
> +	{ 0x460e, 0x08 },
> +	{ 0x460f, 0x01 },
> +	{ 0x4610, 0x00 },
> +	{ 0x4611, 0x01 },
> +	{ 0x4612, 0x00 },
> +	{ 0x4613, 0x01 },
> +	/* 8 bits */
> +	{ 0x4605, 0x08 },
> +	/* Swap data bits order [9:0] -> [0:9] */
> +	{ 0x4709, 0x10 },
> +	{ 0x4608, 0x00 },
> +	{ 0x4609, 0x08 },
> +	{ 0x6804, 0x00 },
> +	{ 0x6805, 0x06 },
> +	{ 0x6806, 0x00 },
> +	{ 0x5120, 0x00 },
> +	{ 0x3510, 0x00 },
> +	{ 0x3504, 0x00 },
> +	{ 0x6800, 0x00 },
> +	{ 0x6f0d, 0x01 },
> +	/* PCLK falling edge */
> +	{ 0x4708, 0x01 },
> +	{ 0x5000, 0xff },
> +	{ 0x5001, 0xbf },
> +	{ 0x5002, 0x7e },
> +	{ 0x503d, 0x00 },
> +	{ 0xc450, 0x01 },
> +	{ 0xc452, 0x04 },
> +	{ 0xc453, 0x00 },
> +	{ 0xc454, 0x00 },
> +	{ 0xc455, 0x01 },
> +	{ 0xc456, 0x01 },
> +	{ 0xc457, 0x00 },
> +	{ 0xc458, 0x00 },
> +	{ 0xc459, 0x00 },
> +	{ 0xc45b, 0x00 },
> +	{ 0xc45c, 0x01 },
> +	{ 0xc45d, 0x00 },
> +	{ 0xc45e, 0x00 },
> +	{ 0xc45f, 0x00 },
> +	{ 0xc460, 0x00 },
> +	{ 0xc461, 0x01 },
> +	{ 0xc462, 0x01 },
> +	{ 0xc464, 0x03 },
> +	{ 0xc465, 0x00 },
> +	{ 0xc466, 0x8a },
> +	{ 0xc467, 0x00 },
> +	{ 0xc468, 0x86 },
> +	{ 0xc469, 0x00 },
> +	{ 0xc46a, 0x40 },
> +	{ 0xc46b, 0x50 },
> +	{ 0xc46c, 0x30 },
> +	{ 0xc46d, 0x28 },
> +	{ 0xc46e, 0x60 },
> +	{ 0xc46f, 0x40 },
> +	{ 0xc47c, 0x01 },
> +	{ 0xc47d, 0x38 },
> +	{ 0xc47e, 0x00 },
> +	{ 0xc47f, 0x00 },
> +	{ 0xc480, 0x00 },
> +	{ 0xc481, 0xff },
> +	{ 0xc482, 0x00 },
> +	{ 0xc483, 0x40 },
> +	{ 0xc484, 0x00 },
> +	{ 0xc485, 0x18 },
> +	{ 0xc486, 0x00 },
> +	{ 0xc487, 0x18 },
> +	{ 0xc488, (OV10635_VTS - 8) * 16 >> 8},
> +	{ 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
> +	{ 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
> +	{ 0xc48b, (OV10635_VTS - 8) * 16 & 0xff},
> +	{ 0xc48c, 0x00 },
> +	{ 0xc48d, 0x04 },
> +	{ 0xc48e, 0x00 },
> +	{ 0xc48f, 0x04 },
> +	{ 0xc490, 0x03 },
> +	{ 0xc492, 0x20 },
> +	{ 0xc493, 0x08 },
> +	{ 0xc498, 0x02 },
> +	{ 0xc499, 0x00 },
> +	{ 0xc49a, 0x02 },
> +	{ 0xc49b, 0x00 },
> +	{ 0xc49c, 0x02 },
> +	{ 0xc49d, 0x00 },
> +	{ 0xc49e, 0x02 },
> +	{ 0xc49f, 0x60 },
> +	{ 0xc4a0, 0x03 },
> +	{ 0xc4a1, 0x00 },
> +	{ 0xc4a2, 0x04 },
> +	{ 0xc4a3, 0x00 },
> +	{ 0xc4a4, 0x00 },
> +	{ 0xc4a5, 0x10 },
> +	{ 0xc4a6, 0x00 },
> +	{ 0xc4a7, 0x40 },
> +	{ 0xc4a8, 0x00 },
> +	{ 0xc4a9, 0x80 },
> +	{ 0xc4aa, 0x0d },
> +	{ 0xc4ab, 0x00 },
> +	{ 0xc4ac, 0x0f },
> +	{ 0xc4ad, 0xc0 },
> +	{ 0xc4b4, 0x01 },
> +	{ 0xc4b5, 0x01 },
> +	{ 0xc4b6, 0x00 },
> +	{ 0xc4b7, 0x01 },
> +	{ 0xc4b8, 0x00 },
> +	{ 0xc4b9, 0x01 },
> +	{ 0xc4ba, 0x01 },
> +	{ 0xc4bb, 0x00 },
> +	{ 0xc4bc, 0x01 },
> +	{ 0xc4bd, 0x60 },
> +	{ 0xc4be, 0x02 },
> +	{ 0xc4bf, 0x33 },
> +	{ 0xc4c8, 0x03 },
> +	{ 0xc4c9, 0xd0 },
> +	{ 0xc4ca, 0x0e },
> +	{ 0xc4cb, 0x00 },
> +	{ 0xc4cc, 0x0e },
> +	{ 0xc4cd, 0x51 },
> +	{ 0xc4ce, 0x0e },
> +	{ 0xc4cf, 0x51 },
> +	{ 0xc4d0, 0x04 },
> +	{ 0xc4d1, 0x80 },
> +	{ 0xc4e0, 0x04 },
> +	{ 0xc4e1, 0x02 },
> +	{ 0xc4e2, 0x01 },
> +	{ 0xc4e4, 0x10 },
> +	{ 0xc4e5, 0x20 },
> +	{ 0xc4e6, 0x30 },
> +	{ 0xc4e7, 0x40 },
> +	{ 0xc4e8, 0x50 },
> +	{ 0xc4e9, 0x60 },
> +	{ 0xc4ea, 0x70 },
> +	{ 0xc4eb, 0x80 },
> +	{ 0xc4ec, 0x90 },
> +	{ 0xc4ed, 0xa0 },
> +	{ 0xc4ee, 0xb0 },
> +	{ 0xc4ef, 0xc0 },
> +	{ 0xc4f0, 0xd0 },
> +	{ 0xc4f1, 0xe0 },
> +	{ 0xc4f2, 0xf0 },
> +	{ 0xc4f3, 0x80 },
> +	{ 0xc4f4, 0x00 },
> +	{ 0xc4f5, 0x20 },
> +	{ 0xc4f6, 0x02 },
> +	{ 0xc4f7, 0x00 },
> +	{ 0xc4f8, 0x00 },
> +	{ 0xc4f9, 0x00 },
> +	{ 0xc4fa, 0x00 },
> +	{ 0xc4fb, 0x01 },
> +	{ 0xc4fc, 0x01 },
> +	{ 0xc4fd, 0x00 },
> +	{ 0xc4fe, 0x04 },
> +	{ 0xc4ff, 0x02 },
> +	{ 0xc500, 0x48 },
> +	{ 0xc501, 0x74 },
> +	{ 0xc502, 0x58 },
> +	{ 0xc503, 0x80 },
> +	{ 0xc504, 0x05 },
> +	{ 0xc505, 0x80 },
> +	{ 0xc506, 0x03 },
> +	{ 0xc507, 0x80 },
> +	{ 0xc508, 0x01 },
> +	{ 0xc509, 0xc0 },
> +	{ 0xc50a, 0x01 },
> +	{ 0xc50b, 0xa0 },
> +	{ 0xc50c, 0x01 },
> +	{ 0xc50d, 0x2c },
> +	{ 0xc50e, 0x01 },
> +	{ 0xc50f, 0x0a },
> +	{ 0xc510, 0x00 },
> +	{ 0xc511, 0x00 },
> +	{ 0xc512, 0xe5 },
> +	{ 0xc513, 0x14 },
> +	{ 0xc514, 0x04 },
> +	{ 0xc515, 0x00 },
> +	{ 0xc518, OV10635_VTS >> 8},
> +	{ 0xc519, OV10635_VTS & 0xff},
> +	{ 0xc51a, OV10635_HTS >> 8},
> +	{ 0xc51b, OV10635_HTS & 0xff},
> +	{ 0xc2e0, 0x00 },
> +	{ 0xc2e1, 0x51 },
> +	{ 0xc2e2, 0x00 },
> +	{ 0xc2e3, 0xd6 },
> +	{ 0xc2e4, 0x01 },
> +	{ 0xc2e5, 0x5e },
> +	{ 0xc2e9, 0x01 },
> +	{ 0xc2ea, 0x7a },
> +	{ 0xc2eb, 0x90 },
> +	{ 0xc2ed, 0x00 },
> +	{ 0xc2ee, 0x7a },
> +	{ 0xc2ef, 0x64 },
> +	{ 0xc308, 0x00 },
> +	{ 0xc309, 0x00 },
> +	{ 0xc30a, 0x00 },
> +	{ 0xc30c, 0x00 },
> +	{ 0xc30d, 0x01 },
> +	{ 0xc30e, 0x00 },
> +	{ 0xc30f, 0x00 },
> +	{ 0xc310, 0x01 },
> +	{ 0xc311, 0x60 },
> +	{ 0xc312, 0xff },
> +	{ 0xc313, 0x08 },
> +	{ 0xc314, 0x01 },
> +	{ 0xc315, 0x00 },
> +	{ 0xc316, 0xff },
> +	{ 0xc317, 0x0b },
> +	{ 0xc318, 0x00 },
> +	{ 0xc319, 0x0c },
> +	{ 0xc31a, 0x00 },
> +	{ 0xc31b, 0xe0 },
> +	{ 0xc31c, 0x00 },
> +	{ 0xc31d, 0x14 },
> +	{ 0xc31e, 0x00 },
> +	{ 0xc31f, 0xc5 },
> +	{ 0xc320, 0xff },
> +	{ 0xc321, 0x4b },
> +	{ 0xc322, 0xff },
> +	{ 0xc323, 0xf0 },
> +	{ 0xc324, 0xff },
> +	{ 0xc325, 0xe8 },
> +	{ 0xc326, 0x00 },
> +	{ 0xc327, 0x46 },
> +	{ 0xc328, 0xff },
> +	{ 0xc329, 0xd2 },
> +	{ 0xc32a, 0xff },
> +	{ 0xc32b, 0xe4 },
> +	{ 0xc32c, 0xff },
> +	{ 0xc32d, 0xbb },
> +	{ 0xc32e, 0x00 },
> +	{ 0xc32f, 0x61 },
> +	{ 0xc330, 0xff },
> +	{ 0xc331, 0xf9 },
> +	{ 0xc332, 0x00 },
> +	{ 0xc333, 0xd9 },
> +	{ 0xc334, 0x00 },
> +	{ 0xc335, 0x2e },
> +	{ 0xc336, 0x00 },
> +	{ 0xc337, 0xb1 },
> +	{ 0xc338, 0xff },
> +	{ 0xc339, 0x64 },
> +	{ 0xc33a, 0xff },
> +	{ 0xc33b, 0xeb },
> +	{ 0xc33c, 0xff },
> +	{ 0xc33d, 0xe8 },
> +	{ 0xc33e, 0x00 },
> +	{ 0xc33f, 0x48 },
> +	{ 0xc340, 0xff },
> +	{ 0xc341, 0xd0 },
> +	{ 0xc342, 0xff },
> +	{ 0xc343, 0xed },
> +	{ 0xc344, 0xff },
> +	{ 0xc345, 0xad },
> +	{ 0xc346, 0x00 },
> +	{ 0xc347, 0x66 },
> +	{ 0xc348, 0x01 },
> +	{ 0xc349, 0x00 },
> +	{ 0x6700, 0x04 },
> +	{ 0x6701, 0x7b },
> +	{ 0x6702, 0xfd },
> +	{ 0x6703, 0xf9 },
> +	{ 0x6704, 0x3d },
> +	{ 0x6705, 0x71 },
> +	{ 0x6706, 0x78 },
> +	{ 0x6708, 0x05 },
> +	{ 0x6f06, 0x6f },
> +	{ 0x6f07, 0x00 },
> +	{ 0x6f0a, 0x6f },
> +	{ 0x6f0b, 0x00 },
> +	{ 0x6f00, 0x03 },
> +	{ 0xc34c, 0x01 },
> +	{ 0xc34d, 0x00 },
> +	{ 0xc34e, 0x46 },
> +	{ 0xc34f, 0x55 },
> +	{ 0xc350, 0x00 },
> +	{ 0xc351, 0x40 },
> +	{ 0xc352, 0x00 },
> +	{ 0xc353, 0xff },
> +	{ 0xc354, 0x04 },
> +	{ 0xc355, 0x08 },
> +	{ 0xc356, 0x01 },
> +	{ 0xc357, 0xef },
> +	{ 0xc358, 0x30 },
> +	{ 0xc359, 0x01 },
> +	{ 0xc35a, 0x64 },
> +	{ 0xc35b, 0x46 },
> +	{ 0xc35c, 0x00 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0x3042, 0xf0 },
> +	{ 0xc261, 0x01 },
> +	{ 0x301b, 0xf0 },
> +	{ 0x301c, 0xf0 },
> +	{ 0x301a, 0xf0 },
> +	{ 0x6f00, 0xc3 },
> +	{ 0xc46a, 0x30 },
> +	{ 0xc46d, 0x20 },
> +	{ 0xc464, 0x84 },
> +	{ 0xc465, 0x00 },
> +	{ 0x6f00, 0x03 },
> +	{ 0x6f00, 0x43 },
> +	{ 0x381c, 0x00 },
> +	{ 0x381d, 0x40 },
> +	{ 0xc454, 0x01 },
> +	{ 0x6f00, 0xc3 },
> +	{ 0xc454, 0x00 },
> +	{ 0xc4b1, 0x02 },
> +	{ 0xc4b2, 0x01 },
> +	{ 0xc4b3, 0x03 },
> +	{ 0x6f00, 0x03 },
> +	{ 0x6f00, 0x43 },
> +	/* enable FSIN (FRAMESYNC input) functionality */
> +	{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
> +	{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
> +	{ 0x3834, OV10635_VTS >> 8 },
> +	{ 0x3835, OV10635_VTS & 0xff },
> +	{ 0x302e, 0x01 },
> +};
> +
> +#endif /* __RDACM20_OV10635_H__ */
> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> new file mode 100644
> index 000000000000..d96b2eb5ab1b
> --- /dev/null
> +++ b/drivers/media/i2c/rdacm20.c
> @@ -0,0 +1,635 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * IMI RDACM20 GMSL Camera Driver
> + *
> + * Copyright (C) 2017-2018 Jacopo Mondi
> + * Copyright (C) 2017-2018 Kieran Bingham
> + * Copyright (C) 2017-2018 Laurent Pinchart
> + * Copyright (C) 2017-2018 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +/*
> + * The camera is mode of an Omnivision OV10635 sensor connected to a Maxim
> + * MAX9271 GMSL serializer.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/fwnode.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "rdacm20-ov10635.h"
> +
> +#define RDACM20_SENSOR_HARD_RESET
> +
> +#define MAX9271_I2C_ADDRESS		0x40
> +
> +/* Register 0x04 */
> +#define MAX9271_SEREN			BIT(7)
> +#define MAX9271_CLINKEN			BIT(6)
> +#define MAX9271_PRBSEN			BIT(5)
> +#define MAX9271_SLEEP			BIT(4)
> +#define MAX9271_INTTYPE_I2C		(0 << 2)
> +#define MAX9271_INTTYPE_UART		(1 << 2)
> +#define MAX9271_INTTYPE_NONE		(2 << 2)
> +#define MAX9271_REVCCEN			BIT(1)
> +#define MAX9271_FWDCCEN			BIT(0)
> +/* Register 0x07 */
> +#define MAX9271_DBL			BIT(7)
> +#define MAX9271_DRS			BIT(6)
> +#define MAX9271_BWS			BIT(5)
> +#define MAX9271_ES			BIT(4)
> +#define MAX9271_HVEN			BIT(2)
> +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> +/* Register 0x08 */
> +#define MAX9271_INVVS			BIT(7)
> +#define MAX9271_INVHS			BIT(6)
> +#define MAX9271_REV_LOGAIN		BIT(3)
> +#define MAX9271_REV_HIVTH		BIT(0)
> +/* Register 0x09 */
> +#define MAX9271_ID			0x09
> +/* Register 0x0d */
> +#define MAX9271_I2CLOCACK		BIT(7)
> +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> +/* Register 0x0f */
> +#define MAX9271_GPIO5OUT		BIT(5)
> +#define MAX9271_GPIO4OUT		BIT(4)
> +#define MAX9271_GPIO3OUT		BIT(3)
> +#define MAX9271_GPIO2OUT		BIT(2)
> +#define MAX9271_GPIO1OUT		BIT(1)
> +#define MAX9271_SETGPO			BIT(0)
> +/* Register 0x15 */
> +#define MAX9271_PCLKDET			BIT(0)
> +
> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
> +
> +#define OV10635_I2C_ADDRESS		0x30
> +
> +#define OV10635_SOFTWARE_RESET		0x0103
> +#define OV10635_PID			0x300a
> +#define OV10635_VER			0x300b
> +#define OV10635_SC_CMMN_SCCB_ID		0x300c
> +#define OV10635_SC_CMMN_SCCB_ID_SELECT	BIT(0)
> +#define OV10635_VERSION			0xa635
> +
> +#define OV10635_WIDTH			1280
> +#define OV10635_HEIGHT			800
> +#define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
> +
> +struct rdacm20_device {
> +	struct i2c_client		*client;
> +	struct i2c_client		*sensor;
> +	struct v4l2_subdev		sd;
> +	struct media_pad		pad;
> +	struct v4l2_ctrl_handler	ctrls;
> +};
> +
> +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct rdacm20_device, sd);
> +}
> +
> +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
> +{
> +	return sd_to_rdacm20(i2c_get_clientdata(client));
> +}
> +
> +static int max9271_read(struct rdacm20_device *dev, u8 reg)
> +{
> +	int ret;
> +
> +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> +
> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> +	if (ret < 0)
> +		dev_dbg(&dev->client->dev,
> +			"%s: register 0x%02x read failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +static int max9271_write(struct rdacm20_device *dev, u8 reg, u8 val)
> +{
> +	int ret;
> +
> +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> +
> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> +	if (ret < 0)
> +		dev_err(&dev->client->dev,
> +			"%s: register 0x%02x write failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
> +{
> +	u8 buf[2] = { reg >> 8, reg & 0xff };
> +	int ret;
> +
> +	ret = i2c_master_send(dev->sensor, buf, 2);
> +	if (ret == 2)
> +		ret = i2c_master_recv(dev->sensor, buf, 2);
> +
> +	if (ret < 0) {
> +		dev_dbg(&dev->client->dev,
> +			"%s: register 0x%04x read failed (%d)\n",
> +			__func__, reg, ret);
> +		return ret;
> +	}
> +
> +	return (buf[0] << 8) | buf[1];
> +}
> +
> +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> +{
> +	u8 buf[3] = { reg >> 8, reg & 0xff, val };
> +	int ret;
> +
> +	dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
> +
> +	ret = i2c_master_send(dev->sensor, buf, 3);
> +	return ret < 0 ? ret : 0;
> +}
> +
> +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> +{
> +	int ret;
> +
> +	ret = __ov10635_write(dev, reg, val);
> +	if (ret < 0)
> +		dev_err(&dev->client->dev,
> +			"%s: register 0x%04x write failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +static int ov10635_set_regs(struct rdacm20_device *dev,
> +			    const struct ov10635_reg *regs,
> +			    unsigned int nr_regs)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < nr_regs; i++) {
> +		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
> +		if (ret) {
> +			dev_err(&dev->client->dev,
> +				"%s: register %u (0x%04x) write failed (%d)\n",
> +				__func__, i, regs[i].reg, ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * rdacm20_pclk_detect() - Detect valid pixel clock from image sensor
> + *
> + * Wait up to 10ms for a valid pixel clock.
> + *
> + * Returns 0 for success, < 0 for pixel clock not properly detected
> + */
> +static int rdacm20_pclk_detect(struct rdacm20_device *dev)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < 100; i++) {
> +		ret = max9271_read(dev, 0x15);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret & MAX9271_PCLKDET)
> +			return 0;
> +
> +		usleep_range(50, 100);
> +	}
> +
> +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> +	return -EIO;
> +}
> +
> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
> +	int ret;
> +
> +	if (enable) {
> +		ret = rdacm20_pclk_detect(dev);
> +		if (ret)
> +			return ret;
> +
> +		/* Enable the serial link. */
> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
> +			      MAX9271_FWDCCEN);
> +	} else {
> +		/* Disable the serial link. */
> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> +			      MAX9271_FWDCCEN);
> +	}
> +
> +	return 0;
> +}
> +
> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
> +				 struct v4l2_mbus_config *cfg)
> +{
> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
> +	cfg->type = V4L2_MBUS_CSI2_DPHY;

Hmm. What are you using g_mbus_config() for?

> +
> +	return 0;
> +}
> +
> +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->pad || code->index > 0)
> +		return -EINVAL;
> +
> +	code->code = OV10635_FORMAT;
> +
> +	return 0;
> +}
> +
> +static int rdacm20_get_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *mf = &format->format;
> +
> +	if (format->pad)
> +		return -EINVAL;
> +
> +	mf->width		= OV10635_WIDTH;
> +	mf->height		= OV10635_HEIGHT;
> +	mf->code		= OV10635_FORMAT;
> +	mf->colorspace		= V4L2_COLORSPACE_RAW;
> +	mf->field		= V4L2_FIELD_NONE;
> +	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;
> +	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;
> +	mf->xfer_func		= V4L2_XFER_FUNC_NONE;
> +
> +	return 0;
> +}
> +
> +static struct v4l2_subdev_video_ops rdacm20_video_ops = {
> +	.s_stream	= rdacm20_s_stream,
> +	.g_mbus_config	= rdacm20_g_mbus_config,
> +};
> +
> +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
> +	.enum_mbus_code = rdacm20_enum_mbus_code,
> +	.get_fmt	= rdacm20_get_fmt,
> +	.set_fmt	= rdacm20_get_fmt,
> +};
> +
> +static struct v4l2_subdev_ops rdacm20_subdev_ops = {
> +	.video		= &rdacm20_video_ops,
> +	.pad		= &rdacm20_subdev_pad_ops,
> +};
> +
> +static int max9271_configure_i2c(struct rdacm20_device *dev)
> +{
> +	/*
> +	 * Configure the I2C bus:
> +	 *
> +	 * - Enable high thresholds on the reverse channel
> +	 * - Disable artificial ACK and set I2C speed
> +	 */
> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
> +	usleep_range(5000, 8000);
> +
> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
> +	usleep_range(5000, 8000);
> +
> +	return 0;
> +}
> +
> +static int max9271_configure_gmsl_link(struct rdacm20_device *dev)
> +{
> +	/*
> +	 * Disable the serial link and enable the configuration link to allow
> +	 * the control channel to operate in a low-speed mode in the absence of
> +	 * the serial link clock.
> +	 */
> +	max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> +		      MAX9271_FWDCCEN);
> +
> +	/*
> +	 * The serializer temporarily disables the reverse control channel for
> +	 * 350µs after starting/stopping the forward serial link, but the
> +	 * deserializer synchronization time isn't clearly documented.
> +	 *
> +	 * According to the serializer datasheet we should wait 3ms, while
> +	 * according to the deserializer datasheet we should wait 5ms.
> +	 *
> +	 * Short delays here appear to show bit-errors in the writes following.
> +	 * Therefore a conservative delay seems best here.
> +	 */
> +	usleep_range(5000, 8000);
> +
> +	/*
> +	 * Configure the GMSL link:
> +	 *
> +	 * - Double input mode, high data rate, 24-bit mode
> +	 * - Latch input data on PCLKIN rising edge
> +	 * - Enable HS/VS encoding
> +	 * - 1-bit parity error detection
> +	 */
> +	max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> +		      MAX9271_EDC_1BIT_PARITY);
> +	usleep_range(5000, 8000);
> +
> +	return 0;
> +}
> +
> +static int max9271_verify_id(struct rdacm20_device *dev)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x1e);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	if (ret != MAX9271_ID) {
> +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
> +{
> +	int ret;
> +
> +	/* Change the MAX9271 I2C address. */
> +	ret = max9271_write(dev, 0x00, addr << 1);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev,
> +			"MAX9271 I2C address change failed (%d)\n", ret);
> +		return ret;
> +	}
> +	dev->client->addr = addr;
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int rdacm20_initialize(struct rdacm20_device *dev)
> +{
> +	u32 addrs[2];
> +	int ret;
> +
> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
> +					 addrs, ARRAY_SIZE(addrs));
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * FIXME: The MAX9271 boots at a default address that we will change to
> +	 * the address specified in DT. Set the client address back to the
> +	 * default for initial communication.
> +	 */
> +	dev->client->addr = MAX9271_I2C_ADDRESS;
> +
> +	/* Verify communication with the MAX9271. */
> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
> +
> +	/*
> +	 *  Ensure that we have a good link configuration before attempting to
> +	 *  identify the device.
> +	 */
> +	max9271_configure_i2c(dev);
> +	max9271_configure_gmsl_link(dev);
> +
> +	ret = max9271_verify_id(dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = max9271_configure_address(dev, addrs[0]);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Reset and verify communication with the OV10635. */
> +#ifdef RDACM20_SENSOR_HARD_RESET
> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
> +	mdelay(10);
> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
> +	mdelay(10);

Do you need a busy loop? Could you use msleep()?

> +#else
> +	/* Perform a software reset. */
> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
> +		return -ENXIO;
> +	}
> +
> +	udelay(100);
> +#endif
> +
> +	ret = ov10635_read16(dev, OV10635_PID);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	if (ret != OV10635_VERSION) {
> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
> +
> +	/* Change the sensor I2C address. */
> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev,
> +			"OV10635 I2C address change failed (%d)\n", ret);
> +		return ret;
> +	}
> +	dev->sensor->addr = addrs[1];
> +	usleep_range(3500, 5000);
> +
> +	/* Program the 0V10635 initial configuration. */
> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
> +			       ARRAY_SIZE(ov10635_regs_wizard));

return ov...(); ?

> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int rdacm20_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *did)
> +{
> +	struct rdacm20_device *dev;
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);

You could use devm_kzalloc().

> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->client = client;
> +
> +	/* Create the dummy I2C client for the sensor. */
> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
> +	if (!dev->sensor) {
> +		ret = -ENXIO;
> +		goto error;
> +	}
> +
> +	/* Initialize the hardware. */
> +	ret = rdacm20_initialize(dev);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialize and register the subdevice. */
> +	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
> +	dev->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;

|=, as in the other patch.

> +
> +	v4l2_ctrl_handler_init(&dev->ctrls, 1);
> +	/*
> +	 * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
> +	 * hardcoded frequency in the BSP CSI-2 receiver driver.
> +	 */
> +	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
> +			  50000000, 1, 50000000);
> +	dev->sd.ctrl_handler = &dev->ctrls;
> +
> +	ret = dev->ctrls.error;
> +	if (ret)
> +		goto error;
> +
> +	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
> +	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
> +	if (ret < 0)
> +		goto error;
> +
> +	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> +	if (!ep) {
> +		dev_err(&client->dev,
> +			"Unable to get endpoint in node %pOF\n",
> +			client->dev.of_node);
> +		ret = -ENOENT;
> +		goto error;
> +	}
> +	dev->sd.fwnode = ep;
> +
> +	ret = v4l2_async_register_subdev(&dev->sd);
> +	if (ret)
> +		goto error_put_node;
> +
> +	return 0;
> +
> +error_put_node:
> +	fwnode_handle_put(ep);
> +error:

You're missing v4l2_ctrl_handler_free() here.

> +	media_entity_cleanup(&dev->sd.entity);
> +	if (dev->sensor)
> +		i2c_unregister_device(dev->sensor);
> +	kfree(dev);
> +
> +	dev_err(&client->dev, "probe failed\n");
> +
> +	return ret;
> +}
> +
> +static int rdacm20_remove(struct i2c_client *client)
> +{
> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
> +
> +	fwnode_handle_put(dev->sd.fwnode);
> +	v4l2_async_unregister_subdev(&dev->sd);

As well as here.

> +	media_entity_cleanup(&dev->sd.entity);
> +	i2c_unregister_device(dev->sensor);
> +	kfree(dev);
> +
> +	return 0;
> +}
> +
> +static void rdacm20_shutdown(struct i2c_client *client)
> +{
> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
> +
> +	/* make sure stream off during shutdown (reset/reboot) */
> +	rdacm20_s_stream(&dev->sd, 0);
> +}
> +
> +static const struct i2c_device_id rdacm20_id[] = {
> +	{ "rdacm20", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
> +
> +static const struct of_device_id rdacm20_of_ids[] = {
> +	{ .compatible = "imi,rdacm20", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
> +
> +static struct i2c_driver rdacm20_i2c_driver = {
> +	.driver	= {
> +		.name	= "rdacm20",
> +		.of_match_table = rdacm20_of_ids,
> +	},
> +	.probe		= rdacm20_probe,

Could you use probe_new, so you could remove the i2c ID table? Or do you
need that for something?

> +	.remove		= rdacm20_remove,
> +	.shutdown	= rdacm20_shutdown,
> +	.id_table	= rdacm20_id,
> +};
> +
> +module_i2c_driver(rdacm20_i2c_driver);
> +
> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_LICENSE("GPL");
Kieran Bingham Nov. 28, 2018, 12:51 p.m. UTC | #2
Hi Sakari,

Thank you for your review,

On 20/11/2018 08:34, Sakari Ailus wrote:
> Hi Kieran,
> 
> On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> The RDACM20 is a GMSL camera supporting 1280x800 resolution images
>> developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
>> GMSL serializer.
>>
>> The GMSL link carries power, control (I2C) and video data over a
>> single coax cable.
>>
>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> ---
>> v2:
>>  - Fix MAINTAINERS entry
>>
>> v3:
>>  - Use new V4L2_MBUS_CSI2_DPHY bus type
>>  - Remove 'always zero' error print
>>  - Fix module description
>> ---
>>  MAINTAINERS                         |  10 +
>>  drivers/media/i2c/Kconfig           |  11 +
>>  drivers/media/i2c/Makefile          |   1 +
>>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
>>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
>>  5 files changed, 1610 insertions(+)
>>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
>>  create mode 100644 drivers/media/i2c/rdacm20.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 745f0fd1fff1..26ef20087a43 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12230,6 +12230,16 @@ S:	Supported
>>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>>  F:	tools/testing/selftests/rcutorture
>>  
>> +RDACM20 Camera Sensor
>> +M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
>> +M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>> +M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>> +M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> 
> I'm happy to see this will be well maintained. :-)

Well it means /someone/ should always be able to pick it up :D

I didn't know who to put here - so I put all of the current blamees ;)
We've all put a lot of time and work in to the GMSL bring up and
refactoring.

If you think it's overkill, I can reduce the names. Same on max9286.


>> +L:	linux-media@vger.kernel.org
>> +S:	Maintained
>> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
>> +F:	drivers/media/i2c/rdacm20*
>> +
>>  RDC R-321X SoC
>>  M:	Florian Fainelli <florian@openwrt.org>
>>  S:	Maintained
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index eadc00bdd3bf..5eded5e337ec 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
>>  	  This is a V4L2 sensor driver for Samsung S5C73M3
>>  	  8 Mpixel camera.
>>  
>> +config VIDEO_RDACM20
>> +	tristate "IMI RDACM20 camera support"
>> +	depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
>> +	select V4L2_FWNODE
>> +	help
>> +	  This driver supports the IMI RDACM20 GMSL camera, used in
>> +	  ADAS systems.
>> +
>> +	  This camera should be used in conjunction with a GMSL
>> +	  deserialiser such as the MAX9286.
>> +
>>  comment "Flash devices"
>>  
>>  config VIDEO_ADP1653
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 4de7fe62b179..121d28283d45 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>>  obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
>>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
>> +obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>>  
>>  obj-$(CONFIG_SDR_MAX2175) += max2175.o
>> diff --git a/drivers/media/i2c/rdacm20-ov10635.h b/drivers/media/i2c/rdacm20-ov10635.h
>> new file mode 100644
>> index 000000000000..3c53a3262ee2
>> --- /dev/null
>> +++ b/drivers/media/i2c/rdacm20-ov10635.h
>> @@ -0,0 +1,953 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * IMI RDACM20 camera OV10635 sensor register initialization values
>> + *
>> + * Copyright (C) 2017-2018 Jacopo Mondi
>> + * Copyright (C) 2017-2018 Kieran Bingham
>> + * Copyright (C) 2017-2018 Laurent Pinchart
>> + * Copyright (C) 2017-2018 Niklas Söderlund
>> + * Copyright (C) 2016 Renesas Electronics Corporation
>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>> + *
>> + */
>> +
>> +/*
>> + * Generated by the OmniVision ov10635 sensor camera wizard for
>> + * 1280x800@30/UYVY/BT601/8bit.
>> + */
>> +
>> +#ifndef __RDACM20_OV10635_H__
>> +#define __RDACM20_OV10635_H__
>> +
>> +#define OV10635_SENSOR_WIDTH		1312
>> +#define OV10635_SENSOR_HEIGHT		814
>> +
>> +#define OV10635_MAX_WIDTH		1280
>> +#define OV10635_MAX_HEIGHT		800
>> +
>> +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
>> +#define OV10635_HTS			1572
>> +/* FPS = 29,9998 */
>> +#define OV10635_VTS			933
> 
> A part of this driver looks like a driver for an OV camera sensor. Would
> there be something that prevents separating the camera sensor driver from
> this one?

I don't think there's anything preventing it - except (a fair bit of)
development time.

We also have the RDACM21 to support, which uses the max9271 and an
OV10640. At that time - this will absolutely have to be split. We
shouldn't replicate the max9271 code.

I mentioned briefly in the cover letter:

> Further anticipated work in this area includes supporting the RDACM21
> camera, at which point the RDACM20 will be adapted to separate out the
> MAX9271 and the OV10635 sensor components.

But to get more dedicated time to work this - we need to show some
progress on GMSL up-streaming, so the max9286 and bindings take priority
for now.

A little bit catch 22 ... :D

I'm sure there will be overlap between GMSL and FPD-Link with the RDACM
range [0] of cameras too, which also provide TI-FPD Link serialisers.

I currently envisage that we would have an RDACM20 'driver' which would
know that it has a max9271 serialiser and an OV10635 sensor, and would
handle the links of any subdevices internally.

As the RDACM20 is an object itself, I think this makes sense ... unless
anyone suggests that each part should be broken down into the DT
directly ? (I think that would possibly be a bit too much)


[0]
https://www.global-imi.com/sites/default/files/Generic-Minicube-Catalogue-1020151.pdf


>> +
>> +struct ov10635_reg {
>> +	u16	reg;
>> +	u8	val;
>> +};
>> +
>> +static const struct ov10635_reg ov10635_regs_wizard[] = {
>> +	{ 0x301b, 0xff },
>> +	{ 0x301c, 0xff },
>> +	{ 0x301a, 0xff },
>> +	{ 0x3011, 0x42 },
>> +	{ 0x6900, 0x0c },
>> +	{ 0x6901, 0x19 },
>> +	{ 0x3503, 0x10 },
>> +	{ 0x3025, 0x03 },
>> +	{ 0x3003, 0x16 },
>> +	{ 0x3004, 0x30 },
>> +	{ 0x3005, 0x40 },
>> +	{ 0x3006, 0x91 },
>> +	{ 0x3600, 0x74 },
>> +	{ 0x3601, 0x2b },
>> +	{ 0x3612, 0x00 },
>> +	{ 0x3611, 0x67 },
>> +	{ 0x3633, 0xca },
>> +	{ 0x3602, 0xaf },
>> +	{ 0x3603, 0x04 },
>> +	{ 0x3630, 0x28 },
>> +	{ 0x3631, 0x16 },
>> +	{ 0x3714, 0x10 },
>> +	{ 0x371d, 0x01 },
>> +	{ 0x4300, 0x3a },
>> +	{ 0x3007, 0x01 },
>> +	{ 0x3024, 0x03 },
>> +	{ 0x3020, 0x0a },
>> +	{ 0x3702, 0x0d },
>> +	{ 0x3703, 0x20 },
>> +	{ 0x3704, 0x15 },
>> +	{ 0x3709, 0xa8 },
>> +	{ 0x370c, 0xc7 },
>> +	{ 0x370d, 0x80 },
>> +	{ 0x3712, 0x00 },
>> +	{ 0x3713, 0x20 },
>> +	{ 0x3715, 0x04 },
>> +	{ 0x381d, 0x40 },
>> +	{ 0x381c, 0x00 },
>> +	{ 0x3822, 0x50 },
>> +	{ 0x3824, 0x10 },
>> +	{ 0x3815, 0x8c },
>> +	{ 0x3804, 0x05 },
>> +	{ 0x3805, 0x1f },
>> +	{ 0x3800, 0x00 },
>> +	{ 0x3801, 0x00 },
>> +	{ 0x3806, 0x03 },
>> +	{ 0x3807, 0x28 },
>> +	{ 0x3802, 0x00 },
>> +	{ 0x3803, 0x07 },
>> +	{ 0x3808, 0x05 },
>> +	{ 0x3809, 0x00 },
>> +	{ 0x380a, 0x03 },
>> +	{ 0x380b, 0x20 },
>> +	{ 0x380c, OV10635_HTS >> 8 },
>> +	{ 0x380d, OV10635_HTS & 0xff },
>> +	{ 0x380e, OV10635_VTS >> 8 },
>> +	{ 0x380f, OV10635_VTS & 0xff },
>> +	{ 0x3813, 0x02 },
>> +	{ 0x3811, 0x08 },
>> +	{ 0x381f, 0x0c },
>> +	{ 0x3819, 0x04 },
>> +	{ 0x3804, 0x01 },
>> +	{ 0x3805, 0x00 },
>> +	{ 0x3828, 0x03 },
>> +	{ 0x3829, 0x10 },
>> +	{ 0x382a, 0x10 },
>> +	{ 0x3621, 0x63 },
>> +	{ 0x5005, 0x08 },
>> +	{ 0x56d5, 0x00 },
>> +	{ 0x56d6, 0x80 },
>> +	{ 0x56d7, 0x00 },
>> +	{ 0x56d8, 0x00 },
>> +	{ 0x56d9, 0x00 },
>> +	{ 0x56da, 0x80 },
>> +	{ 0x56db, 0x00 },
>> +	{ 0x56dc, 0x00 },
>> +	{ 0x56e8, 0x00 },
>> +	{ 0x56e9, 0x7f },
>> +	{ 0x56ea, 0x00 },
>> +	{ 0x56eb, 0x7f },
>> +	{ 0x5100, 0x00 },
>> +	{ 0x5101, 0x80 },
>> +	{ 0x5102, 0x00 },
>> +	{ 0x5103, 0x80 },
>> +	{ 0x5104, 0x00 },
>> +	{ 0x5105, 0x80 },
>> +	{ 0x5106, 0x00 },
>> +	{ 0x5107, 0x80 },
>> +	{ 0x5108, 0x00 },
>> +	{ 0x5109, 0x00 },
>> +	{ 0x510a, 0x00 },
>> +	{ 0x510b, 0x00 },
>> +	{ 0x510c, 0x00 },
>> +	{ 0x510d, 0x00 },
>> +	{ 0x510e, 0x00 },
>> +	{ 0x510f, 0x00 },
>> +	{ 0x5110, 0x00 },
>> +	{ 0x5111, 0x80 },
>> +	{ 0x5112, 0x00 },
>> +	{ 0x5113, 0x80 },
>> +	{ 0x5114, 0x00 },
>> +	{ 0x5115, 0x80 },
>> +	{ 0x5116, 0x00 },
>> +	{ 0x5117, 0x80 },
>> +	{ 0x5118, 0x00 },
>> +	{ 0x5119, 0x00 },
>> +	{ 0x511a, 0x00 },
>> +	{ 0x511b, 0x00 },
>> +	{ 0x511c, 0x00 },
>> +	{ 0x511d, 0x00 },
>> +	{ 0x511e, 0x00 },
>> +	{ 0x511f, 0x00 },
>> +	{ 0x56d0, 0x00 },
>> +	{ 0x5006, 0x04 },
>> +	{ 0x5608, 0x05 },
>> +	{ 0x52d7, 0x06 },
>> +	{ 0x528d, 0x08 },
>> +	{ 0x5293, 0x12 },
>> +	{ 0x52d3, 0x12 },
>> +	{ 0x5288, 0x06 },
>> +	{ 0x5289, 0x20 },
>> +	{ 0x52c8, 0x06 },
>> +	{ 0x52c9, 0x20 },
>> +	{ 0x52cd, 0x04 },
>> +	{ 0x5381, 0x00 },
>> +	{ 0x5382, 0xff },
>> +	{ 0x5589, 0x76 },
>> +	{ 0x558a, 0x47 },
>> +	{ 0x558b, 0xef },
>> +	{ 0x558c, 0xc9 },
>> +	{ 0x558d, 0x49 },
>> +	{ 0x558e, 0x30 },
>> +	{ 0x558f, 0x67 },
>> +	{ 0x5590, 0x3f },
>> +	{ 0x5591, 0xf0 },
>> +	{ 0x5592, 0x10 },
>> +	{ 0x55a2, 0x6d },
>> +	{ 0x55a3, 0x55 },
>> +	{ 0x55a4, 0xc3 },
>> +	{ 0x55a5, 0xb5 },
>> +	{ 0x55a6, 0x43 },
>> +	{ 0x55a7, 0x38 },
>> +	{ 0x55a8, 0x5f },
>> +	{ 0x55a9, 0x4b },
>> +	{ 0x55aa, 0xf0 },
>> +	{ 0x55ab, 0x10 },
>> +	{ 0x5581, 0x52 },
>> +	{ 0x5300, 0x01 },
>> +	{ 0x5301, 0x00 },
>> +	{ 0x5302, 0x00 },
>> +	{ 0x5303, 0x0e },
>> +	{ 0x5304, 0x00 },
>> +	{ 0x5305, 0x0e },
>> +	{ 0x5306, 0x00 },
>> +	{ 0x5307, 0x36 },
>> +	{ 0x5308, 0x00 },
>> +	{ 0x5309, 0xd9 },
>> +	{ 0x530a, 0x00 },
>> +	{ 0x530b, 0x0f },
>> +	{ 0x530c, 0x00 },
>> +	{ 0x530d, 0x2c },
>> +	{ 0x530e, 0x00 },
>> +	{ 0x530f, 0x59 },
>> +	{ 0x5310, 0x00 },
>> +	{ 0x5311, 0x7b },
>> +	{ 0x5312, 0x00 },
>> +	{ 0x5313, 0x22 },
>> +	{ 0x5314, 0x00 },
>> +	{ 0x5315, 0xd5 },
>> +	{ 0x5316, 0x00 },
>> +	{ 0x5317, 0x13 },
>> +	{ 0x5318, 0x00 },
>> +	{ 0x5319, 0x18 },
>> +	{ 0x531a, 0x00 },
>> +	{ 0x531b, 0x26 },
>> +	{ 0x531c, 0x00 },
>> +	{ 0x531d, 0xdc },
>> +	{ 0x531e, 0x00 },
>> +	{ 0x531f, 0x02 },
>> +	{ 0x5320, 0x00 },
>> +	{ 0x5321, 0x24 },
>> +	{ 0x5322, 0x00 },
>> +	{ 0x5323, 0x56 },
>> +	{ 0x5324, 0x00 },
>> +	{ 0x5325, 0x85 },
>> +	{ 0x5326, 0x00 },
>> +	{ 0x5327, 0x20 },
>> +	{ 0x5609, 0x01 },
>> +	{ 0x560a, 0x40 },
>> +	{ 0x560b, 0x01 },
>> +	{ 0x560c, 0x40 },
>> +	{ 0x560d, 0x00 },
>> +	{ 0x560e, 0xfa },
>> +	{ 0x560f, 0x00 },
>> +	{ 0x5610, 0xfa },
>> +	{ 0x5611, 0x02 },
>> +	{ 0x5612, 0x80 },
>> +	{ 0x5613, 0x02 },
>> +	{ 0x5614, 0x80 },
>> +	{ 0x5615, 0x01 },
>> +	{ 0x5616, 0x2c },
>> +	{ 0x5617, 0x01 },
>> +	{ 0x5618, 0x2c },
>> +	{ 0x563b, 0x01 },
>> +	{ 0x563c, 0x01 },
>> +	{ 0x563d, 0x01 },
>> +	{ 0x563e, 0x01 },
>> +	{ 0x563f, 0x03 },
>> +	{ 0x5640, 0x03 },
>> +	{ 0x5641, 0x03 },
>> +	{ 0x5642, 0x05 },
>> +	{ 0x5643, 0x09 },
>> +	{ 0x5644, 0x05 },
>> +	{ 0x5645, 0x05 },
>> +	{ 0x5646, 0x05 },
>> +	{ 0x5647, 0x05 },
>> +	{ 0x5651, 0x00 },
>> +	{ 0x5652, 0x80 },
>> +	{ 0x521a, 0x01 },
>> +	{ 0x521b, 0x03 },
>> +	{ 0x521c, 0x06 },
>> +	{ 0x521d, 0x0a },
>> +	{ 0x521e, 0x0e },
>> +	{ 0x521f, 0x12 },
>> +	{ 0x5220, 0x16 },
>> +	{ 0x5223, 0x02 },
>> +	{ 0x5225, 0x04 },
>> +	{ 0x5227, 0x08 },
>> +	{ 0x5229, 0x0c },
>> +	{ 0x522b, 0x12 },
>> +	{ 0x522d, 0x18 },
>> +	{ 0x522f, 0x1e },
>> +	{ 0x5241, 0x04 },
>> +	{ 0x5242, 0x01 },
>> +	{ 0x5243, 0x03 },
>> +	{ 0x5244, 0x06 },
>> +	{ 0x5245, 0x0a },
>> +	{ 0x5246, 0x0e },
>> +	{ 0x5247, 0x12 },
>> +	{ 0x5248, 0x16 },
>> +	{ 0x524a, 0x03 },
>> +	{ 0x524c, 0x04 },
>> +	{ 0x524e, 0x08 },
>> +	{ 0x5250, 0x0c },
>> +	{ 0x5252, 0x12 },
>> +	{ 0x5254, 0x18 },
>> +	{ 0x5256, 0x1e },
>> +	/* fifo_line_length = 2*hts */
>> +	{ 0x4606, (2 * OV10635_HTS) >> 8 },
>> +	{ 0x4607, (2 * OV10635_HTS) & 0xff },
>> +	/* fifo_hsync_start = 2*(hts - xres) */
>> +	{ 0x460a, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) >> 8 },
>> +	{ 0x460b, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) & 0xff },
>> +	{ 0x460c, 0x00 },
>> +	{ 0x4620, 0x0e },
>> +	/* BT601: 0x08 is also acceptable as HS/VS mode */
>> +	{ 0x4700, 0x04 },
>> +	{ 0x4701, 0x00 },
>> +	{ 0x4702, 0x01 },
>> +	{ 0x4004, 0x04 },
>> +	{ 0x4005, 0x18 },
>> +	{ 0x4001, 0x06 },
>> +	{ 0x4050, 0x22 },
>> +	{ 0x4051, 0x24 },
>> +	{ 0x4052, 0x02 },
>> +	{ 0x4057, 0x9c },
>> +	{ 0x405a, 0x00 },
>> +	{ 0x4202, 0x02 },
>> +	{ 0x3023, 0x10 },
>> +	{ 0x0100, 0x01 },
>> +	{ 0x0100, 0x01 },
>> +	{ 0x6f10, 0x07 },
>> +	{ 0x6f11, 0x82 },
>> +	{ 0x6f12, 0x04 },
>> +	{ 0x6f13, 0x00 },
>> +	{ 0xd000, 0x19 },
>> +	{ 0xd001, 0xa0 },
>> +	{ 0xd002, 0x00 },
>> +	{ 0xd003, 0x01 },
>> +	{ 0xd004, 0xa9 },
>> +	{ 0xd005, 0xad },
>> +	{ 0xd006, 0x10 },
>> +	{ 0xd007, 0x40 },
>> +	{ 0xd008, 0x44 },
>> +	{ 0xd009, 0x00 },
>> +	{ 0xd00a, 0x68 },
>> +	{ 0xd00b, 0x00 },
>> +	{ 0xd00c, 0x15 },
>> +	{ 0xd00d, 0x00 },
>> +	{ 0xd00e, 0x00 },
>> +	{ 0xd00f, 0x00 },
>> +	{ 0xd040, 0x9c },
>> +	{ 0xd041, 0x21 },
>> +	{ 0xd042, 0xff },
>> +	{ 0xd043, 0xf8 },
>> +	{ 0xd044, 0xd4 },
>> +	{ 0xd045, 0x01 },
>> +	{ 0xd046, 0x48 },
>> +	{ 0xd047, 0x00 },
>> +	{ 0xd048, 0xd4 },
>> +	{ 0xd049, 0x01 },
>> +	{ 0xd04a, 0x50 },
>> +	{ 0xd04b, 0x04 },
>> +	{ 0xd04c, 0x18 },
>> +	{ 0xd04d, 0x60 },
>> +	{ 0xd04e, 0x00 },
>> +	{ 0xd04f, 0x01 },
>> +	{ 0xd050, 0xa8 },
>> +	{ 0xd051, 0x63 },
>> +	{ 0xd052, 0x02 },
>> +	{ 0xd053, 0xa4 },
>> +	{ 0xd054, 0x85 },
>> +	{ 0xd055, 0x43 },
>> +	{ 0xd056, 0x00 },
>> +	{ 0xd057, 0x00 },
>> +	{ 0xd058, 0x18 },
>> +	{ 0xd059, 0x60 },
>> +	{ 0xd05a, 0x00 },
>> +	{ 0xd05b, 0x01 },
>> +	{ 0xd05c, 0xa8 },
>> +	{ 0xd05d, 0x63 },
>> +	{ 0xd05e, 0x03 },
>> +	{ 0xd05f, 0xf0 },
>> +	{ 0xd060, 0x98 },
>> +	{ 0xd061, 0xa3 },
>> +	{ 0xd062, 0x00 },
>> +	{ 0xd063, 0x00 },
>> +	{ 0xd064, 0x8c },
>> +	{ 0xd065, 0x6a },
>> +	{ 0xd066, 0x00 },
>> +	{ 0xd067, 0x6e },
>> +	{ 0xd068, 0xe5 },
>> +	{ 0xd069, 0x85 },
>> +	{ 0xd06a, 0x18 },
>> +	{ 0xd06b, 0x00 },
>> +	{ 0xd06c, 0x10 },
>> +	{ 0xd06d, 0x00 },
>> +	{ 0xd06e, 0x00 },
>> +	{ 0xd06f, 0x10 },
>> +	{ 0xd070, 0x9c },
>> +	{ 0xd071, 0x80 },
>> +	{ 0xd072, 0x00 },
>> +	{ 0xd073, 0x03 },
>> +	{ 0xd074, 0x18 },
>> +	{ 0xd075, 0x60 },
>> +	{ 0xd076, 0x00 },
>> +	{ 0xd077, 0x01 },
>> +	{ 0xd078, 0xa8 },
>> +	{ 0xd079, 0x63 },
>> +	{ 0xd07a, 0x07 },
>> +	{ 0xd07b, 0x80 },
>> +	{ 0xd07c, 0x07 },
>> +	{ 0xd07d, 0xff },
>> +	{ 0xd07e, 0xf9 },
>> +	{ 0xd07f, 0x03 },
>> +	{ 0xd080, 0x8c },
>> +	{ 0xd081, 0x63 },
>> +	{ 0xd082, 0x00 },
>> +	{ 0xd083, 0x00 },
>> +	{ 0xd084, 0xa5 },
>> +	{ 0xd085, 0x6b },
>> +	{ 0xd086, 0x00 },
>> +	{ 0xd087, 0xff },
>> +	{ 0xd088, 0x18 },
>> +	{ 0xd089, 0x80 },
>> +	{ 0xd08a, 0x00 },
>> +	{ 0xd08b, 0x01 },
>> +	{ 0xd08c, 0xa8 },
>> +	{ 0xd08d, 0x84 },
>> +	{ 0xd08e, 0x01 },
>> +	{ 0xd08f, 0x04 },
>> +	{ 0xd090, 0xe1 },
>> +	{ 0xd091, 0x6b },
>> +	{ 0xd092, 0x58 },
>> +	{ 0xd093, 0x00 },
>> +	{ 0xd094, 0x94 },
>> +	{ 0xd095, 0x6a },
>> +	{ 0xd096, 0x00 },
>> +	{ 0xd097, 0x70 },
>> +	{ 0xd098, 0xe1 },
>> +	{ 0xd099, 0x6b },
>> +	{ 0xd09a, 0x20 },
>> +	{ 0xd09b, 0x00 },
>> +	{ 0xd09c, 0x95 },
>> +	{ 0xd09d, 0x6b },
>> +	{ 0xd09e, 0x00 },
>> +	{ 0xd09f, 0x00 },
>> +	{ 0xd0a0, 0xe4 },
>> +	{ 0xd0a1, 0x8b },
>> +	{ 0xd0a2, 0x18 },
>> +	{ 0xd0a3, 0x00 },
>> +	{ 0xd0a4, 0x0c },
>> +	{ 0xd0a5, 0x00 },
>> +	{ 0xd0a6, 0x00 },
>> +	{ 0xd0a7, 0x23 },
>> +	{ 0xd0a8, 0x15 },
>> +	{ 0xd0a9, 0x00 },
>> +	{ 0xd0aa, 0x00 },
>> +	{ 0xd0ab, 0x00 },
>> +	{ 0xd0ac, 0x18 },
>> +	{ 0xd0ad, 0x60 },
>> +	{ 0xd0ae, 0x80 },
>> +	{ 0xd0af, 0x06 },
>> +	{ 0xd0b0, 0xa8 },
>> +	{ 0xd0b1, 0x83 },
>> +	{ 0xd0b2, 0x40 },
>> +	{ 0xd0b3, 0x08 },
>> +	{ 0xd0b4, 0xa8 },
>> +	{ 0xd0b5, 0xe3 },
>> +	{ 0xd0b6, 0x38 },
>> +	{ 0xd0b7, 0x2a },
>> +	{ 0xd0b8, 0xa8 },
>> +	{ 0xd0b9, 0xc3 },
>> +	{ 0xd0ba, 0x40 },
>> +	{ 0xd0bb, 0x09 },
>> +	{ 0xd0bc, 0xa8 },
>> +	{ 0xd0bd, 0xa3 },
>> +	{ 0xd0be, 0x38 },
>> +	{ 0xd0bf, 0x29 },
>> +	{ 0xd0c0, 0x8c },
>> +	{ 0xd0c1, 0x65 },
>> +	{ 0xd0c2, 0x00 },
>> +	{ 0xd0c3, 0x00 },
>> +	{ 0xd0c4, 0xd8 },
>> +	{ 0xd0c5, 0x04 },
>> +	{ 0xd0c6, 0x18 },
>> +	{ 0xd0c7, 0x00 },
>> +	{ 0xd0c8, 0x8c },
>> +	{ 0xd0c9, 0x67 },
>> +	{ 0xd0ca, 0x00 },
>> +	{ 0xd0cb, 0x00 },
>> +	{ 0xd0cc, 0xd8 },
>> +	{ 0xd0cd, 0x06 },
>> +	{ 0xd0ce, 0x18 },
>> +	{ 0xd0cf, 0x00 },
>> +	{ 0xd0d0, 0x18 },
>> +	{ 0xd0d1, 0x60 },
>> +	{ 0xd0d2, 0x80 },
>> +	{ 0xd0d3, 0x06 },
>> +	{ 0xd0d4, 0xa8 },
>> +	{ 0xd0d5, 0xe3 },
>> +	{ 0xd0d6, 0x67 },
>> +	{ 0xd0d7, 0x02 },
>> +	{ 0xd0d8, 0xa9 },
>> +	{ 0xd0d9, 0x03 },
>> +	{ 0xd0da, 0x67 },
>> +	{ 0xd0db, 0x03 },
>> +	{ 0xd0dc, 0xa8 },
>> +	{ 0xd0dd, 0xc3 },
>> +	{ 0xd0de, 0x3d },
>> +	{ 0xd0df, 0x05 },
>> +	{ 0xd0e0, 0x8c },
>> +	{ 0xd0e1, 0x66 },
>> +	{ 0xd0e2, 0x00 },
>> +	{ 0xd0e3, 0x00 },
>> +	{ 0xd0e4, 0xb8 },
>> +	{ 0xd0e5, 0x63 },
>> +	{ 0xd0e6, 0x00 },
>> +	{ 0xd0e7, 0x18 },
>> +	{ 0xd0e8, 0xb8 },
>> +	{ 0xd0e9, 0x63 },
>> +	{ 0xd0ea, 0x00 },
>> +	{ 0xd0eb, 0x98 },
>> +	{ 0xd0ec, 0xbc },
>> +	{ 0xd0ed, 0x03 },
>> +	{ 0xd0ee, 0x00 },
>> +	{ 0xd0ef, 0x00 },
>> +	{ 0xd0f0, 0x10 },
>> +	{ 0xd0f1, 0x00 },
>> +	{ 0xd0f2, 0x00 },
>> +	{ 0xd0f3, 0x16 },
>> +	{ 0xd0f4, 0xb8 },
>> +	{ 0xd0f5, 0x83 },
>> +	{ 0xd0f6, 0x00 },
>> +	{ 0xd0f7, 0x19 },
>> +	{ 0xd0f8, 0x8c },
>> +	{ 0xd0f9, 0x67 },
>> +	{ 0xd0fa, 0x00 },
>> +	{ 0xd0fb, 0x00 },
>> +	{ 0xd0fc, 0xb8 },
>> +	{ 0xd0fd, 0xa4 },
>> +	{ 0xd0fe, 0x00 },
>> +	{ 0xd0ff, 0x98 },
>> +	{ 0xd100, 0xb8 },
>> +	{ 0xd101, 0x83 },
>> +	{ 0xd102, 0x00 },
>> +	{ 0xd103, 0x08 },
>> +	{ 0xd104, 0x8c },
>> +	{ 0xd105, 0x68 },
>> +	{ 0xd106, 0x00 },
>> +	{ 0xd107, 0x00 },
>> +	{ 0xd108, 0xe0 },
>> +	{ 0xd109, 0x63 },
>> +	{ 0xd10a, 0x20 },
>> +	{ 0xd10b, 0x04 },
>> +	{ 0xd10c, 0xe0 },
>> +	{ 0xd10d, 0x65 },
>> +	{ 0xd10e, 0x18 },
>> +	{ 0xd10f, 0x00 },
>> +	{ 0xd110, 0xa4 },
>> +	{ 0xd111, 0x83 },
>> +	{ 0xd112, 0xff },
>> +	{ 0xd113, 0xff },
>> +	{ 0xd114, 0xb8 },
>> +	{ 0xd115, 0x64 },
>> +	{ 0xd116, 0x00 },
>> +	{ 0xd117, 0x48 },
>> +	{ 0xd118, 0xd8 },
>> +	{ 0xd119, 0x07 },
>> +	{ 0xd11a, 0x18 },
>> +	{ 0xd11b, 0x00 },
>> +	{ 0xd11c, 0xd8 },
>> +	{ 0xd11d, 0x08 },
>> +	{ 0xd11e, 0x20 },
>> +	{ 0xd11f, 0x00 },
>> +	{ 0xd120, 0x9c },
>> +	{ 0xd121, 0x60 },
>> +	{ 0xd122, 0x00 },
>> +	{ 0xd123, 0x00 },
>> +	{ 0xd124, 0xd8 },
>> +	{ 0xd125, 0x06 },
>> +	{ 0xd126, 0x18 },
>> +	{ 0xd127, 0x00 },
>> +	{ 0xd128, 0x00 },
>> +	{ 0xd129, 0x00 },
>> +	{ 0xd12a, 0x00 },
>> +	{ 0xd12b, 0x08 },
>> +	{ 0xd12c, 0x15 },
>> +	{ 0xd12d, 0x00 },
>> +	{ 0xd12e, 0x00 },
>> +	{ 0xd12f, 0x00 },
>> +	{ 0xd130, 0x8c },
>> +	{ 0xd131, 0x6a },
>> +	{ 0xd132, 0x00 },
>> +	{ 0xd133, 0x76 },
>> +	{ 0xd134, 0xbc },
>> +	{ 0xd135, 0x23 },
>> +	{ 0xd136, 0x00 },
>> +	{ 0xd137, 0x00 },
>> +	{ 0xd138, 0x13 },
>> +	{ 0xd139, 0xff },
>> +	{ 0xd13a, 0xff },
>> +	{ 0xd13b, 0xe6 },
>> +	{ 0xd13c, 0x18 },
>> +	{ 0xd13d, 0x60 },
>> +	{ 0xd13e, 0x80 },
>> +	{ 0xd13f, 0x06 },
>> +	{ 0xd140, 0x03 },
>> +	{ 0xd141, 0xff },
>> +	{ 0xd142, 0xff },
>> +	{ 0xd143, 0xdd },
>> +	{ 0xd144, 0xa8 },
>> +	{ 0xd145, 0x83 },
>> +	{ 0xd146, 0x40 },
>> +	{ 0xd147, 0x08 },
>> +	{ 0xd148, 0x85 },
>> +	{ 0xd149, 0x21 },
>> +	{ 0xd14a, 0x00 },
>> +	{ 0xd14b, 0x00 },
>> +	{ 0xd14c, 0x85 },
>> +	{ 0xd14d, 0x41 },
>> +	{ 0xd14e, 0x00 },
>> +	{ 0xd14f, 0x04 },
>> +	{ 0xd150, 0x44 },
>> +	{ 0xd151, 0x00 },
>> +	{ 0xd152, 0x48 },
>> +	{ 0xd153, 0x00 },
>> +	{ 0xd154, 0x9c },
>> +	{ 0xd155, 0x21 },
>> +	{ 0xd156, 0x00 },
>> +	{ 0xd157, 0x08 },
>> +	{ 0x6f0e, 0x03 },
>> +	{ 0x6f0f, 0x00 },
>> +	{ 0x460e, 0x08 },
>> +	{ 0x460f, 0x01 },
>> +	{ 0x4610, 0x00 },
>> +	{ 0x4611, 0x01 },
>> +	{ 0x4612, 0x00 },
>> +	{ 0x4613, 0x01 },
>> +	/* 8 bits */
>> +	{ 0x4605, 0x08 },
>> +	/* Swap data bits order [9:0] -> [0:9] */
>> +	{ 0x4709, 0x10 },
>> +	{ 0x4608, 0x00 },
>> +	{ 0x4609, 0x08 },
>> +	{ 0x6804, 0x00 },
>> +	{ 0x6805, 0x06 },
>> +	{ 0x6806, 0x00 },
>> +	{ 0x5120, 0x00 },
>> +	{ 0x3510, 0x00 },
>> +	{ 0x3504, 0x00 },
>> +	{ 0x6800, 0x00 },
>> +	{ 0x6f0d, 0x01 },
>> +	/* PCLK falling edge */
>> +	{ 0x4708, 0x01 },
>> +	{ 0x5000, 0xff },
>> +	{ 0x5001, 0xbf },
>> +	{ 0x5002, 0x7e },
>> +	{ 0x503d, 0x00 },
>> +	{ 0xc450, 0x01 },
>> +	{ 0xc452, 0x04 },
>> +	{ 0xc453, 0x00 },
>> +	{ 0xc454, 0x00 },
>> +	{ 0xc455, 0x01 },
>> +	{ 0xc456, 0x01 },
>> +	{ 0xc457, 0x00 },
>> +	{ 0xc458, 0x00 },
>> +	{ 0xc459, 0x00 },
>> +	{ 0xc45b, 0x00 },
>> +	{ 0xc45c, 0x01 },
>> +	{ 0xc45d, 0x00 },
>> +	{ 0xc45e, 0x00 },
>> +	{ 0xc45f, 0x00 },
>> +	{ 0xc460, 0x00 },
>> +	{ 0xc461, 0x01 },
>> +	{ 0xc462, 0x01 },
>> +	{ 0xc464, 0x03 },
>> +	{ 0xc465, 0x00 },
>> +	{ 0xc466, 0x8a },
>> +	{ 0xc467, 0x00 },
>> +	{ 0xc468, 0x86 },
>> +	{ 0xc469, 0x00 },
>> +	{ 0xc46a, 0x40 },
>> +	{ 0xc46b, 0x50 },
>> +	{ 0xc46c, 0x30 },
>> +	{ 0xc46d, 0x28 },
>> +	{ 0xc46e, 0x60 },
>> +	{ 0xc46f, 0x40 },
>> +	{ 0xc47c, 0x01 },
>> +	{ 0xc47d, 0x38 },
>> +	{ 0xc47e, 0x00 },
>> +	{ 0xc47f, 0x00 },
>> +	{ 0xc480, 0x00 },
>> +	{ 0xc481, 0xff },
>> +	{ 0xc482, 0x00 },
>> +	{ 0xc483, 0x40 },
>> +	{ 0xc484, 0x00 },
>> +	{ 0xc485, 0x18 },
>> +	{ 0xc486, 0x00 },
>> +	{ 0xc487, 0x18 },
>> +	{ 0xc488, (OV10635_VTS - 8) * 16 >> 8},
>> +	{ 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
>> +	{ 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
>> +	{ 0xc48b, (OV10635_VTS - 8) * 16 & 0xff},
>> +	{ 0xc48c, 0x00 },
>> +	{ 0xc48d, 0x04 },
>> +	{ 0xc48e, 0x00 },
>> +	{ 0xc48f, 0x04 },
>> +	{ 0xc490, 0x03 },
>> +	{ 0xc492, 0x20 },
>> +	{ 0xc493, 0x08 },
>> +	{ 0xc498, 0x02 },
>> +	{ 0xc499, 0x00 },
>> +	{ 0xc49a, 0x02 },
>> +	{ 0xc49b, 0x00 },
>> +	{ 0xc49c, 0x02 },
>> +	{ 0xc49d, 0x00 },
>> +	{ 0xc49e, 0x02 },
>> +	{ 0xc49f, 0x60 },
>> +	{ 0xc4a0, 0x03 },
>> +	{ 0xc4a1, 0x00 },
>> +	{ 0xc4a2, 0x04 },
>> +	{ 0xc4a3, 0x00 },
>> +	{ 0xc4a4, 0x00 },
>> +	{ 0xc4a5, 0x10 },
>> +	{ 0xc4a6, 0x00 },
>> +	{ 0xc4a7, 0x40 },
>> +	{ 0xc4a8, 0x00 },
>> +	{ 0xc4a9, 0x80 },
>> +	{ 0xc4aa, 0x0d },
>> +	{ 0xc4ab, 0x00 },
>> +	{ 0xc4ac, 0x0f },
>> +	{ 0xc4ad, 0xc0 },
>> +	{ 0xc4b4, 0x01 },
>> +	{ 0xc4b5, 0x01 },
>> +	{ 0xc4b6, 0x00 },
>> +	{ 0xc4b7, 0x01 },
>> +	{ 0xc4b8, 0x00 },
>> +	{ 0xc4b9, 0x01 },
>> +	{ 0xc4ba, 0x01 },
>> +	{ 0xc4bb, 0x00 },
>> +	{ 0xc4bc, 0x01 },
>> +	{ 0xc4bd, 0x60 },
>> +	{ 0xc4be, 0x02 },
>> +	{ 0xc4bf, 0x33 },
>> +	{ 0xc4c8, 0x03 },
>> +	{ 0xc4c9, 0xd0 },
>> +	{ 0xc4ca, 0x0e },
>> +	{ 0xc4cb, 0x00 },
>> +	{ 0xc4cc, 0x0e },
>> +	{ 0xc4cd, 0x51 },
>> +	{ 0xc4ce, 0x0e },
>> +	{ 0xc4cf, 0x51 },
>> +	{ 0xc4d0, 0x04 },
>> +	{ 0xc4d1, 0x80 },
>> +	{ 0xc4e0, 0x04 },
>> +	{ 0xc4e1, 0x02 },
>> +	{ 0xc4e2, 0x01 },
>> +	{ 0xc4e4, 0x10 },
>> +	{ 0xc4e5, 0x20 },
>> +	{ 0xc4e6, 0x30 },
>> +	{ 0xc4e7, 0x40 },
>> +	{ 0xc4e8, 0x50 },
>> +	{ 0xc4e9, 0x60 },
>> +	{ 0xc4ea, 0x70 },
>> +	{ 0xc4eb, 0x80 },
>> +	{ 0xc4ec, 0x90 },
>> +	{ 0xc4ed, 0xa0 },
>> +	{ 0xc4ee, 0xb0 },
>> +	{ 0xc4ef, 0xc0 },
>> +	{ 0xc4f0, 0xd0 },
>> +	{ 0xc4f1, 0xe0 },
>> +	{ 0xc4f2, 0xf0 },
>> +	{ 0xc4f3, 0x80 },
>> +	{ 0xc4f4, 0x00 },
>> +	{ 0xc4f5, 0x20 },
>> +	{ 0xc4f6, 0x02 },
>> +	{ 0xc4f7, 0x00 },
>> +	{ 0xc4f8, 0x00 },
>> +	{ 0xc4f9, 0x00 },
>> +	{ 0xc4fa, 0x00 },
>> +	{ 0xc4fb, 0x01 },
>> +	{ 0xc4fc, 0x01 },
>> +	{ 0xc4fd, 0x00 },
>> +	{ 0xc4fe, 0x04 },
>> +	{ 0xc4ff, 0x02 },
>> +	{ 0xc500, 0x48 },
>> +	{ 0xc501, 0x74 },
>> +	{ 0xc502, 0x58 },
>> +	{ 0xc503, 0x80 },
>> +	{ 0xc504, 0x05 },
>> +	{ 0xc505, 0x80 },
>> +	{ 0xc506, 0x03 },
>> +	{ 0xc507, 0x80 },
>> +	{ 0xc508, 0x01 },
>> +	{ 0xc509, 0xc0 },
>> +	{ 0xc50a, 0x01 },
>> +	{ 0xc50b, 0xa0 },
>> +	{ 0xc50c, 0x01 },
>> +	{ 0xc50d, 0x2c },
>> +	{ 0xc50e, 0x01 },
>> +	{ 0xc50f, 0x0a },
>> +	{ 0xc510, 0x00 },
>> +	{ 0xc511, 0x00 },
>> +	{ 0xc512, 0xe5 },
>> +	{ 0xc513, 0x14 },
>> +	{ 0xc514, 0x04 },
>> +	{ 0xc515, 0x00 },
>> +	{ 0xc518, OV10635_VTS >> 8},
>> +	{ 0xc519, OV10635_VTS & 0xff},
>> +	{ 0xc51a, OV10635_HTS >> 8},
>> +	{ 0xc51b, OV10635_HTS & 0xff},
>> +	{ 0xc2e0, 0x00 },
>> +	{ 0xc2e1, 0x51 },
>> +	{ 0xc2e2, 0x00 },
>> +	{ 0xc2e3, 0xd6 },
>> +	{ 0xc2e4, 0x01 },
>> +	{ 0xc2e5, 0x5e },
>> +	{ 0xc2e9, 0x01 },
>> +	{ 0xc2ea, 0x7a },
>> +	{ 0xc2eb, 0x90 },
>> +	{ 0xc2ed, 0x00 },
>> +	{ 0xc2ee, 0x7a },
>> +	{ 0xc2ef, 0x64 },
>> +	{ 0xc308, 0x00 },
>> +	{ 0xc309, 0x00 },
>> +	{ 0xc30a, 0x00 },
>> +	{ 0xc30c, 0x00 },
>> +	{ 0xc30d, 0x01 },
>> +	{ 0xc30e, 0x00 },
>> +	{ 0xc30f, 0x00 },
>> +	{ 0xc310, 0x01 },
>> +	{ 0xc311, 0x60 },
>> +	{ 0xc312, 0xff },
>> +	{ 0xc313, 0x08 },
>> +	{ 0xc314, 0x01 },
>> +	{ 0xc315, 0x00 },
>> +	{ 0xc316, 0xff },
>> +	{ 0xc317, 0x0b },
>> +	{ 0xc318, 0x00 },
>> +	{ 0xc319, 0x0c },
>> +	{ 0xc31a, 0x00 },
>> +	{ 0xc31b, 0xe0 },
>> +	{ 0xc31c, 0x00 },
>> +	{ 0xc31d, 0x14 },
>> +	{ 0xc31e, 0x00 },
>> +	{ 0xc31f, 0xc5 },
>> +	{ 0xc320, 0xff },
>> +	{ 0xc321, 0x4b },
>> +	{ 0xc322, 0xff },
>> +	{ 0xc323, 0xf0 },
>> +	{ 0xc324, 0xff },
>> +	{ 0xc325, 0xe8 },
>> +	{ 0xc326, 0x00 },
>> +	{ 0xc327, 0x46 },
>> +	{ 0xc328, 0xff },
>> +	{ 0xc329, 0xd2 },
>> +	{ 0xc32a, 0xff },
>> +	{ 0xc32b, 0xe4 },
>> +	{ 0xc32c, 0xff },
>> +	{ 0xc32d, 0xbb },
>> +	{ 0xc32e, 0x00 },
>> +	{ 0xc32f, 0x61 },
>> +	{ 0xc330, 0xff },
>> +	{ 0xc331, 0xf9 },
>> +	{ 0xc332, 0x00 },
>> +	{ 0xc333, 0xd9 },
>> +	{ 0xc334, 0x00 },
>> +	{ 0xc335, 0x2e },
>> +	{ 0xc336, 0x00 },
>> +	{ 0xc337, 0xb1 },
>> +	{ 0xc338, 0xff },
>> +	{ 0xc339, 0x64 },
>> +	{ 0xc33a, 0xff },
>> +	{ 0xc33b, 0xeb },
>> +	{ 0xc33c, 0xff },
>> +	{ 0xc33d, 0xe8 },
>> +	{ 0xc33e, 0x00 },
>> +	{ 0xc33f, 0x48 },
>> +	{ 0xc340, 0xff },
>> +	{ 0xc341, 0xd0 },
>> +	{ 0xc342, 0xff },
>> +	{ 0xc343, 0xed },
>> +	{ 0xc344, 0xff },
>> +	{ 0xc345, 0xad },
>> +	{ 0xc346, 0x00 },
>> +	{ 0xc347, 0x66 },
>> +	{ 0xc348, 0x01 },
>> +	{ 0xc349, 0x00 },
>> +	{ 0x6700, 0x04 },
>> +	{ 0x6701, 0x7b },
>> +	{ 0x6702, 0xfd },
>> +	{ 0x6703, 0xf9 },
>> +	{ 0x6704, 0x3d },
>> +	{ 0x6705, 0x71 },
>> +	{ 0x6706, 0x78 },
>> +	{ 0x6708, 0x05 },
>> +	{ 0x6f06, 0x6f },
>> +	{ 0x6f07, 0x00 },
>> +	{ 0x6f0a, 0x6f },
>> +	{ 0x6f0b, 0x00 },
>> +	{ 0x6f00, 0x03 },
>> +	{ 0xc34c, 0x01 },
>> +	{ 0xc34d, 0x00 },
>> +	{ 0xc34e, 0x46 },
>> +	{ 0xc34f, 0x55 },
>> +	{ 0xc350, 0x00 },
>> +	{ 0xc351, 0x40 },
>> +	{ 0xc352, 0x00 },
>> +	{ 0xc353, 0xff },
>> +	{ 0xc354, 0x04 },
>> +	{ 0xc355, 0x08 },
>> +	{ 0xc356, 0x01 },
>> +	{ 0xc357, 0xef },
>> +	{ 0xc358, 0x30 },
>> +	{ 0xc359, 0x01 },
>> +	{ 0xc35a, 0x64 },
>> +	{ 0xc35b, 0x46 },
>> +	{ 0xc35c, 0x00 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0x3042, 0xf0 },
>> +	{ 0xc261, 0x01 },
>> +	{ 0x301b, 0xf0 },
>> +	{ 0x301c, 0xf0 },
>> +	{ 0x301a, 0xf0 },
>> +	{ 0x6f00, 0xc3 },
>> +	{ 0xc46a, 0x30 },
>> +	{ 0xc46d, 0x20 },
>> +	{ 0xc464, 0x84 },
>> +	{ 0xc465, 0x00 },
>> +	{ 0x6f00, 0x03 },
>> +	{ 0x6f00, 0x43 },
>> +	{ 0x381c, 0x00 },
>> +	{ 0x381d, 0x40 },
>> +	{ 0xc454, 0x01 },
>> +	{ 0x6f00, 0xc3 },
>> +	{ 0xc454, 0x00 },
>> +	{ 0xc4b1, 0x02 },
>> +	{ 0xc4b2, 0x01 },
>> +	{ 0xc4b3, 0x03 },
>> +	{ 0x6f00, 0x03 },
>> +	{ 0x6f00, 0x43 },
>> +	/* enable FSIN (FRAMESYNC input) functionality */
>> +	{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
>> +	{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
>> +	{ 0x3834, OV10635_VTS >> 8 },
>> +	{ 0x3835, OV10635_VTS & 0xff },
>> +	{ 0x302e, 0x01 },
>> +};
>> +
>> +#endif /* __RDACM20_OV10635_H__ */
>> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
>> new file mode 100644
>> index 000000000000..d96b2eb5ab1b
>> --- /dev/null
>> +++ b/drivers/media/i2c/rdacm20.c
>> @@ -0,0 +1,635 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * IMI RDACM20 GMSL Camera Driver
>> + *
>> + * Copyright (C) 2017-2018 Jacopo Mondi
>> + * Copyright (C) 2017-2018 Kieran Bingham
>> + * Copyright (C) 2017-2018 Laurent Pinchart
>> + * Copyright (C) 2017-2018 Niklas Söderlund
>> + * Copyright (C) 2016 Renesas Electronics Corporation
>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>> + */
>> +
>> +/*
>> + * The camera is mode of an Omnivision OV10635 sensor connected to a Maxim
>> + * MAX9271 GMSL serializer.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/fwnode.h>
>> +#include <linux/init.h>
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/videodev2.h>
>> +
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#include "rdacm20-ov10635.h"
>> +
>> +#define RDACM20_SENSOR_HARD_RESET
>> +
>> +#define MAX9271_I2C_ADDRESS		0x40
>> +
>> +/* Register 0x04 */
>> +#define MAX9271_SEREN			BIT(7)
>> +#define MAX9271_CLINKEN			BIT(6)
>> +#define MAX9271_PRBSEN			BIT(5)
>> +#define MAX9271_SLEEP			BIT(4)
>> +#define MAX9271_INTTYPE_I2C		(0 << 2)
>> +#define MAX9271_INTTYPE_UART		(1 << 2)
>> +#define MAX9271_INTTYPE_NONE		(2 << 2)
>> +#define MAX9271_REVCCEN			BIT(1)
>> +#define MAX9271_FWDCCEN			BIT(0)
>> +/* Register 0x07 */
>> +#define MAX9271_DBL			BIT(7)
>> +#define MAX9271_DRS			BIT(6)
>> +#define MAX9271_BWS			BIT(5)
>> +#define MAX9271_ES			BIT(4)
>> +#define MAX9271_HVEN			BIT(2)
>> +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
>> +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
>> +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
>> +/* Register 0x08 */
>> +#define MAX9271_INVVS			BIT(7)
>> +#define MAX9271_INVHS			BIT(6)
>> +#define MAX9271_REV_LOGAIN		BIT(3)
>> +#define MAX9271_REV_HIVTH		BIT(0)
>> +/* Register 0x09 */
>> +#define MAX9271_ID			0x09
>> +/* Register 0x0d */
>> +#define MAX9271_I2CLOCACK		BIT(7)
>> +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
>> +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
>> +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
>> +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
>> +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
>> +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
>> +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
>> +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
>> +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
>> +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
>> +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
>> +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
>> +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
>> +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
>> +#define MAX9271_I2CSLVTO_256US		(1 << 0)
>> +#define MAX9271_I2CSLVTO_64US		(0 << 0)
>> +/* Register 0x0f */
>> +#define MAX9271_GPIO5OUT		BIT(5)
>> +#define MAX9271_GPIO4OUT		BIT(4)
>> +#define MAX9271_GPIO3OUT		BIT(3)
>> +#define MAX9271_GPIO2OUT		BIT(2)
>> +#define MAX9271_GPIO1OUT		BIT(1)
>> +#define MAX9271_SETGPO			BIT(0)
>> +/* Register 0x15 */
>> +#define MAX9271_PCLKDET			BIT(0)
>> +
>> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
>> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
>> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
>> +
>> +#define OV10635_I2C_ADDRESS		0x30
>> +
>> +#define OV10635_SOFTWARE_RESET		0x0103
>> +#define OV10635_PID			0x300a
>> +#define OV10635_VER			0x300b
>> +#define OV10635_SC_CMMN_SCCB_ID		0x300c
>> +#define OV10635_SC_CMMN_SCCB_ID_SELECT	BIT(0)
>> +#define OV10635_VERSION			0xa635
>> +
>> +#define OV10635_WIDTH			1280
>> +#define OV10635_HEIGHT			800
>> +#define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
>> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
>> +
>> +struct rdacm20_device {
>> +	struct i2c_client		*client;
>> +	struct i2c_client		*sensor;
>> +	struct v4l2_subdev		sd;
>> +	struct media_pad		pad;
>> +	struct v4l2_ctrl_handler	ctrls;
>> +};
>> +
>> +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
>> +{
>> +	return container_of(sd, struct rdacm20_device, sd);
>> +}
>> +
>> +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
>> +{
>> +	return sd_to_rdacm20(i2c_get_clientdata(client));
>> +}
>> +
>> +static int max9271_read(struct rdacm20_device *dev, u8 reg)
>> +{
>> +	int ret;
>> +
>> +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
>> +
>> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
>> +	if (ret < 0)
>> +		dev_dbg(&dev->client->dev,
>> +			"%s: register 0x%02x read failed (%d)\n",
>> +			__func__, reg, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int max9271_write(struct rdacm20_device *dev, u8 reg, u8 val)
>> +{
>> +	int ret;
>> +
>> +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
>> +
>> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
>> +	if (ret < 0)
>> +		dev_err(&dev->client->dev,
>> +			"%s: register 0x%02x write failed (%d)\n",
>> +			__func__, reg, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
>> +{
>> +	u8 buf[2] = { reg >> 8, reg & 0xff };
>> +	int ret;
>> +
>> +	ret = i2c_master_send(dev->sensor, buf, 2);
>> +	if (ret == 2)
>> +		ret = i2c_master_recv(dev->sensor, buf, 2);
>> +
>> +	if (ret < 0) {
>> +		dev_dbg(&dev->client->dev,
>> +			"%s: register 0x%04x read failed (%d)\n",
>> +			__func__, reg, ret);
>> +		return ret;
>> +	}
>> +
>> +	return (buf[0] << 8) | buf[1];
>> +}
>> +
>> +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
>> +{
>> +	u8 buf[3] = { reg >> 8, reg & 0xff, val };
>> +	int ret;
>> +
>> +	dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
>> +
>> +	ret = i2c_master_send(dev->sensor, buf, 3);
>> +	return ret < 0 ? ret : 0;
>> +}
>> +
>> +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
>> +{
>> +	int ret;
>> +
>> +	ret = __ov10635_write(dev, reg, val);
>> +	if (ret < 0)
>> +		dev_err(&dev->client->dev,
>> +			"%s: register 0x%04x write failed (%d)\n",
>> +			__func__, reg, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov10635_set_regs(struct rdacm20_device *dev,
>> +			    const struct ov10635_reg *regs,
>> +			    unsigned int nr_regs)
>> +{
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	for (i = 0; i < nr_regs; i++) {
>> +		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
>> +		if (ret) {
>> +			dev_err(&dev->client->dev,
>> +				"%s: register %u (0x%04x) write failed (%d)\n",
>> +				__func__, i, regs[i].reg, ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * rdacm20_pclk_detect() - Detect valid pixel clock from image sensor
>> + *
>> + * Wait up to 10ms for a valid pixel clock.
>> + *
>> + * Returns 0 for success, < 0 for pixel clock not properly detected
>> + */
>> +static int rdacm20_pclk_detect(struct rdacm20_device *dev)
>> +{
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	for (i = 0; i < 100; i++) {
>> +		ret = max9271_read(dev, 0x15);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		if (ret & MAX9271_PCLKDET)
>> +			return 0;
>> +
>> +		usleep_range(50, 100);
>> +	}
>> +
>> +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
>> +	return -EIO;
>> +}
>> +
>> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
>> +	int ret;
>> +
>> +	if (enable) {
>> +		ret = rdacm20_pclk_detect(dev);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Enable the serial link. */
>> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
>> +			      MAX9271_FWDCCEN);
>> +	} else {
>> +		/* Disable the serial link. */
>> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
>> +			      MAX9271_FWDCCEN);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
>> +				 struct v4l2_mbus_config *cfg)
>> +{
>> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
>> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
>> +	cfg->type = V4L2_MBUS_CSI2_DPHY;
> 
> Hmm. What are you using g_mbus_config() for?

Good point here ... I assumed it was passed through up to the VIN - but
it's really not applicable here.

Or if it is - then it should be describing the GMSL bus link!

I'll bet this isn't even getting called and can likely be removed.


> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
>> +				  struct v4l2_subdev_pad_config *cfg,
>> +				  struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	if (code->pad || code->index > 0)
>> +		return -EINVAL;
>> +
>> +	code->code = OV10635_FORMAT;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_get_fmt(struct v4l2_subdev *sd,
>> +			   struct v4l2_subdev_pad_config *cfg,
>> +			   struct v4l2_subdev_format *format)
>> +{
>> +	struct v4l2_mbus_framefmt *mf = &format->format;
>> +
>> +	if (format->pad)
>> +		return -EINVAL;
>> +
>> +	mf->width		= OV10635_WIDTH;
>> +	mf->height		= OV10635_HEIGHT;
>> +	mf->code		= OV10635_FORMAT;
>> +	mf->colorspace		= V4L2_COLORSPACE_RAW;
>> +	mf->field		= V4L2_FIELD_NONE;
>> +	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;
>> +	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;
>> +	mf->xfer_func		= V4L2_XFER_FUNC_NONE;
>> +
>> +	return 0;
>> +}
>> +
>> +static struct v4l2_subdev_video_ops rdacm20_video_ops = {
>> +	.s_stream	= rdacm20_s_stream,
>> +	.g_mbus_config	= rdacm20_g_mbus_config,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
>> +	.enum_mbus_code = rdacm20_enum_mbus_code,
>> +	.get_fmt	= rdacm20_get_fmt,
>> +	.set_fmt	= rdacm20_get_fmt,
>> +};
>> +
>> +static struct v4l2_subdev_ops rdacm20_subdev_ops = {
>> +	.video		= &rdacm20_video_ops,
>> +	.pad		= &rdacm20_subdev_pad_ops,
>> +};
>> +
>> +static int max9271_configure_i2c(struct rdacm20_device *dev)
>> +{
>> +	/*
>> +	 * Configure the I2C bus:
>> +	 *
>> +	 * - Enable high thresholds on the reverse channel
>> +	 * - Disable artificial ACK and set I2C speed
>> +	 */
>> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
>> +	usleep_range(5000, 8000);
>> +
>> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
>> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
>> +	usleep_range(5000, 8000);
>> +
>> +	return 0;
>> +}
>> +
>> +static int max9271_configure_gmsl_link(struct rdacm20_device *dev)
>> +{
>> +	/*
>> +	 * Disable the serial link and enable the configuration link to allow
>> +	 * the control channel to operate in a low-speed mode in the absence of
>> +	 * the serial link clock.
>> +	 */
>> +	max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
>> +		      MAX9271_FWDCCEN);
>> +
>> +	/*
>> +	 * The serializer temporarily disables the reverse control channel for
>> +	 * 350µs after starting/stopping the forward serial link, but the
>> +	 * deserializer synchronization time isn't clearly documented.
>> +	 *
>> +	 * According to the serializer datasheet we should wait 3ms, while
>> +	 * according to the deserializer datasheet we should wait 5ms.
>> +	 *
>> +	 * Short delays here appear to show bit-errors in the writes following.
>> +	 * Therefore a conservative delay seems best here.
>> +	 */
>> +	usleep_range(5000, 8000);
>> +
>> +	/*
>> +	 * Configure the GMSL link:
>> +	 *
>> +	 * - Double input mode, high data rate, 24-bit mode
>> +	 * - Latch input data on PCLKIN rising edge
>> +	 * - Enable HS/VS encoding
>> +	 * - 1-bit parity error detection
>> +	 */
>> +	max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
>> +		      MAX9271_EDC_1BIT_PARITY);
>> +	usleep_range(5000, 8000);
>> +
>> +	return 0;
>> +}
>> +
>> +static int max9271_verify_id(struct rdacm20_device *dev)
>> +{
>> +	int ret;
>> +
>> +	ret = max9271_read(dev, 0x1e);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	if (ret != MAX9271_ID) {
>> +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
>> +			ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
>> +{
>> +	int ret;
>> +
>> +	/* Change the MAX9271 I2C address. */
>> +	ret = max9271_write(dev, 0x00, addr << 1);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev,
>> +			"MAX9271 I2C address change failed (%d)\n", ret);
>> +		return ret;
>> +	}
>> +	dev->client->addr = addr;
>> +	usleep_range(3500, 5000);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_initialize(struct rdacm20_device *dev)
>> +{
>> +	u32 addrs[2];
>> +	int ret;
>> +
>> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
>> +					 addrs, ARRAY_SIZE(addrs));
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * FIXME: The MAX9271 boots at a default address that we will change to
>> +	 * the address specified in DT. Set the client address back to the
>> +	 * default for initial communication.
>> +	 */
>> +	dev->client->addr = MAX9271_I2C_ADDRESS;
>> +
>> +	/* Verify communication with the MAX9271. */
>> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
>> +
>> +	/*
>> +	 *  Ensure that we have a good link configuration before attempting to
>> +	 *  identify the device.
>> +	 */
>> +	max9271_configure_i2c(dev);
>> +	max9271_configure_gmsl_link(dev);
>> +
>> +	ret = max9271_verify_id(dev);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = max9271_configure_address(dev, addrs[0]);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Reset and verify communication with the OV10635. */
>> +#ifdef RDACM20_SENSOR_HARD_RESET
>> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
>> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
>> +	mdelay(10);
>> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
>> +	mdelay(10);
> 
> Do you need a busy loop? Could you use msleep()?

Checkpatch warns here:

WARNING: msleep < 20ms can sleep for up to 20ms; see
Documentation/timers/timers-howto.txt
#10: FILE: drivers/media/i2c/rdacm20.c:461:
+       msleep(10);

I think for this context, msleep(10) even with the warning is fine here,
but perhaps we can meet that with a usleep_range(10000, 20000); too.


>> +#else
>> +	/* Perform a software reset. */
>> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	udelay(100);
>> +#endif
>> +
>> +	ret = ov10635_read16(dev, OV10635_PID);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
>> +			ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	if (ret != OV10635_VERSION) {
>> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
>> +			ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
>> +
>> +	/* Change the sensor I2C address. */
>> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
>> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev,
>> +			"OV10635 I2C address change failed (%d)\n", ret);
>> +		return ret;
>> +	}
>> +	dev->sensor->addr = addrs[1];
>> +	usleep_range(3500, 5000);
>> +
>> +	/* Program the 0V10635 initial configuration. */
>> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
>> +			       ARRAY_SIZE(ov10635_regs_wizard));
> 
> return ov...(); ?

Yes, that would be nicer.

> 
>> +	if (ret)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_probe(struct i2c_client *client,
>> +			 const struct i2c_device_id *did)
>> +{
>> +	struct rdacm20_device *dev;
>> +	struct fwnode_handle *ep;
>> +	int ret;
>> +
>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> 
> You could use devm_kzalloc().

Will change.

> 
>> +	if (!dev)
>> +		return -ENOMEM;
>> +
>> +	dev->client = client;
>> +
>> +	/* Create the dummy I2C client for the sensor. */
>> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
>> +	if (!dev->sensor) {
>> +		ret = -ENXIO;
>> +		goto error;
>> +	}
>> +
>> +	/* Initialize the hardware. */
>> +	ret = rdacm20_initialize(dev);
>> +	if (ret < 0)
>> +		goto error;
>> +
>> +	/* Initialize and register the subdevice. */
>> +	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
>> +	dev->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> 
> |=, as in the other patch.

Bah - yes :)


> 
>> +
>> +	v4l2_ctrl_handler_init(&dev->ctrls, 1);
>> +	/*
>> +	 * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
>> +	 * hardcoded frequency in the BSP CSI-2 receiver driver.
>> +	 */
>> +	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
>> +			  50000000, 1, 50000000);
>> +	dev->sd.ctrl_handler = &dev->ctrls;
>> +
>> +	ret = dev->ctrls.error;
>> +	if (ret)
>> +		goto error;
>> +
>> +	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
>> +	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
>> +	if (ret < 0)
>> +		goto error;
>> +
>> +	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
>> +	if (!ep) {
>> +		dev_err(&client->dev,
>> +			"Unable to get endpoint in node %pOF\n",
>> +			client->dev.of_node);
>> +		ret = -ENOENT;
>> +		goto error;
>> +	}
>> +	dev->sd.fwnode = ep;
>> +
>> +	ret = v4l2_async_register_subdev(&dev->sd);
>> +	if (ret)
>> +		goto error_put_node;
>> +
>> +	return 0;
>> +
>> +error_put_node:
>> +	fwnode_handle_put(ep);
>> +error:
> 
> You're missing v4l2_ctrl_handler_free() here.

Good spot. Thanks

> 
>> +	media_entity_cleanup(&dev->sd.entity);
>> +	if (dev->sensor)
>> +		i2c_unregister_device(dev->sensor);
>> +	kfree(dev);
>> +
>> +	dev_err(&client->dev, "probe failed\n");
>> +
>> +	return ret;
>> +}
>> +
>> +static int rdacm20_remove(struct i2c_client *client)
>> +{
>> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
>> +
>> +	fwnode_handle_put(dev->sd.fwnode);
>> +	v4l2_async_unregister_subdev(&dev->sd);
> 
> As well as here.


Ack.


> 
>> +	media_entity_cleanup(&dev->sd.entity);
>> +	i2c_unregister_device(dev->sensor);
>> +	kfree(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static void rdacm20_shutdown(struct i2c_client *client)
>> +{
>> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
>> +
>> +	/* make sure stream off during shutdown (reset/reboot) */
>> +	rdacm20_s_stream(&dev->sd, 0);
>> +}
>> +
>> +static const struct i2c_device_id rdacm20_id[] = {
>> +	{ "rdacm20", 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
>> +
>> +static const struct of_device_id rdacm20_of_ids[] = {
>> +	{ .compatible = "imi,rdacm20", },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
>> +
>> +static struct i2c_driver rdacm20_i2c_driver = {
>> +	.driver	= {
>> +		.name	= "rdacm20",
>> +		.of_match_table = rdacm20_of_ids,
>> +	},
>> +	.probe		= rdacm20_probe,
> 
> Could you use probe_new, so you could remove the i2c ID table? Or do you
> need that for something?

I believe probe_new is probably fine.

I should really resurrect my i2c-probe-coccinelle patch and get that
conversion task done, so we can get to removing and replacing .probe :)

(note to self ... starting projects when unemployed becomes difficult to
continue when someone else gives you projects to work on all the time ...)


> 
>> +	.remove		= rdacm20_remove,
>> +	.shutdown	= rdacm20_shutdown,
>> +	.id_table	= rdacm20_id,
>> +};
>> +
>> +module_i2c_driver(rdacm20_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
>> +MODULE_AUTHOR("Vladimir Barinov");
>> +MODULE_LICENSE("GPL");

Changes described above made and tested on a *single MAX9286* capturing
4 cameras simultaneously, now on my rcar.git gmsl/v5 branch ... :D
Sakari Ailus Dec. 20, 2018, 11:11 p.m. UTC | #3
Hu Kieran,

Apologies for the late reply.

On Wed, Nov 28, 2018 at 12:51:39PM +0000, Kieran Bingham wrote:
> Hi Sakari,
> 
> Thank you for your review,
> 
> On 20/11/2018 08:34, Sakari Ailus wrote:
> > Hi Kieran,
> > 
> > On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
> >> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >>
> >> The RDACM20 is a GMSL camera supporting 1280x800 resolution images
> >> developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
> >> GMSL serializer.
> >>
> >> The GMSL link carries power, control (I2C) and video data over a
> >> single coax cable.
> >>
> >> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> >> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> >> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >>
> >> ---
> >> v2:
> >>  - Fix MAINTAINERS entry
> >>
> >> v3:
> >>  - Use new V4L2_MBUS_CSI2_DPHY bus type
> >>  - Remove 'always zero' error print
> >>  - Fix module description
> >> ---
> >>  MAINTAINERS                         |  10 +
> >>  drivers/media/i2c/Kconfig           |  11 +
> >>  drivers/media/i2c/Makefile          |   1 +
> >>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
> >>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
> >>  5 files changed, 1610 insertions(+)
> >>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
> >>  create mode 100644 drivers/media/i2c/rdacm20.c
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 745f0fd1fff1..26ef20087a43 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -12230,6 +12230,16 @@ S:	Supported
> >>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
> >>  F:	tools/testing/selftests/rcutorture
> >>  
> >> +RDACM20 Camera Sensor
> >> +M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
> >> +M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >> +M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >> +M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> > 
> > I'm happy to see this will be well maintained. :-)
> 
> Well it means /someone/ should always be able to pick it up :D
> 
> I didn't know who to put here - so I put all of the current blamees ;)
> We've all put a lot of time and work in to the GMSL bring up and
> refactoring.
> 
> If you think it's overkill, I can reduce the names. Same on max9286.

Not at all. There are too many drivers that do not receive the attention
they'd need. :-I

> 
> 
> >> +L:	linux-media@vger.kernel.org
> >> +S:	Maintained
> >> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
> >> +F:	drivers/media/i2c/rdacm20*
> >> +
> >>  RDC R-321X SoC
> >>  M:	Florian Fainelli <florian@openwrt.org>
> >>  S:	Maintained
> >> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> >> index eadc00bdd3bf..5eded5e337ec 100644
> >> --- a/drivers/media/i2c/Kconfig
> >> +++ b/drivers/media/i2c/Kconfig
> >> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
> >>  	  This is a V4L2 sensor driver for Samsung S5C73M3
> >>  	  8 Mpixel camera.
> >>  
> >> +config VIDEO_RDACM20

Btw. I think this might be worth a new comment to tell what devices can be
found here --- it's not a camera sensor as such really. But I wonder how
should it be called. We do have "Miscellaneous helper chips" at the end.
I'm not sure that'd be better. As-is could be fine, too.

> >> +	tristate "IMI RDACM20 camera support"
> >> +	depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> >> +	select V4L2_FWNODE
> >> +	help
> >> +	  This driver supports the IMI RDACM20 GMSL camera, used in
> >> +	  ADAS systems.
> >> +
> >> +	  This camera should be used in conjunction with a GMSL
> >> +	  deserialiser such as the MAX9286.
> >> +
> >>  comment "Flash devices"
> >>  
> >>  config VIDEO_ADP1653
> >> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> >> index 4de7fe62b179..121d28283d45 100644
> >> --- a/drivers/media/i2c/Makefile
> >> +++ b/drivers/media/i2c/Makefile
> >> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
> >>  obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
> >>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
> >>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> >> +obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
> >>  
> >>  obj-$(CONFIG_SDR_MAX2175) += max2175.o
> >> diff --git a/drivers/media/i2c/rdacm20-ov10635.h b/drivers/media/i2c/rdacm20-ov10635.h
> >> new file mode 100644
> >> index 000000000000..3c53a3262ee2
> >> --- /dev/null
> >> +++ b/drivers/media/i2c/rdacm20-ov10635.h
> >> @@ -0,0 +1,953 @@
> >> +/* SPDX-License-Identifier: GPL-2.0+ */
> >> +/*
> >> + * IMI RDACM20 camera OV10635 sensor register initialization values
> >> + *
> >> + * Copyright (C) 2017-2018 Jacopo Mondi
> >> + * Copyright (C) 2017-2018 Kieran Bingham
> >> + * Copyright (C) 2017-2018 Laurent Pinchart
> >> + * Copyright (C) 2017-2018 Niklas Söderlund
> >> + * Copyright (C) 2016 Renesas Electronics Corporation
> >> + * Copyright (C) 2015 Cogent Embedded, Inc.
> >> + *
> >> + */
> >> +
> >> +/*
> >> + * Generated by the OmniVision ov10635 sensor camera wizard for
> >> + * 1280x800@30/UYVY/BT601/8bit.
> >> + */
> >> +
> >> +#ifndef __RDACM20_OV10635_H__
> >> +#define __RDACM20_OV10635_H__
> >> +
> >> +#define OV10635_SENSOR_WIDTH		1312
> >> +#define OV10635_SENSOR_HEIGHT		814
> >> +
> >> +#define OV10635_MAX_WIDTH		1280
> >> +#define OV10635_MAX_HEIGHT		800
> >> +
> >> +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
> >> +#define OV10635_HTS			1572
> >> +/* FPS = 29,9998 */
> >> +#define OV10635_VTS			933
> > 
> > A part of this driver looks like a driver for an OV camera sensor. Would
> > there be something that prevents separating the camera sensor driver from
> > this one?
> 
> I don't think there's anything preventing it - except (a fair bit of)
> development time.
> 
> We also have the RDACM21 to support, which uses the max9271 and an
> OV10640. At that time - this will absolutely have to be split. We
> shouldn't replicate the max9271 code.
> 
> I mentioned briefly in the cover letter:
> 
> > Further anticipated work in this area includes supporting the RDACM21
> > camera, at which point the RDACM20 will be adapted to separate out the
> > MAX9271 and the OV10635 sensor components.

Oh, sorry; I missed that.

Does the DT currently contain all the necessary information for the drivers
to get everything they need, if you separated them?

> 
> But to get more dedicated time to work this - we need to show some
> progress on GMSL up-streaming, so the max9286 and bindings take priority
> for now.
> 
> A little bit catch 22 ... :D
> 
> I'm sure there will be overlap between GMSL and FPD-Link with the RDACM
> range [0] of cameras too, which also provide TI-FPD Link serialisers.
> 
> I currently envisage that we would have an RDACM20 'driver' which would
> know that it has a max9271 serialiser and an OV10635 sensor, and would
> handle the links of any subdevices internally.

I don't remember all the details, but my understanding is that RDACM20 is
much more than just a box that contains the serialiser and the sensors. So
very probably it'll need its own driver, too. Powering on the sensors, for
instance, seemed hard to make generic.

> 
> As the RDACM20 is an object itself, I think this makes sense ... unless
> anyone suggests that each part should be broken down into the DT
> directly ? (I think that would possibly be a bit too much)
> 
> 
> [0]
> https://www.global-imi.com/sites/default/files/Generic-Minicube-Catalogue-1020151.pdf
> 
> 
> >> +
> >> +struct ov10635_reg {
> >> +	u16	reg;
> >> +	u8	val;
> >> +};
> >> +
> >> +static const struct ov10635_reg ov10635_regs_wizard[] = {
> >> +	{ 0x301b, 0xff },
> >> +	{ 0x301c, 0xff },
> >> +	{ 0x301a, 0xff },
> >> +	{ 0x3011, 0x42 },
> >> +	{ 0x6900, 0x0c },
> >> +	{ 0x6901, 0x19 },
> >> +	{ 0x3503, 0x10 },
> >> +	{ 0x3025, 0x03 },
> >> +	{ 0x3003, 0x16 },
> >> +	{ 0x3004, 0x30 },
> >> +	{ 0x3005, 0x40 },
> >> +	{ 0x3006, 0x91 },
> >> +	{ 0x3600, 0x74 },
> >> +	{ 0x3601, 0x2b },
> >> +	{ 0x3612, 0x00 },
> >> +	{ 0x3611, 0x67 },
> >> +	{ 0x3633, 0xca },
> >> +	{ 0x3602, 0xaf },
> >> +	{ 0x3603, 0x04 },
> >> +	{ 0x3630, 0x28 },
> >> +	{ 0x3631, 0x16 },
> >> +	{ 0x3714, 0x10 },
> >> +	{ 0x371d, 0x01 },
> >> +	{ 0x4300, 0x3a },
> >> +	{ 0x3007, 0x01 },
> >> +	{ 0x3024, 0x03 },
> >> +	{ 0x3020, 0x0a },
> >> +	{ 0x3702, 0x0d },
> >> +	{ 0x3703, 0x20 },
> >> +	{ 0x3704, 0x15 },
> >> +	{ 0x3709, 0xa8 },
> >> +	{ 0x370c, 0xc7 },
> >> +	{ 0x370d, 0x80 },
> >> +	{ 0x3712, 0x00 },
> >> +	{ 0x3713, 0x20 },
> >> +	{ 0x3715, 0x04 },
> >> +	{ 0x381d, 0x40 },
> >> +	{ 0x381c, 0x00 },
> >> +	{ 0x3822, 0x50 },
> >> +	{ 0x3824, 0x10 },
> >> +	{ 0x3815, 0x8c },
> >> +	{ 0x3804, 0x05 },
> >> +	{ 0x3805, 0x1f },
> >> +	{ 0x3800, 0x00 },
> >> +	{ 0x3801, 0x00 },
> >> +	{ 0x3806, 0x03 },
> >> +	{ 0x3807, 0x28 },
> >> +	{ 0x3802, 0x00 },
> >> +	{ 0x3803, 0x07 },
> >> +	{ 0x3808, 0x05 },
> >> +	{ 0x3809, 0x00 },
> >> +	{ 0x380a, 0x03 },
> >> +	{ 0x380b, 0x20 },
> >> +	{ 0x380c, OV10635_HTS >> 8 },
> >> +	{ 0x380d, OV10635_HTS & 0xff },
> >> +	{ 0x380e, OV10635_VTS >> 8 },
> >> +	{ 0x380f, OV10635_VTS & 0xff },
> >> +	{ 0x3813, 0x02 },
> >> +	{ 0x3811, 0x08 },
> >> +	{ 0x381f, 0x0c },
> >> +	{ 0x3819, 0x04 },
> >> +	{ 0x3804, 0x01 },
> >> +	{ 0x3805, 0x00 },
> >> +	{ 0x3828, 0x03 },
> >> +	{ 0x3829, 0x10 },
> >> +	{ 0x382a, 0x10 },
> >> +	{ 0x3621, 0x63 },
> >> +	{ 0x5005, 0x08 },
> >> +	{ 0x56d5, 0x00 },
> >> +	{ 0x56d6, 0x80 },
> >> +	{ 0x56d7, 0x00 },
> >> +	{ 0x56d8, 0x00 },
> >> +	{ 0x56d9, 0x00 },
> >> +	{ 0x56da, 0x80 },
> >> +	{ 0x56db, 0x00 },
> >> +	{ 0x56dc, 0x00 },
> >> +	{ 0x56e8, 0x00 },
> >> +	{ 0x56e9, 0x7f },
> >> +	{ 0x56ea, 0x00 },
> >> +	{ 0x56eb, 0x7f },
> >> +	{ 0x5100, 0x00 },
> >> +	{ 0x5101, 0x80 },
> >> +	{ 0x5102, 0x00 },
> >> +	{ 0x5103, 0x80 },
> >> +	{ 0x5104, 0x00 },
> >> +	{ 0x5105, 0x80 },
> >> +	{ 0x5106, 0x00 },
> >> +	{ 0x5107, 0x80 },
> >> +	{ 0x5108, 0x00 },
> >> +	{ 0x5109, 0x00 },
> >> +	{ 0x510a, 0x00 },
> >> +	{ 0x510b, 0x00 },
> >> +	{ 0x510c, 0x00 },
> >> +	{ 0x510d, 0x00 },
> >> +	{ 0x510e, 0x00 },
> >> +	{ 0x510f, 0x00 },
> >> +	{ 0x5110, 0x00 },
> >> +	{ 0x5111, 0x80 },
> >> +	{ 0x5112, 0x00 },
> >> +	{ 0x5113, 0x80 },
> >> +	{ 0x5114, 0x00 },
> >> +	{ 0x5115, 0x80 },
> >> +	{ 0x5116, 0x00 },
> >> +	{ 0x5117, 0x80 },
> >> +	{ 0x5118, 0x00 },
> >> +	{ 0x5119, 0x00 },
> >> +	{ 0x511a, 0x00 },
> >> +	{ 0x511b, 0x00 },
> >> +	{ 0x511c, 0x00 },
> >> +	{ 0x511d, 0x00 },
> >> +	{ 0x511e, 0x00 },
> >> +	{ 0x511f, 0x00 },
> >> +	{ 0x56d0, 0x00 },
> >> +	{ 0x5006, 0x04 },
> >> +	{ 0x5608, 0x05 },
> >> +	{ 0x52d7, 0x06 },
> >> +	{ 0x528d, 0x08 },
> >> +	{ 0x5293, 0x12 },
> >> +	{ 0x52d3, 0x12 },
> >> +	{ 0x5288, 0x06 },
> >> +	{ 0x5289, 0x20 },
> >> +	{ 0x52c8, 0x06 },
> >> +	{ 0x52c9, 0x20 },
> >> +	{ 0x52cd, 0x04 },
> >> +	{ 0x5381, 0x00 },
> >> +	{ 0x5382, 0xff },
> >> +	{ 0x5589, 0x76 },
> >> +	{ 0x558a, 0x47 },
> >> +	{ 0x558b, 0xef },
> >> +	{ 0x558c, 0xc9 },
> >> +	{ 0x558d, 0x49 },
> >> +	{ 0x558e, 0x30 },
> >> +	{ 0x558f, 0x67 },
> >> +	{ 0x5590, 0x3f },
> >> +	{ 0x5591, 0xf0 },
> >> +	{ 0x5592, 0x10 },
> >> +	{ 0x55a2, 0x6d },
> >> +	{ 0x55a3, 0x55 },
> >> +	{ 0x55a4, 0xc3 },
> >> +	{ 0x55a5, 0xb5 },
> >> +	{ 0x55a6, 0x43 },
> >> +	{ 0x55a7, 0x38 },
> >> +	{ 0x55a8, 0x5f },
> >> +	{ 0x55a9, 0x4b },
> >> +	{ 0x55aa, 0xf0 },
> >> +	{ 0x55ab, 0x10 },
> >> +	{ 0x5581, 0x52 },
> >> +	{ 0x5300, 0x01 },
> >> +	{ 0x5301, 0x00 },
> >> +	{ 0x5302, 0x00 },
> >> +	{ 0x5303, 0x0e },
> >> +	{ 0x5304, 0x00 },
> >> +	{ 0x5305, 0x0e },
> >> +	{ 0x5306, 0x00 },
> >> +	{ 0x5307, 0x36 },
> >> +	{ 0x5308, 0x00 },
> >> +	{ 0x5309, 0xd9 },
> >> +	{ 0x530a, 0x00 },
> >> +	{ 0x530b, 0x0f },
> >> +	{ 0x530c, 0x00 },
> >> +	{ 0x530d, 0x2c },
> >> +	{ 0x530e, 0x00 },
> >> +	{ 0x530f, 0x59 },
> >> +	{ 0x5310, 0x00 },
> >> +	{ 0x5311, 0x7b },
> >> +	{ 0x5312, 0x00 },
> >> +	{ 0x5313, 0x22 },
> >> +	{ 0x5314, 0x00 },
> >> +	{ 0x5315, 0xd5 },
> >> +	{ 0x5316, 0x00 },
> >> +	{ 0x5317, 0x13 },
> >> +	{ 0x5318, 0x00 },
> >> +	{ 0x5319, 0x18 },
> >> +	{ 0x531a, 0x00 },
> >> +	{ 0x531b, 0x26 },
> >> +	{ 0x531c, 0x00 },
> >> +	{ 0x531d, 0xdc },
> >> +	{ 0x531e, 0x00 },
> >> +	{ 0x531f, 0x02 },
> >> +	{ 0x5320, 0x00 },
> >> +	{ 0x5321, 0x24 },
> >> +	{ 0x5322, 0x00 },
> >> +	{ 0x5323, 0x56 },
> >> +	{ 0x5324, 0x00 },
> >> +	{ 0x5325, 0x85 },
> >> +	{ 0x5326, 0x00 },
> >> +	{ 0x5327, 0x20 },
> >> +	{ 0x5609, 0x01 },
> >> +	{ 0x560a, 0x40 },
> >> +	{ 0x560b, 0x01 },
> >> +	{ 0x560c, 0x40 },
> >> +	{ 0x560d, 0x00 },
> >> +	{ 0x560e, 0xfa },
> >> +	{ 0x560f, 0x00 },
> >> +	{ 0x5610, 0xfa },
> >> +	{ 0x5611, 0x02 },
> >> +	{ 0x5612, 0x80 },
> >> +	{ 0x5613, 0x02 },
> >> +	{ 0x5614, 0x80 },
> >> +	{ 0x5615, 0x01 },
> >> +	{ 0x5616, 0x2c },
> >> +	{ 0x5617, 0x01 },
> >> +	{ 0x5618, 0x2c },
> >> +	{ 0x563b, 0x01 },
> >> +	{ 0x563c, 0x01 },
> >> +	{ 0x563d, 0x01 },
> >> +	{ 0x563e, 0x01 },
> >> +	{ 0x563f, 0x03 },
> >> +	{ 0x5640, 0x03 },
> >> +	{ 0x5641, 0x03 },
> >> +	{ 0x5642, 0x05 },
> >> +	{ 0x5643, 0x09 },
> >> +	{ 0x5644, 0x05 },
> >> +	{ 0x5645, 0x05 },
> >> +	{ 0x5646, 0x05 },
> >> +	{ 0x5647, 0x05 },
> >> +	{ 0x5651, 0x00 },
> >> +	{ 0x5652, 0x80 },
> >> +	{ 0x521a, 0x01 },
> >> +	{ 0x521b, 0x03 },
> >> +	{ 0x521c, 0x06 },
> >> +	{ 0x521d, 0x0a },
> >> +	{ 0x521e, 0x0e },
> >> +	{ 0x521f, 0x12 },
> >> +	{ 0x5220, 0x16 },
> >> +	{ 0x5223, 0x02 },
> >> +	{ 0x5225, 0x04 },
> >> +	{ 0x5227, 0x08 },
> >> +	{ 0x5229, 0x0c },
> >> +	{ 0x522b, 0x12 },
> >> +	{ 0x522d, 0x18 },
> >> +	{ 0x522f, 0x1e },
> >> +	{ 0x5241, 0x04 },
> >> +	{ 0x5242, 0x01 },
> >> +	{ 0x5243, 0x03 },
> >> +	{ 0x5244, 0x06 },
> >> +	{ 0x5245, 0x0a },
> >> +	{ 0x5246, 0x0e },
> >> +	{ 0x5247, 0x12 },
> >> +	{ 0x5248, 0x16 },
> >> +	{ 0x524a, 0x03 },
> >> +	{ 0x524c, 0x04 },
> >> +	{ 0x524e, 0x08 },
> >> +	{ 0x5250, 0x0c },
> >> +	{ 0x5252, 0x12 },
> >> +	{ 0x5254, 0x18 },
> >> +	{ 0x5256, 0x1e },
> >> +	/* fifo_line_length = 2*hts */
> >> +	{ 0x4606, (2 * OV10635_HTS) >> 8 },
> >> +	{ 0x4607, (2 * OV10635_HTS) & 0xff },
> >> +	/* fifo_hsync_start = 2*(hts - xres) */
> >> +	{ 0x460a, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) >> 8 },
> >> +	{ 0x460b, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) & 0xff },
> >> +	{ 0x460c, 0x00 },
> >> +	{ 0x4620, 0x0e },
> >> +	/* BT601: 0x08 is also acceptable as HS/VS mode */
> >> +	{ 0x4700, 0x04 },
> >> +	{ 0x4701, 0x00 },
> >> +	{ 0x4702, 0x01 },
> >> +	{ 0x4004, 0x04 },
> >> +	{ 0x4005, 0x18 },
> >> +	{ 0x4001, 0x06 },
> >> +	{ 0x4050, 0x22 },
> >> +	{ 0x4051, 0x24 },
> >> +	{ 0x4052, 0x02 },
> >> +	{ 0x4057, 0x9c },
> >> +	{ 0x405a, 0x00 },
> >> +	{ 0x4202, 0x02 },
> >> +	{ 0x3023, 0x10 },
> >> +	{ 0x0100, 0x01 },
> >> +	{ 0x0100, 0x01 },
> >> +	{ 0x6f10, 0x07 },
> >> +	{ 0x6f11, 0x82 },
> >> +	{ 0x6f12, 0x04 },
> >> +	{ 0x6f13, 0x00 },
> >> +	{ 0xd000, 0x19 },
> >> +	{ 0xd001, 0xa0 },
> >> +	{ 0xd002, 0x00 },
> >> +	{ 0xd003, 0x01 },
> >> +	{ 0xd004, 0xa9 },
> >> +	{ 0xd005, 0xad },
> >> +	{ 0xd006, 0x10 },
> >> +	{ 0xd007, 0x40 },
> >> +	{ 0xd008, 0x44 },
> >> +	{ 0xd009, 0x00 },
> >> +	{ 0xd00a, 0x68 },
> >> +	{ 0xd00b, 0x00 },
> >> +	{ 0xd00c, 0x15 },
> >> +	{ 0xd00d, 0x00 },
> >> +	{ 0xd00e, 0x00 },
> >> +	{ 0xd00f, 0x00 },
> >> +	{ 0xd040, 0x9c },
> >> +	{ 0xd041, 0x21 },
> >> +	{ 0xd042, 0xff },
> >> +	{ 0xd043, 0xf8 },
> >> +	{ 0xd044, 0xd4 },
> >> +	{ 0xd045, 0x01 },
> >> +	{ 0xd046, 0x48 },
> >> +	{ 0xd047, 0x00 },
> >> +	{ 0xd048, 0xd4 },
> >> +	{ 0xd049, 0x01 },
> >> +	{ 0xd04a, 0x50 },
> >> +	{ 0xd04b, 0x04 },
> >> +	{ 0xd04c, 0x18 },
> >> +	{ 0xd04d, 0x60 },
> >> +	{ 0xd04e, 0x00 },
> >> +	{ 0xd04f, 0x01 },
> >> +	{ 0xd050, 0xa8 },
> >> +	{ 0xd051, 0x63 },
> >> +	{ 0xd052, 0x02 },
> >> +	{ 0xd053, 0xa4 },
> >> +	{ 0xd054, 0x85 },
> >> +	{ 0xd055, 0x43 },
> >> +	{ 0xd056, 0x00 },
> >> +	{ 0xd057, 0x00 },
> >> +	{ 0xd058, 0x18 },
> >> +	{ 0xd059, 0x60 },
> >> +	{ 0xd05a, 0x00 },
> >> +	{ 0xd05b, 0x01 },
> >> +	{ 0xd05c, 0xa8 },
> >> +	{ 0xd05d, 0x63 },
> >> +	{ 0xd05e, 0x03 },
> >> +	{ 0xd05f, 0xf0 },
> >> +	{ 0xd060, 0x98 },
> >> +	{ 0xd061, 0xa3 },
> >> +	{ 0xd062, 0x00 },
> >> +	{ 0xd063, 0x00 },
> >> +	{ 0xd064, 0x8c },
> >> +	{ 0xd065, 0x6a },
> >> +	{ 0xd066, 0x00 },
> >> +	{ 0xd067, 0x6e },
> >> +	{ 0xd068, 0xe5 },
> >> +	{ 0xd069, 0x85 },
> >> +	{ 0xd06a, 0x18 },
> >> +	{ 0xd06b, 0x00 },
> >> +	{ 0xd06c, 0x10 },
> >> +	{ 0xd06d, 0x00 },
> >> +	{ 0xd06e, 0x00 },
> >> +	{ 0xd06f, 0x10 },
> >> +	{ 0xd070, 0x9c },
> >> +	{ 0xd071, 0x80 },
> >> +	{ 0xd072, 0x00 },
> >> +	{ 0xd073, 0x03 },
> >> +	{ 0xd074, 0x18 },
> >> +	{ 0xd075, 0x60 },
> >> +	{ 0xd076, 0x00 },
> >> +	{ 0xd077, 0x01 },
> >> +	{ 0xd078, 0xa8 },
> >> +	{ 0xd079, 0x63 },
> >> +	{ 0xd07a, 0x07 },
> >> +	{ 0xd07b, 0x80 },
> >> +	{ 0xd07c, 0x07 },
> >> +	{ 0xd07d, 0xff },
> >> +	{ 0xd07e, 0xf9 },
> >> +	{ 0xd07f, 0x03 },
> >> +	{ 0xd080, 0x8c },
> >> +	{ 0xd081, 0x63 },
> >> +	{ 0xd082, 0x00 },
> >> +	{ 0xd083, 0x00 },
> >> +	{ 0xd084, 0xa5 },
> >> +	{ 0xd085, 0x6b },
> >> +	{ 0xd086, 0x00 },
> >> +	{ 0xd087, 0xff },
> >> +	{ 0xd088, 0x18 },
> >> +	{ 0xd089, 0x80 },
> >> +	{ 0xd08a, 0x00 },
> >> +	{ 0xd08b, 0x01 },
> >> +	{ 0xd08c, 0xa8 },
> >> +	{ 0xd08d, 0x84 },
> >> +	{ 0xd08e, 0x01 },
> >> +	{ 0xd08f, 0x04 },
> >> +	{ 0xd090, 0xe1 },
> >> +	{ 0xd091, 0x6b },
> >> +	{ 0xd092, 0x58 },
> >> +	{ 0xd093, 0x00 },
> >> +	{ 0xd094, 0x94 },
> >> +	{ 0xd095, 0x6a },
> >> +	{ 0xd096, 0x00 },
> >> +	{ 0xd097, 0x70 },
> >> +	{ 0xd098, 0xe1 },
> >> +	{ 0xd099, 0x6b },
> >> +	{ 0xd09a, 0x20 },
> >> +	{ 0xd09b, 0x00 },
> >> +	{ 0xd09c, 0x95 },
> >> +	{ 0xd09d, 0x6b },
> >> +	{ 0xd09e, 0x00 },
> >> +	{ 0xd09f, 0x00 },
> >> +	{ 0xd0a0, 0xe4 },
> >> +	{ 0xd0a1, 0x8b },
> >> +	{ 0xd0a2, 0x18 },
> >> +	{ 0xd0a3, 0x00 },
> >> +	{ 0xd0a4, 0x0c },
> >> +	{ 0xd0a5, 0x00 },
> >> +	{ 0xd0a6, 0x00 },
> >> +	{ 0xd0a7, 0x23 },
> >> +	{ 0xd0a8, 0x15 },
> >> +	{ 0xd0a9, 0x00 },
> >> +	{ 0xd0aa, 0x00 },
> >> +	{ 0xd0ab, 0x00 },
> >> +	{ 0xd0ac, 0x18 },
> >> +	{ 0xd0ad, 0x60 },
> >> +	{ 0xd0ae, 0x80 },
> >> +	{ 0xd0af, 0x06 },
> >> +	{ 0xd0b0, 0xa8 },
> >> +	{ 0xd0b1, 0x83 },
> >> +	{ 0xd0b2, 0x40 },
> >> +	{ 0xd0b3, 0x08 },
> >> +	{ 0xd0b4, 0xa8 },
> >> +	{ 0xd0b5, 0xe3 },
> >> +	{ 0xd0b6, 0x38 },
> >> +	{ 0xd0b7, 0x2a },
> >> +	{ 0xd0b8, 0xa8 },
> >> +	{ 0xd0b9, 0xc3 },
> >> +	{ 0xd0ba, 0x40 },
> >> +	{ 0xd0bb, 0x09 },
> >> +	{ 0xd0bc, 0xa8 },
> >> +	{ 0xd0bd, 0xa3 },
> >> +	{ 0xd0be, 0x38 },
> >> +	{ 0xd0bf, 0x29 },
> >> +	{ 0xd0c0, 0x8c },
> >> +	{ 0xd0c1, 0x65 },
> >> +	{ 0xd0c2, 0x00 },
> >> +	{ 0xd0c3, 0x00 },
> >> +	{ 0xd0c4, 0xd8 },
> >> +	{ 0xd0c5, 0x04 },
> >> +	{ 0xd0c6, 0x18 },
> >> +	{ 0xd0c7, 0x00 },
> >> +	{ 0xd0c8, 0x8c },
> >> +	{ 0xd0c9, 0x67 },
> >> +	{ 0xd0ca, 0x00 },
> >> +	{ 0xd0cb, 0x00 },
> >> +	{ 0xd0cc, 0xd8 },
> >> +	{ 0xd0cd, 0x06 },
> >> +	{ 0xd0ce, 0x18 },
> >> +	{ 0xd0cf, 0x00 },
> >> +	{ 0xd0d0, 0x18 },
> >> +	{ 0xd0d1, 0x60 },
> >> +	{ 0xd0d2, 0x80 },
> >> +	{ 0xd0d3, 0x06 },
> >> +	{ 0xd0d4, 0xa8 },
> >> +	{ 0xd0d5, 0xe3 },
> >> +	{ 0xd0d6, 0x67 },
> >> +	{ 0xd0d7, 0x02 },
> >> +	{ 0xd0d8, 0xa9 },
> >> +	{ 0xd0d9, 0x03 },
> >> +	{ 0xd0da, 0x67 },
> >> +	{ 0xd0db, 0x03 },
> >> +	{ 0xd0dc, 0xa8 },
> >> +	{ 0xd0dd, 0xc3 },
> >> +	{ 0xd0de, 0x3d },
> >> +	{ 0xd0df, 0x05 },
> >> +	{ 0xd0e0, 0x8c },
> >> +	{ 0xd0e1, 0x66 },
> >> +	{ 0xd0e2, 0x00 },
> >> +	{ 0xd0e3, 0x00 },
> >> +	{ 0xd0e4, 0xb8 },
> >> +	{ 0xd0e5, 0x63 },
> >> +	{ 0xd0e6, 0x00 },
> >> +	{ 0xd0e7, 0x18 },
> >> +	{ 0xd0e8, 0xb8 },
> >> +	{ 0xd0e9, 0x63 },
> >> +	{ 0xd0ea, 0x00 },
> >> +	{ 0xd0eb, 0x98 },
> >> +	{ 0xd0ec, 0xbc },
> >> +	{ 0xd0ed, 0x03 },
> >> +	{ 0xd0ee, 0x00 },
> >> +	{ 0xd0ef, 0x00 },
> >> +	{ 0xd0f0, 0x10 },
> >> +	{ 0xd0f1, 0x00 },
> >> +	{ 0xd0f2, 0x00 },
> >> +	{ 0xd0f3, 0x16 },
> >> +	{ 0xd0f4, 0xb8 },
> >> +	{ 0xd0f5, 0x83 },
> >> +	{ 0xd0f6, 0x00 },
> >> +	{ 0xd0f7, 0x19 },
> >> +	{ 0xd0f8, 0x8c },
> >> +	{ 0xd0f9, 0x67 },
> >> +	{ 0xd0fa, 0x00 },
> >> +	{ 0xd0fb, 0x00 },
> >> +	{ 0xd0fc, 0xb8 },
> >> +	{ 0xd0fd, 0xa4 },
> >> +	{ 0xd0fe, 0x00 },
> >> +	{ 0xd0ff, 0x98 },
> >> +	{ 0xd100, 0xb8 },
> >> +	{ 0xd101, 0x83 },
> >> +	{ 0xd102, 0x00 },
> >> +	{ 0xd103, 0x08 },
> >> +	{ 0xd104, 0x8c },
> >> +	{ 0xd105, 0x68 },
> >> +	{ 0xd106, 0x00 },
> >> +	{ 0xd107, 0x00 },
> >> +	{ 0xd108, 0xe0 },
> >> +	{ 0xd109, 0x63 },
> >> +	{ 0xd10a, 0x20 },
> >> +	{ 0xd10b, 0x04 },
> >> +	{ 0xd10c, 0xe0 },
> >> +	{ 0xd10d, 0x65 },
> >> +	{ 0xd10e, 0x18 },
> >> +	{ 0xd10f, 0x00 },
> >> +	{ 0xd110, 0xa4 },
> >> +	{ 0xd111, 0x83 },
> >> +	{ 0xd112, 0xff },
> >> +	{ 0xd113, 0xff },
> >> +	{ 0xd114, 0xb8 },
> >> +	{ 0xd115, 0x64 },
> >> +	{ 0xd116, 0x00 },
> >> +	{ 0xd117, 0x48 },
> >> +	{ 0xd118, 0xd8 },
> >> +	{ 0xd119, 0x07 },
> >> +	{ 0xd11a, 0x18 },
> >> +	{ 0xd11b, 0x00 },
> >> +	{ 0xd11c, 0xd8 },
> >> +	{ 0xd11d, 0x08 },
> >> +	{ 0xd11e, 0x20 },
> >> +	{ 0xd11f, 0x00 },
> >> +	{ 0xd120, 0x9c },
> >> +	{ 0xd121, 0x60 },
> >> +	{ 0xd122, 0x00 },
> >> +	{ 0xd123, 0x00 },
> >> +	{ 0xd124, 0xd8 },
> >> +	{ 0xd125, 0x06 },
> >> +	{ 0xd126, 0x18 },
> >> +	{ 0xd127, 0x00 },
> >> +	{ 0xd128, 0x00 },
> >> +	{ 0xd129, 0x00 },
> >> +	{ 0xd12a, 0x00 },
> >> +	{ 0xd12b, 0x08 },
> >> +	{ 0xd12c, 0x15 },
> >> +	{ 0xd12d, 0x00 },
> >> +	{ 0xd12e, 0x00 },
> >> +	{ 0xd12f, 0x00 },
> >> +	{ 0xd130, 0x8c },
> >> +	{ 0xd131, 0x6a },
> >> +	{ 0xd132, 0x00 },
> >> +	{ 0xd133, 0x76 },
> >> +	{ 0xd134, 0xbc },
> >> +	{ 0xd135, 0x23 },
> >> +	{ 0xd136, 0x00 },
> >> +	{ 0xd137, 0x00 },
> >> +	{ 0xd138, 0x13 },
> >> +	{ 0xd139, 0xff },
> >> +	{ 0xd13a, 0xff },
> >> +	{ 0xd13b, 0xe6 },
> >> +	{ 0xd13c, 0x18 },
> >> +	{ 0xd13d, 0x60 },
> >> +	{ 0xd13e, 0x80 },
> >> +	{ 0xd13f, 0x06 },
> >> +	{ 0xd140, 0x03 },
> >> +	{ 0xd141, 0xff },
> >> +	{ 0xd142, 0xff },
> >> +	{ 0xd143, 0xdd },
> >> +	{ 0xd144, 0xa8 },
> >> +	{ 0xd145, 0x83 },
> >> +	{ 0xd146, 0x40 },
> >> +	{ 0xd147, 0x08 },
> >> +	{ 0xd148, 0x85 },
> >> +	{ 0xd149, 0x21 },
> >> +	{ 0xd14a, 0x00 },
> >> +	{ 0xd14b, 0x00 },
> >> +	{ 0xd14c, 0x85 },
> >> +	{ 0xd14d, 0x41 },
> >> +	{ 0xd14e, 0x00 },
> >> +	{ 0xd14f, 0x04 },
> >> +	{ 0xd150, 0x44 },
> >> +	{ 0xd151, 0x00 },
> >> +	{ 0xd152, 0x48 },
> >> +	{ 0xd153, 0x00 },
> >> +	{ 0xd154, 0x9c },
> >> +	{ 0xd155, 0x21 },
> >> +	{ 0xd156, 0x00 },
> >> +	{ 0xd157, 0x08 },
> >> +	{ 0x6f0e, 0x03 },
> >> +	{ 0x6f0f, 0x00 },
> >> +	{ 0x460e, 0x08 },
> >> +	{ 0x460f, 0x01 },
> >> +	{ 0x4610, 0x00 },
> >> +	{ 0x4611, 0x01 },
> >> +	{ 0x4612, 0x00 },
> >> +	{ 0x4613, 0x01 },
> >> +	/* 8 bits */
> >> +	{ 0x4605, 0x08 },
> >> +	/* Swap data bits order [9:0] -> [0:9] */
> >> +	{ 0x4709, 0x10 },
> >> +	{ 0x4608, 0x00 },
> >> +	{ 0x4609, 0x08 },
> >> +	{ 0x6804, 0x00 },
> >> +	{ 0x6805, 0x06 },
> >> +	{ 0x6806, 0x00 },
> >> +	{ 0x5120, 0x00 },
> >> +	{ 0x3510, 0x00 },
> >> +	{ 0x3504, 0x00 },
> >> +	{ 0x6800, 0x00 },
> >> +	{ 0x6f0d, 0x01 },
> >> +	/* PCLK falling edge */
> >> +	{ 0x4708, 0x01 },
> >> +	{ 0x5000, 0xff },
> >> +	{ 0x5001, 0xbf },
> >> +	{ 0x5002, 0x7e },
> >> +	{ 0x503d, 0x00 },
> >> +	{ 0xc450, 0x01 },
> >> +	{ 0xc452, 0x04 },
> >> +	{ 0xc453, 0x00 },
> >> +	{ 0xc454, 0x00 },
> >> +	{ 0xc455, 0x01 },
> >> +	{ 0xc456, 0x01 },
> >> +	{ 0xc457, 0x00 },
> >> +	{ 0xc458, 0x00 },
> >> +	{ 0xc459, 0x00 },
> >> +	{ 0xc45b, 0x00 },
> >> +	{ 0xc45c, 0x01 },
> >> +	{ 0xc45d, 0x00 },
> >> +	{ 0xc45e, 0x00 },
> >> +	{ 0xc45f, 0x00 },
> >> +	{ 0xc460, 0x00 },
> >> +	{ 0xc461, 0x01 },
> >> +	{ 0xc462, 0x01 },
> >> +	{ 0xc464, 0x03 },
> >> +	{ 0xc465, 0x00 },
> >> +	{ 0xc466, 0x8a },
> >> +	{ 0xc467, 0x00 },
> >> +	{ 0xc468, 0x86 },
> >> +	{ 0xc469, 0x00 },
> >> +	{ 0xc46a, 0x40 },
> >> +	{ 0xc46b, 0x50 },
> >> +	{ 0xc46c, 0x30 },
> >> +	{ 0xc46d, 0x28 },
> >> +	{ 0xc46e, 0x60 },
> >> +	{ 0xc46f, 0x40 },
> >> +	{ 0xc47c, 0x01 },
> >> +	{ 0xc47d, 0x38 },
> >> +	{ 0xc47e, 0x00 },
> >> +	{ 0xc47f, 0x00 },
> >> +	{ 0xc480, 0x00 },
> >> +	{ 0xc481, 0xff },
> >> +	{ 0xc482, 0x00 },
> >> +	{ 0xc483, 0x40 },
> >> +	{ 0xc484, 0x00 },
> >> +	{ 0xc485, 0x18 },
> >> +	{ 0xc486, 0x00 },
> >> +	{ 0xc487, 0x18 },
> >> +	{ 0xc488, (OV10635_VTS - 8) * 16 >> 8},
> >> +	{ 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
> >> +	{ 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
> >> +	{ 0xc48b, (OV10635_VTS - 8) * 16 & 0xff},
> >> +	{ 0xc48c, 0x00 },
> >> +	{ 0xc48d, 0x04 },
> >> +	{ 0xc48e, 0x00 },
> >> +	{ 0xc48f, 0x04 },
> >> +	{ 0xc490, 0x03 },
> >> +	{ 0xc492, 0x20 },
> >> +	{ 0xc493, 0x08 },
> >> +	{ 0xc498, 0x02 },
> >> +	{ 0xc499, 0x00 },
> >> +	{ 0xc49a, 0x02 },
> >> +	{ 0xc49b, 0x00 },
> >> +	{ 0xc49c, 0x02 },
> >> +	{ 0xc49d, 0x00 },
> >> +	{ 0xc49e, 0x02 },
> >> +	{ 0xc49f, 0x60 },
> >> +	{ 0xc4a0, 0x03 },
> >> +	{ 0xc4a1, 0x00 },
> >> +	{ 0xc4a2, 0x04 },
> >> +	{ 0xc4a3, 0x00 },
> >> +	{ 0xc4a4, 0x00 },
> >> +	{ 0xc4a5, 0x10 },
> >> +	{ 0xc4a6, 0x00 },
> >> +	{ 0xc4a7, 0x40 },
> >> +	{ 0xc4a8, 0x00 },
> >> +	{ 0xc4a9, 0x80 },
> >> +	{ 0xc4aa, 0x0d },
> >> +	{ 0xc4ab, 0x00 },
> >> +	{ 0xc4ac, 0x0f },
> >> +	{ 0xc4ad, 0xc0 },
> >> +	{ 0xc4b4, 0x01 },
> >> +	{ 0xc4b5, 0x01 },
> >> +	{ 0xc4b6, 0x00 },
> >> +	{ 0xc4b7, 0x01 },
> >> +	{ 0xc4b8, 0x00 },
> >> +	{ 0xc4b9, 0x01 },
> >> +	{ 0xc4ba, 0x01 },
> >> +	{ 0xc4bb, 0x00 },
> >> +	{ 0xc4bc, 0x01 },
> >> +	{ 0xc4bd, 0x60 },
> >> +	{ 0xc4be, 0x02 },
> >> +	{ 0xc4bf, 0x33 },
> >> +	{ 0xc4c8, 0x03 },
> >> +	{ 0xc4c9, 0xd0 },
> >> +	{ 0xc4ca, 0x0e },
> >> +	{ 0xc4cb, 0x00 },
> >> +	{ 0xc4cc, 0x0e },
> >> +	{ 0xc4cd, 0x51 },
> >> +	{ 0xc4ce, 0x0e },
> >> +	{ 0xc4cf, 0x51 },
> >> +	{ 0xc4d0, 0x04 },
> >> +	{ 0xc4d1, 0x80 },
> >> +	{ 0xc4e0, 0x04 },
> >> +	{ 0xc4e1, 0x02 },
> >> +	{ 0xc4e2, 0x01 },
> >> +	{ 0xc4e4, 0x10 },
> >> +	{ 0xc4e5, 0x20 },
> >> +	{ 0xc4e6, 0x30 },
> >> +	{ 0xc4e7, 0x40 },
> >> +	{ 0xc4e8, 0x50 },
> >> +	{ 0xc4e9, 0x60 },
> >> +	{ 0xc4ea, 0x70 },
> >> +	{ 0xc4eb, 0x80 },
> >> +	{ 0xc4ec, 0x90 },
> >> +	{ 0xc4ed, 0xa0 },
> >> +	{ 0xc4ee, 0xb0 },
> >> +	{ 0xc4ef, 0xc0 },
> >> +	{ 0xc4f0, 0xd0 },
> >> +	{ 0xc4f1, 0xe0 },
> >> +	{ 0xc4f2, 0xf0 },
> >> +	{ 0xc4f3, 0x80 },
> >> +	{ 0xc4f4, 0x00 },
> >> +	{ 0xc4f5, 0x20 },
> >> +	{ 0xc4f6, 0x02 },
> >> +	{ 0xc4f7, 0x00 },
> >> +	{ 0xc4f8, 0x00 },
> >> +	{ 0xc4f9, 0x00 },
> >> +	{ 0xc4fa, 0x00 },
> >> +	{ 0xc4fb, 0x01 },
> >> +	{ 0xc4fc, 0x01 },
> >> +	{ 0xc4fd, 0x00 },
> >> +	{ 0xc4fe, 0x04 },
> >> +	{ 0xc4ff, 0x02 },
> >> +	{ 0xc500, 0x48 },
> >> +	{ 0xc501, 0x74 },
> >> +	{ 0xc502, 0x58 },
> >> +	{ 0xc503, 0x80 },
> >> +	{ 0xc504, 0x05 },
> >> +	{ 0xc505, 0x80 },
> >> +	{ 0xc506, 0x03 },
> >> +	{ 0xc507, 0x80 },
> >> +	{ 0xc508, 0x01 },
> >> +	{ 0xc509, 0xc0 },
> >> +	{ 0xc50a, 0x01 },
> >> +	{ 0xc50b, 0xa0 },
> >> +	{ 0xc50c, 0x01 },
> >> +	{ 0xc50d, 0x2c },
> >> +	{ 0xc50e, 0x01 },
> >> +	{ 0xc50f, 0x0a },
> >> +	{ 0xc510, 0x00 },
> >> +	{ 0xc511, 0x00 },
> >> +	{ 0xc512, 0xe5 },
> >> +	{ 0xc513, 0x14 },
> >> +	{ 0xc514, 0x04 },
> >> +	{ 0xc515, 0x00 },
> >> +	{ 0xc518, OV10635_VTS >> 8},
> >> +	{ 0xc519, OV10635_VTS & 0xff},
> >> +	{ 0xc51a, OV10635_HTS >> 8},
> >> +	{ 0xc51b, OV10635_HTS & 0xff},
> >> +	{ 0xc2e0, 0x00 },
> >> +	{ 0xc2e1, 0x51 },
> >> +	{ 0xc2e2, 0x00 },
> >> +	{ 0xc2e3, 0xd6 },
> >> +	{ 0xc2e4, 0x01 },
> >> +	{ 0xc2e5, 0x5e },
> >> +	{ 0xc2e9, 0x01 },
> >> +	{ 0xc2ea, 0x7a },
> >> +	{ 0xc2eb, 0x90 },
> >> +	{ 0xc2ed, 0x00 },
> >> +	{ 0xc2ee, 0x7a },
> >> +	{ 0xc2ef, 0x64 },
> >> +	{ 0xc308, 0x00 },
> >> +	{ 0xc309, 0x00 },
> >> +	{ 0xc30a, 0x00 },
> >> +	{ 0xc30c, 0x00 },
> >> +	{ 0xc30d, 0x01 },
> >> +	{ 0xc30e, 0x00 },
> >> +	{ 0xc30f, 0x00 },
> >> +	{ 0xc310, 0x01 },
> >> +	{ 0xc311, 0x60 },
> >> +	{ 0xc312, 0xff },
> >> +	{ 0xc313, 0x08 },
> >> +	{ 0xc314, 0x01 },
> >> +	{ 0xc315, 0x00 },
> >> +	{ 0xc316, 0xff },
> >> +	{ 0xc317, 0x0b },
> >> +	{ 0xc318, 0x00 },
> >> +	{ 0xc319, 0x0c },
> >> +	{ 0xc31a, 0x00 },
> >> +	{ 0xc31b, 0xe0 },
> >> +	{ 0xc31c, 0x00 },
> >> +	{ 0xc31d, 0x14 },
> >> +	{ 0xc31e, 0x00 },
> >> +	{ 0xc31f, 0xc5 },
> >> +	{ 0xc320, 0xff },
> >> +	{ 0xc321, 0x4b },
> >> +	{ 0xc322, 0xff },
> >> +	{ 0xc323, 0xf0 },
> >> +	{ 0xc324, 0xff },
> >> +	{ 0xc325, 0xe8 },
> >> +	{ 0xc326, 0x00 },
> >> +	{ 0xc327, 0x46 },
> >> +	{ 0xc328, 0xff },
> >> +	{ 0xc329, 0xd2 },
> >> +	{ 0xc32a, 0xff },
> >> +	{ 0xc32b, 0xe4 },
> >> +	{ 0xc32c, 0xff },
> >> +	{ 0xc32d, 0xbb },
> >> +	{ 0xc32e, 0x00 },
> >> +	{ 0xc32f, 0x61 },
> >> +	{ 0xc330, 0xff },
> >> +	{ 0xc331, 0xf9 },
> >> +	{ 0xc332, 0x00 },
> >> +	{ 0xc333, 0xd9 },
> >> +	{ 0xc334, 0x00 },
> >> +	{ 0xc335, 0x2e },
> >> +	{ 0xc336, 0x00 },
> >> +	{ 0xc337, 0xb1 },
> >> +	{ 0xc338, 0xff },
> >> +	{ 0xc339, 0x64 },
> >> +	{ 0xc33a, 0xff },
> >> +	{ 0xc33b, 0xeb },
> >> +	{ 0xc33c, 0xff },
> >> +	{ 0xc33d, 0xe8 },
> >> +	{ 0xc33e, 0x00 },
> >> +	{ 0xc33f, 0x48 },
> >> +	{ 0xc340, 0xff },
> >> +	{ 0xc341, 0xd0 },
> >> +	{ 0xc342, 0xff },
> >> +	{ 0xc343, 0xed },
> >> +	{ 0xc344, 0xff },
> >> +	{ 0xc345, 0xad },
> >> +	{ 0xc346, 0x00 },
> >> +	{ 0xc347, 0x66 },
> >> +	{ 0xc348, 0x01 },
> >> +	{ 0xc349, 0x00 },
> >> +	{ 0x6700, 0x04 },
> >> +	{ 0x6701, 0x7b },
> >> +	{ 0x6702, 0xfd },
> >> +	{ 0x6703, 0xf9 },
> >> +	{ 0x6704, 0x3d },
> >> +	{ 0x6705, 0x71 },
> >> +	{ 0x6706, 0x78 },
> >> +	{ 0x6708, 0x05 },
> >> +	{ 0x6f06, 0x6f },
> >> +	{ 0x6f07, 0x00 },
> >> +	{ 0x6f0a, 0x6f },
> >> +	{ 0x6f0b, 0x00 },
> >> +	{ 0x6f00, 0x03 },
> >> +	{ 0xc34c, 0x01 },
> >> +	{ 0xc34d, 0x00 },
> >> +	{ 0xc34e, 0x46 },
> >> +	{ 0xc34f, 0x55 },
> >> +	{ 0xc350, 0x00 },
> >> +	{ 0xc351, 0x40 },
> >> +	{ 0xc352, 0x00 },
> >> +	{ 0xc353, 0xff },
> >> +	{ 0xc354, 0x04 },
> >> +	{ 0xc355, 0x08 },
> >> +	{ 0xc356, 0x01 },
> >> +	{ 0xc357, 0xef },
> >> +	{ 0xc358, 0x30 },
> >> +	{ 0xc359, 0x01 },
> >> +	{ 0xc35a, 0x64 },
> >> +	{ 0xc35b, 0x46 },
> >> +	{ 0xc35c, 0x00 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0x3042, 0xf0 },
> >> +	{ 0xc261, 0x01 },
> >> +	{ 0x301b, 0xf0 },
> >> +	{ 0x301c, 0xf0 },
> >> +	{ 0x301a, 0xf0 },
> >> +	{ 0x6f00, 0xc3 },
> >> +	{ 0xc46a, 0x30 },
> >> +	{ 0xc46d, 0x20 },
> >> +	{ 0xc464, 0x84 },
> >> +	{ 0xc465, 0x00 },
> >> +	{ 0x6f00, 0x03 },
> >> +	{ 0x6f00, 0x43 },
> >> +	{ 0x381c, 0x00 },
> >> +	{ 0x381d, 0x40 },
> >> +	{ 0xc454, 0x01 },
> >> +	{ 0x6f00, 0xc3 },
> >> +	{ 0xc454, 0x00 },
> >> +	{ 0xc4b1, 0x02 },
> >> +	{ 0xc4b2, 0x01 },
> >> +	{ 0xc4b3, 0x03 },
> >> +	{ 0x6f00, 0x03 },
> >> +	{ 0x6f00, 0x43 },
> >> +	/* enable FSIN (FRAMESYNC input) functionality */
> >> +	{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
> >> +	{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
> >> +	{ 0x3834, OV10635_VTS >> 8 },
> >> +	{ 0x3835, OV10635_VTS & 0xff },
> >> +	{ 0x302e, 0x01 },
> >> +};
> >> +
> >> +#endif /* __RDACM20_OV10635_H__ */
> >> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> >> new file mode 100644
> >> index 000000000000..d96b2eb5ab1b
> >> --- /dev/null
> >> +++ b/drivers/media/i2c/rdacm20.c
> >> @@ -0,0 +1,635 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * IMI RDACM20 GMSL Camera Driver
> >> + *
> >> + * Copyright (C) 2017-2018 Jacopo Mondi
> >> + * Copyright (C) 2017-2018 Kieran Bingham
> >> + * Copyright (C) 2017-2018 Laurent Pinchart
> >> + * Copyright (C) 2017-2018 Niklas Söderlund
> >> + * Copyright (C) 2016 Renesas Electronics Corporation
> >> + * Copyright (C) 2015 Cogent Embedded, Inc.
> >> + */
> >> +
> >> +/*
> >> + * The camera is mode of an Omnivision OV10635 sensor connected to a Maxim
> >> + * MAX9271 GMSL serializer.
> >> + */
> >> +
> >> +#include <linux/delay.h>
> >> +#include <linux/fwnode.h>
> >> +#include <linux/init.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/module.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/videodev2.h>
> >> +
> >> +#include <media/v4l2-async.h>
> >> +#include <media/v4l2-ctrls.h>
> >> +#include <media/v4l2-subdev.h>
> >> +
> >> +#include "rdacm20-ov10635.h"
> >> +
> >> +#define RDACM20_SENSOR_HARD_RESET
> >> +
> >> +#define MAX9271_I2C_ADDRESS		0x40
> >> +
> >> +/* Register 0x04 */
> >> +#define MAX9271_SEREN			BIT(7)
> >> +#define MAX9271_CLINKEN			BIT(6)
> >> +#define MAX9271_PRBSEN			BIT(5)
> >> +#define MAX9271_SLEEP			BIT(4)
> >> +#define MAX9271_INTTYPE_I2C		(0 << 2)
> >> +#define MAX9271_INTTYPE_UART		(1 << 2)
> >> +#define MAX9271_INTTYPE_NONE		(2 << 2)
> >> +#define MAX9271_REVCCEN			BIT(1)
> >> +#define MAX9271_FWDCCEN			BIT(0)
> >> +/* Register 0x07 */
> >> +#define MAX9271_DBL			BIT(7)
> >> +#define MAX9271_DRS			BIT(6)
> >> +#define MAX9271_BWS			BIT(5)
> >> +#define MAX9271_ES			BIT(4)
> >> +#define MAX9271_HVEN			BIT(2)
> >> +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> >> +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> >> +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> >> +/* Register 0x08 */
> >> +#define MAX9271_INVVS			BIT(7)
> >> +#define MAX9271_INVHS			BIT(6)
> >> +#define MAX9271_REV_LOGAIN		BIT(3)
> >> +#define MAX9271_REV_HIVTH		BIT(0)
> >> +/* Register 0x09 */
> >> +#define MAX9271_ID			0x09
> >> +/* Register 0x0d */
> >> +#define MAX9271_I2CLOCACK		BIT(7)
> >> +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> >> +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> >> +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> >> +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> >> +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> >> +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> >> +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> >> +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> >> +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> >> +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> >> +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> >> +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> >> +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> >> +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> >> +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> >> +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> >> +/* Register 0x0f */
> >> +#define MAX9271_GPIO5OUT		BIT(5)
> >> +#define MAX9271_GPIO4OUT		BIT(4)
> >> +#define MAX9271_GPIO3OUT		BIT(3)
> >> +#define MAX9271_GPIO2OUT		BIT(2)
> >> +#define MAX9271_GPIO1OUT		BIT(1)
> >> +#define MAX9271_SETGPO			BIT(0)
> >> +/* Register 0x15 */
> >> +#define MAX9271_PCLKDET			BIT(0)
> >> +
> >> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
> >> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
> >> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
> >> +
> >> +#define OV10635_I2C_ADDRESS		0x30
> >> +
> >> +#define OV10635_SOFTWARE_RESET		0x0103
> >> +#define OV10635_PID			0x300a
> >> +#define OV10635_VER			0x300b
> >> +#define OV10635_SC_CMMN_SCCB_ID		0x300c
> >> +#define OV10635_SC_CMMN_SCCB_ID_SELECT	BIT(0)
> >> +#define OV10635_VERSION			0xa635
> >> +
> >> +#define OV10635_WIDTH			1280
> >> +#define OV10635_HEIGHT			800
> >> +#define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
> >> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
> >> +
> >> +struct rdacm20_device {
> >> +	struct i2c_client		*client;
> >> +	struct i2c_client		*sensor;
> >> +	struct v4l2_subdev		sd;
> >> +	struct media_pad		pad;
> >> +	struct v4l2_ctrl_handler	ctrls;
> >> +};
> >> +
> >> +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
> >> +{
> >> +	return container_of(sd, struct rdacm20_device, sd);
> >> +}
> >> +
> >> +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
> >> +{
> >> +	return sd_to_rdacm20(i2c_get_clientdata(client));
> >> +}
> >> +
> >> +static int max9271_read(struct rdacm20_device *dev, u8 reg)
> >> +{
> >> +	int ret;
> >> +
> >> +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> >> +
> >> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> >> +	if (ret < 0)
> >> +		dev_dbg(&dev->client->dev,
> >> +			"%s: register 0x%02x read failed (%d)\n",
> >> +			__func__, reg, ret);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int max9271_write(struct rdacm20_device *dev, u8 reg, u8 val)
> >> +{
> >> +	int ret;
> >> +
> >> +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> >> +
> >> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> >> +	if (ret < 0)
> >> +		dev_err(&dev->client->dev,
> >> +			"%s: register 0x%02x write failed (%d)\n",
> >> +			__func__, reg, ret);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
> >> +{
> >> +	u8 buf[2] = { reg >> 8, reg & 0xff };
> >> +	int ret;
> >> +
> >> +	ret = i2c_master_send(dev->sensor, buf, 2);
> >> +	if (ret == 2)
> >> +		ret = i2c_master_recv(dev->sensor, buf, 2);
> >> +
> >> +	if (ret < 0) {
> >> +		dev_dbg(&dev->client->dev,
> >> +			"%s: register 0x%04x read failed (%d)\n",
> >> +			__func__, reg, ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return (buf[0] << 8) | buf[1];
> >> +}
> >> +
> >> +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> >> +{
> >> +	u8 buf[3] = { reg >> 8, reg & 0xff, val };
> >> +	int ret;
> >> +
> >> +	dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
> >> +
> >> +	ret = i2c_master_send(dev->sensor, buf, 3);
> >> +	return ret < 0 ? ret : 0;
> >> +}
> >> +
> >> +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> >> +{
> >> +	int ret;
> >> +
> >> +	ret = __ov10635_write(dev, reg, val);
> >> +	if (ret < 0)
> >> +		dev_err(&dev->client->dev,
> >> +			"%s: register 0x%04x write failed (%d)\n",
> >> +			__func__, reg, ret);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int ov10635_set_regs(struct rdacm20_device *dev,
> >> +			    const struct ov10635_reg *regs,
> >> +			    unsigned int nr_regs)
> >> +{
> >> +	unsigned int i;
> >> +	int ret;
> >> +
> >> +	for (i = 0; i < nr_regs; i++) {
> >> +		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
> >> +		if (ret) {
> >> +			dev_err(&dev->client->dev,
> >> +				"%s: register %u (0x%04x) write failed (%d)\n",
> >> +				__func__, i, regs[i].reg, ret);
> >> +			return ret;
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/*
> >> + * rdacm20_pclk_detect() - Detect valid pixel clock from image sensor
> >> + *
> >> + * Wait up to 10ms for a valid pixel clock.
> >> + *
> >> + * Returns 0 for success, < 0 for pixel clock not properly detected
> >> + */
> >> +static int rdacm20_pclk_detect(struct rdacm20_device *dev)
> >> +{
> >> +	unsigned int i;
> >> +	int ret;
> >> +
> >> +	for (i = 0; i < 100; i++) {
> >> +		ret = max9271_read(dev, 0x15);
> >> +		if (ret < 0)
> >> +			return ret;
> >> +
> >> +		if (ret & MAX9271_PCLKDET)
> >> +			return 0;
> >> +
> >> +		usleep_range(50, 100);
> >> +	}
> >> +
> >> +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> >> +	return -EIO;
> >> +}
> >> +
> >> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
> >> +{
> >> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
> >> +	int ret;
> >> +
> >> +	if (enable) {
> >> +		ret = rdacm20_pclk_detect(dev);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Enable the serial link. */
> >> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
> >> +			      MAX9271_FWDCCEN);
> >> +	} else {
> >> +		/* Disable the serial link. */
> >> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> >> +			      MAX9271_FWDCCEN);
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
> >> +				 struct v4l2_mbus_config *cfg)
> >> +{
> >> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
> >> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
> >> +	cfg->type = V4L2_MBUS_CSI2_DPHY;
> > 
> > Hmm. What are you using g_mbus_config() for?
> 
> Good point here ... I assumed it was passed through up to the VIN - but
> it's really not applicable here.
> 
> Or if it is - then it should be describing the GMSL bus link!
> 
> I'll bet this isn't even getting called and can likely be removed.

There are two use cases I know for this --- SoC camera and something that
changes dynamically. The former is obsolete and the latter is better
addressed by the frame descriptors I'd like to see go in for 4.22.

> 
> 
> > 
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
> >> +				  struct v4l2_subdev_pad_config *cfg,
> >> +				  struct v4l2_subdev_mbus_code_enum *code)
> >> +{
> >> +	if (code->pad || code->index > 0)
> >> +		return -EINVAL;
> >> +
> >> +	code->code = OV10635_FORMAT;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_get_fmt(struct v4l2_subdev *sd,
> >> +			   struct v4l2_subdev_pad_config *cfg,
> >> +			   struct v4l2_subdev_format *format)
> >> +{
> >> +	struct v4l2_mbus_framefmt *mf = &format->format;
> >> +
> >> +	if (format->pad)
> >> +		return -EINVAL;
> >> +
> >> +	mf->width		= OV10635_WIDTH;
> >> +	mf->height		= OV10635_HEIGHT;
> >> +	mf->code		= OV10635_FORMAT;
> >> +	mf->colorspace		= V4L2_COLORSPACE_RAW;
> >> +	mf->field		= V4L2_FIELD_NONE;
> >> +	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;
> >> +	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;
> >> +	mf->xfer_func		= V4L2_XFER_FUNC_NONE;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct v4l2_subdev_video_ops rdacm20_video_ops = {
> >> +	.s_stream	= rdacm20_s_stream,
> >> +	.g_mbus_config	= rdacm20_g_mbus_config,
> >> +};
> >> +
> >> +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
> >> +	.enum_mbus_code = rdacm20_enum_mbus_code,
> >> +	.get_fmt	= rdacm20_get_fmt,
> >> +	.set_fmt	= rdacm20_get_fmt,
> >> +};
> >> +
> >> +static struct v4l2_subdev_ops rdacm20_subdev_ops = {
> >> +	.video		= &rdacm20_video_ops,
> >> +	.pad		= &rdacm20_subdev_pad_ops,
> >> +};
> >> +
> >> +static int max9271_configure_i2c(struct rdacm20_device *dev)
> >> +{
> >> +	/*
> >> +	 * Configure the I2C bus:
> >> +	 *
> >> +	 * - Enable high thresholds on the reverse channel
> >> +	 * - Disable artificial ACK and set I2C speed
> >> +	 */
> >> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
> >> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int max9271_configure_gmsl_link(struct rdacm20_device *dev)
> >> +{
> >> +	/*
> >> +	 * Disable the serial link and enable the configuration link to allow
> >> +	 * the control channel to operate in a low-speed mode in the absence of
> >> +	 * the serial link clock.
> >> +	 */
> >> +	max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> >> +		      MAX9271_FWDCCEN);
> >> +
> >> +	/*
> >> +	 * The serializer temporarily disables the reverse control channel for
> >> +	 * 350µs after starting/stopping the forward serial link, but the
> >> +	 * deserializer synchronization time isn't clearly documented.
> >> +	 *
> >> +	 * According to the serializer datasheet we should wait 3ms, while
> >> +	 * according to the deserializer datasheet we should wait 5ms.
> >> +	 *
> >> +	 * Short delays here appear to show bit-errors in the writes following.
> >> +	 * Therefore a conservative delay seems best here.
> >> +	 */
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	/*
> >> +	 * Configure the GMSL link:
> >> +	 *
> >> +	 * - Double input mode, high data rate, 24-bit mode
> >> +	 * - Latch input data on PCLKIN rising edge
> >> +	 * - Enable HS/VS encoding
> >> +	 * - 1-bit parity error detection
> >> +	 */
> >> +	max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> >> +		      MAX9271_EDC_1BIT_PARITY);
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int max9271_verify_id(struct rdacm20_device *dev)
> >> +{
> >> +	int ret;
> >> +
> >> +	ret = max9271_read(dev, 0x1e);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> >> +			ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	if (ret != MAX9271_ID) {
> >> +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> >> +			ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
> >> +{
> >> +	int ret;
> >> +
> >> +	/* Change the MAX9271 I2C address. */
> >> +	ret = max9271_write(dev, 0x00, addr << 1);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev,
> >> +			"MAX9271 I2C address change failed (%d)\n", ret);
> >> +		return ret;
> >> +	}
> >> +	dev->client->addr = addr;
> >> +	usleep_range(3500, 5000);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_initialize(struct rdacm20_device *dev)
> >> +{
> >> +	u32 addrs[2];
> >> +	int ret;
> >> +
> >> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
> >> +					 addrs, ARRAY_SIZE(addrs));
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/*
> >> +	 * FIXME: The MAX9271 boots at a default address that we will change to
> >> +	 * the address specified in DT. Set the client address back to the
> >> +	 * default for initial communication.
> >> +	 */
> >> +	dev->client->addr = MAX9271_I2C_ADDRESS;
> >> +
> >> +	/* Verify communication with the MAX9271. */
> >> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
> >> +
> >> +	/*
> >> +	 *  Ensure that we have a good link configuration before attempting to
> >> +	 *  identify the device.
> >> +	 */
> >> +	max9271_configure_i2c(dev);
> >> +	max9271_configure_gmsl_link(dev);
> >> +
> >> +	ret = max9271_verify_id(dev);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	ret = max9271_configure_address(dev, addrs[0]);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	/* Reset and verify communication with the OV10635. */
> >> +#ifdef RDACM20_SENSOR_HARD_RESET
> >> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
> >> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
> >> +	mdelay(10);
> >> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
> >> +	mdelay(10);
> > 
> > Do you need a busy loop? Could you use msleep()?
> 
> Checkpatch warns here:
> 
> WARNING: msleep < 20ms can sleep for up to 20ms; see
> Documentation/timers/timers-howto.txt
> #10: FILE: drivers/media/i2c/rdacm20.c:461:
> +       msleep(10);
> 
> I think for this context, msleep(10) even with the warning is fine here,
> but perhaps we can meet that with a usleep_range(10000, 20000); too.

usleep_range(), then, but just setting the delay to precisely 10 ms is much
better than a 10 ms busy loop.

> 
> 
> >> +#else
> >> +	/* Perform a software reset. */
> >> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	udelay(100);
> >> +#endif
> >> +
> >> +	ret = ov10635_read16(dev, OV10635_PID);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
> >> +			ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	if (ret != OV10635_VERSION) {
> >> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
> >> +			ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
> >> +
> >> +	/* Change the sensor I2C address. */
> >> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
> >> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev,
> >> +			"OV10635 I2C address change failed (%d)\n", ret);
> >> +		return ret;
> >> +	}
> >> +	dev->sensor->addr = addrs[1];
> >> +	usleep_range(3500, 5000);
> >> +
> >> +	/* Program the 0V10635 initial configuration. */
> >> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
> >> +			       ARRAY_SIZE(ov10635_regs_wizard));
> > 
> > return ov...(); ?
> 
> Yes, that would be nicer.
> 
> > 
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_probe(struct i2c_client *client,
> >> +			 const struct i2c_device_id *did)
> >> +{
> >> +	struct rdacm20_device *dev;
> >> +	struct fwnode_handle *ep;
> >> +	int ret;
> >> +
> >> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> > 
> > You could use devm_kzalloc().
> 
> Will change.
> 
> > 
> >> +	if (!dev)
> >> +		return -ENOMEM;
> >> +
> >> +	dev->client = client;
> >> +
> >> +	/* Create the dummy I2C client for the sensor. */
> >> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
> >> +	if (!dev->sensor) {
> >> +		ret = -ENXIO;
> >> +		goto error;
> >> +	}
> >> +
> >> +	/* Initialize the hardware. */
> >> +	ret = rdacm20_initialize(dev);
> >> +	if (ret < 0)
> >> +		goto error;
> >> +
> >> +	/* Initialize and register the subdevice. */
> >> +	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
> >> +	dev->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > 
> > |=, as in the other patch.
> 
> Bah - yes :)
> 
> 
> > 
> >> +
> >> +	v4l2_ctrl_handler_init(&dev->ctrls, 1);
> >> +	/*
> >> +	 * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
> >> +	 * hardcoded frequency in the BSP CSI-2 receiver driver.
> >> +	 */
> >> +	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
> >> +			  50000000, 1, 50000000);
> >> +	dev->sd.ctrl_handler = &dev->ctrls;
> >> +
> >> +	ret = dev->ctrls.error;
> >> +	if (ret)
> >> +		goto error;
> >> +
> >> +	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
> >> +	dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
> >> +	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
> >> +	if (ret < 0)
> >> +		goto error;
> >> +
> >> +	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> >> +	if (!ep) {
> >> +		dev_err(&client->dev,
> >> +			"Unable to get endpoint in node %pOF\n",
> >> +			client->dev.of_node);
> >> +		ret = -ENOENT;
> >> +		goto error;
> >> +	}
> >> +	dev->sd.fwnode = ep;
> >> +
> >> +	ret = v4l2_async_register_subdev(&dev->sd);
> >> +	if (ret)
> >> +		goto error_put_node;
> >> +
> >> +	return 0;
> >> +
> >> +error_put_node:
> >> +	fwnode_handle_put(ep);
> >> +error:
> > 
> > You're missing v4l2_ctrl_handler_free() here.
> 
> Good spot. Thanks
> 
> > 
> >> +	media_entity_cleanup(&dev->sd.entity);
> >> +	if (dev->sensor)
> >> +		i2c_unregister_device(dev->sensor);
> >> +	kfree(dev);
> >> +
> >> +	dev_err(&client->dev, "probe failed\n");
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int rdacm20_remove(struct i2c_client *client)
> >> +{
> >> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
> >> +
> >> +	fwnode_handle_put(dev->sd.fwnode);
> >> +	v4l2_async_unregister_subdev(&dev->sd);
> > 
> > As well as here.
> 
> 
> Ack.
> 
> 
> > 
> >> +	media_entity_cleanup(&dev->sd.entity);
> >> +	i2c_unregister_device(dev->sensor);
> >> +	kfree(dev);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static void rdacm20_shutdown(struct i2c_client *client)
> >> +{
> >> +	struct rdacm20_device *dev = i2c_to_rdacm20(client);
> >> +
> >> +	/* make sure stream off during shutdown (reset/reboot) */
> >> +	rdacm20_s_stream(&dev->sd, 0);
> >> +}
> >> +
> >> +static const struct i2c_device_id rdacm20_id[] = {
> >> +	{ "rdacm20", 0 },
> >> +	{ }
> >> +};
> >> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
> >> +
> >> +static const struct of_device_id rdacm20_of_ids[] = {
> >> +	{ .compatible = "imi,rdacm20", },
> >> +	{ }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
> >> +
> >> +static struct i2c_driver rdacm20_i2c_driver = {
> >> +	.driver	= {
> >> +		.name	= "rdacm20",
> >> +		.of_match_table = rdacm20_of_ids,
> >> +	},
> >> +	.probe		= rdacm20_probe,
> > 
> > Could you use probe_new, so you could remove the i2c ID table? Or do you
> > need that for something?
> 
> I believe probe_new is probably fine.
> 
> I should really resurrect my i2c-probe-coccinelle patch and get that
> conversion task done, so we can get to removing and replacing .probe :)

That'd be nice!

> 
> (note to self ... starting projects when unemployed becomes difficult to
> continue when someone else gives you projects to work on all the time ...)
> 
> 
> > 
> >> +	.remove		= rdacm20_remove,
> >> +	.shutdown	= rdacm20_shutdown,
> >> +	.id_table	= rdacm20_id,
> >> +};
> >> +
> >> +module_i2c_driver(rdacm20_i2c_driver);
> >> +
> >> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
> >> +MODULE_AUTHOR("Vladimir Barinov");
> >> +MODULE_LICENSE("GPL");
> 
> Changes described above made and tested on a *single MAX9286* capturing
> 4 cameras simultaneously, now on my rcar.git gmsl/v5 branch ... :D
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 745f0fd1fff1..26ef20087a43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12230,6 +12230,16 @@  S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
 F:	tools/testing/selftests/rcutorture
 
+RDACM20 Camera Sensor
+M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
+M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
+F:	drivers/media/i2c/rdacm20*
+
 RDC R-321X SoC
 M:	Florian Fainelli <florian@openwrt.org>
 S:	Maintained
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index eadc00bdd3bf..5eded5e337ec 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -989,6 +989,17 @@  config VIDEO_S5C73M3
 	  This is a V4L2 sensor driver for Samsung S5C73M3
 	  8 Mpixel camera.
 
+config VIDEO_RDACM20
+	tristate "IMI RDACM20 camera support"
+	depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	select V4L2_FWNODE
+	help
+	  This driver supports the IMI RDACM20 GMSL camera, used in
+	  ADAS systems.
+
+	  This camera should be used in conjunction with a GMSL
+	  deserialiser such as the MAX9286.
+
 comment "Flash devices"
 
 config VIDEO_ADP1653
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4de7fe62b179..121d28283d45 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -111,5 +111,6 @@  obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
 obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
+obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
 
 obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/rdacm20-ov10635.h b/drivers/media/i2c/rdacm20-ov10635.h
new file mode 100644
index 000000000000..3c53a3262ee2
--- /dev/null
+++ b/drivers/media/i2c/rdacm20-ov10635.h
@@ -0,0 +1,953 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * IMI RDACM20 camera OV10635 sensor register initialization values
+ *
+ * Copyright (C) 2017-2018 Jacopo Mondi
+ * Copyright (C) 2017-2018 Kieran Bingham
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ * Copyright (C) 2017-2018 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ */
+
+/*
+ * Generated by the OmniVision ov10635 sensor camera wizard for
+ * 1280x800@30/UYVY/BT601/8bit.
+ */
+
+#ifndef __RDACM20_OV10635_H__
+#define __RDACM20_OV10635_H__
+
+#define OV10635_SENSOR_WIDTH		1312
+#define OV10635_SENSOR_HEIGHT		814
+
+#define OV10635_MAX_WIDTH		1280
+#define OV10635_MAX_HEIGHT		800
+
+/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
+#define OV10635_HTS			1572
+/* FPS = 29,9998 */
+#define OV10635_VTS			933
+
+struct ov10635_reg {
+	u16	reg;
+	u8	val;
+};
+
+static const struct ov10635_reg ov10635_regs_wizard[] = {
+	{ 0x301b, 0xff },
+	{ 0x301c, 0xff },
+	{ 0x301a, 0xff },
+	{ 0x3011, 0x42 },
+	{ 0x6900, 0x0c },
+	{ 0x6901, 0x19 },
+	{ 0x3503, 0x10 },
+	{ 0x3025, 0x03 },
+	{ 0x3003, 0x16 },
+	{ 0x3004, 0x30 },
+	{ 0x3005, 0x40 },
+	{ 0x3006, 0x91 },
+	{ 0x3600, 0x74 },
+	{ 0x3601, 0x2b },
+	{ 0x3612, 0x00 },
+	{ 0x3611, 0x67 },
+	{ 0x3633, 0xca },
+	{ 0x3602, 0xaf },
+	{ 0x3603, 0x04 },
+	{ 0x3630, 0x28 },
+	{ 0x3631, 0x16 },
+	{ 0x3714, 0x10 },
+	{ 0x371d, 0x01 },
+	{ 0x4300, 0x3a },
+	{ 0x3007, 0x01 },
+	{ 0x3024, 0x03 },
+	{ 0x3020, 0x0a },
+	{ 0x3702, 0x0d },
+	{ 0x3703, 0x20 },
+	{ 0x3704, 0x15 },
+	{ 0x3709, 0xa8 },
+	{ 0x370c, 0xc7 },
+	{ 0x370d, 0x80 },
+	{ 0x3712, 0x00 },
+	{ 0x3713, 0x20 },
+	{ 0x3715, 0x04 },
+	{ 0x381d, 0x40 },
+	{ 0x381c, 0x00 },
+	{ 0x3822, 0x50 },
+	{ 0x3824, 0x10 },
+	{ 0x3815, 0x8c },
+	{ 0x3804, 0x05 },
+	{ 0x3805, 0x1f },
+	{ 0x3800, 0x00 },
+	{ 0x3801, 0x00 },
+	{ 0x3806, 0x03 },
+	{ 0x3807, 0x28 },
+	{ 0x3802, 0x00 },
+	{ 0x3803, 0x07 },
+	{ 0x3808, 0x05 },
+	{ 0x3809, 0x00 },
+	{ 0x380a, 0x03 },
+	{ 0x380b, 0x20 },
+	{ 0x380c, OV10635_HTS >> 8 },
+	{ 0x380d, OV10635_HTS & 0xff },
+	{ 0x380e, OV10635_VTS >> 8 },
+	{ 0x380f, OV10635_VTS & 0xff },
+	{ 0x3813, 0x02 },
+	{ 0x3811, 0x08 },
+	{ 0x381f, 0x0c },
+	{ 0x3819, 0x04 },
+	{ 0x3804, 0x01 },
+	{ 0x3805, 0x00 },
+	{ 0x3828, 0x03 },
+	{ 0x3829, 0x10 },
+	{ 0x382a, 0x10 },
+	{ 0x3621, 0x63 },
+	{ 0x5005, 0x08 },
+	{ 0x56d5, 0x00 },
+	{ 0x56d6, 0x80 },
+	{ 0x56d7, 0x00 },
+	{ 0x56d8, 0x00 },
+	{ 0x56d9, 0x00 },
+	{ 0x56da, 0x80 },
+	{ 0x56db, 0x00 },
+	{ 0x56dc, 0x00 },
+	{ 0x56e8, 0x00 },
+	{ 0x56e9, 0x7f },
+	{ 0x56ea, 0x00 },
+	{ 0x56eb, 0x7f },
+	{ 0x5100, 0x00 },
+	{ 0x5101, 0x80 },
+	{ 0x5102, 0x00 },
+	{ 0x5103, 0x80 },
+	{ 0x5104, 0x00 },
+	{ 0x5105, 0x80 },
+	{ 0x5106, 0x00 },
+	{ 0x5107, 0x80 },
+	{ 0x5108, 0x00 },
+	{ 0x5109, 0x00 },
+	{ 0x510a, 0x00 },
+	{ 0x510b, 0x00 },
+	{ 0x510c, 0x00 },
+	{ 0x510d, 0x00 },
+	{ 0x510e, 0x00 },
+	{ 0x510f, 0x00 },
+	{ 0x5110, 0x00 },
+	{ 0x5111, 0x80 },
+	{ 0x5112, 0x00 },
+	{ 0x5113, 0x80 },
+	{ 0x5114, 0x00 },
+	{ 0x5115, 0x80 },
+	{ 0x5116, 0x00 },
+	{ 0x5117, 0x80 },
+	{ 0x5118, 0x00 },
+	{ 0x5119, 0x00 },
+	{ 0x511a, 0x00 },
+	{ 0x511b, 0x00 },
+	{ 0x511c, 0x00 },
+	{ 0x511d, 0x00 },
+	{ 0x511e, 0x00 },
+	{ 0x511f, 0x00 },
+	{ 0x56d0, 0x00 },
+	{ 0x5006, 0x04 },
+	{ 0x5608, 0x05 },
+	{ 0x52d7, 0x06 },
+	{ 0x528d, 0x08 },
+	{ 0x5293, 0x12 },
+	{ 0x52d3, 0x12 },
+	{ 0x5288, 0x06 },
+	{ 0x5289, 0x20 },
+	{ 0x52c8, 0x06 },
+	{ 0x52c9, 0x20 },
+	{ 0x52cd, 0x04 },
+	{ 0x5381, 0x00 },
+	{ 0x5382, 0xff },
+	{ 0x5589, 0x76 },
+	{ 0x558a, 0x47 },
+	{ 0x558b, 0xef },
+	{ 0x558c, 0xc9 },
+	{ 0x558d, 0x49 },
+	{ 0x558e, 0x30 },
+	{ 0x558f, 0x67 },
+	{ 0x5590, 0x3f },
+	{ 0x5591, 0xf0 },
+	{ 0x5592, 0x10 },
+	{ 0x55a2, 0x6d },
+	{ 0x55a3, 0x55 },
+	{ 0x55a4, 0xc3 },
+	{ 0x55a5, 0xb5 },
+	{ 0x55a6, 0x43 },
+	{ 0x55a7, 0x38 },
+	{ 0x55a8, 0x5f },
+	{ 0x55a9, 0x4b },
+	{ 0x55aa, 0xf0 },
+	{ 0x55ab, 0x10 },
+	{ 0x5581, 0x52 },
+	{ 0x5300, 0x01 },
+	{ 0x5301, 0x00 },
+	{ 0x5302, 0x00 },
+	{ 0x5303, 0x0e },
+	{ 0x5304, 0x00 },
+	{ 0x5305, 0x0e },
+	{ 0x5306, 0x00 },
+	{ 0x5307, 0x36 },
+	{ 0x5308, 0x00 },
+	{ 0x5309, 0xd9 },
+	{ 0x530a, 0x00 },
+	{ 0x530b, 0x0f },
+	{ 0x530c, 0x00 },
+	{ 0x530d, 0x2c },
+	{ 0x530e, 0x00 },
+	{ 0x530f, 0x59 },
+	{ 0x5310, 0x00 },
+	{ 0x5311, 0x7b },
+	{ 0x5312, 0x00 },
+	{ 0x5313, 0x22 },
+	{ 0x5314, 0x00 },
+	{ 0x5315, 0xd5 },
+	{ 0x5316, 0x00 },
+	{ 0x5317, 0x13 },
+	{ 0x5318, 0x00 },
+	{ 0x5319, 0x18 },
+	{ 0x531a, 0x00 },
+	{ 0x531b, 0x26 },
+	{ 0x531c, 0x00 },
+	{ 0x531d, 0xdc },
+	{ 0x531e, 0x00 },
+	{ 0x531f, 0x02 },
+	{ 0x5320, 0x00 },
+	{ 0x5321, 0x24 },
+	{ 0x5322, 0x00 },
+	{ 0x5323, 0x56 },
+	{ 0x5324, 0x00 },
+	{ 0x5325, 0x85 },
+	{ 0x5326, 0x00 },
+	{ 0x5327, 0x20 },
+	{ 0x5609, 0x01 },
+	{ 0x560a, 0x40 },
+	{ 0x560b, 0x01 },
+	{ 0x560c, 0x40 },
+	{ 0x560d, 0x00 },
+	{ 0x560e, 0xfa },
+	{ 0x560f, 0x00 },
+	{ 0x5610, 0xfa },
+	{ 0x5611, 0x02 },
+	{ 0x5612, 0x80 },
+	{ 0x5613, 0x02 },
+	{ 0x5614, 0x80 },
+	{ 0x5615, 0x01 },
+	{ 0x5616, 0x2c },
+	{ 0x5617, 0x01 },
+	{ 0x5618, 0x2c },
+	{ 0x563b, 0x01 },
+	{ 0x563c, 0x01 },
+	{ 0x563d, 0x01 },
+	{ 0x563e, 0x01 },
+	{ 0x563f, 0x03 },
+	{ 0x5640, 0x03 },
+	{ 0x5641, 0x03 },
+	{ 0x5642, 0x05 },
+	{ 0x5643, 0x09 },
+	{ 0x5644, 0x05 },
+	{ 0x5645, 0x05 },
+	{ 0x5646, 0x05 },
+	{ 0x5647, 0x05 },
+	{ 0x5651, 0x00 },
+	{ 0x5652, 0x80 },
+	{ 0x521a, 0x01 },
+	{ 0x521b, 0x03 },
+	{ 0x521c, 0x06 },
+	{ 0x521d, 0x0a },
+	{ 0x521e, 0x0e },
+	{ 0x521f, 0x12 },
+	{ 0x5220, 0x16 },
+	{ 0x5223, 0x02 },
+	{ 0x5225, 0x04 },
+	{ 0x5227, 0x08 },
+	{ 0x5229, 0x0c },
+	{ 0x522b, 0x12 },
+	{ 0x522d, 0x18 },
+	{ 0x522f, 0x1e },
+	{ 0x5241, 0x04 },
+	{ 0x5242, 0x01 },
+	{ 0x5243, 0x03 },
+	{ 0x5244, 0x06 },
+	{ 0x5245, 0x0a },
+	{ 0x5246, 0x0e },
+	{ 0x5247, 0x12 },
+	{ 0x5248, 0x16 },
+	{ 0x524a, 0x03 },
+	{ 0x524c, 0x04 },
+	{ 0x524e, 0x08 },
+	{ 0x5250, 0x0c },
+	{ 0x5252, 0x12 },
+	{ 0x5254, 0x18 },
+	{ 0x5256, 0x1e },
+	/* fifo_line_length = 2*hts */
+	{ 0x4606, (2 * OV10635_HTS) >> 8 },
+	{ 0x4607, (2 * OV10635_HTS) & 0xff },
+	/* fifo_hsync_start = 2*(hts - xres) */
+	{ 0x460a, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) >> 8 },
+	{ 0x460b, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) & 0xff },
+	{ 0x460c, 0x00 },
+	{ 0x4620, 0x0e },
+	/* BT601: 0x08 is also acceptable as HS/VS mode */
+	{ 0x4700, 0x04 },
+	{ 0x4701, 0x00 },
+	{ 0x4702, 0x01 },
+	{ 0x4004, 0x04 },
+	{ 0x4005, 0x18 },
+	{ 0x4001, 0x06 },
+	{ 0x4050, 0x22 },
+	{ 0x4051, 0x24 },
+	{ 0x4052, 0x02 },
+	{ 0x4057, 0x9c },
+	{ 0x405a, 0x00 },
+	{ 0x4202, 0x02 },
+	{ 0x3023, 0x10 },
+	{ 0x0100, 0x01 },
+	{ 0x0100, 0x01 },
+	{ 0x6f10, 0x07 },
+	{ 0x6f11, 0x82 },
+	{ 0x6f12, 0x04 },
+	{ 0x6f13, 0x00 },
+	{ 0xd000, 0x19 },
+	{ 0xd001, 0xa0 },
+	{ 0xd002, 0x00 },
+	{ 0xd003, 0x01 },
+	{ 0xd004, 0xa9 },
+	{ 0xd005, 0xad },
+	{ 0xd006, 0x10 },
+	{ 0xd007, 0x40 },
+	{ 0xd008, 0x44 },
+	{ 0xd009, 0x00 },
+	{ 0xd00a, 0x68 },
+	{ 0xd00b, 0x00 },
+	{ 0xd00c, 0x15 },
+	{ 0xd00d, 0x00 },
+	{ 0xd00e, 0x00 },
+	{ 0xd00f, 0x00 },
+	{ 0xd040, 0x9c },
+	{ 0xd041, 0x21 },
+	{ 0xd042, 0xff },
+	{ 0xd043, 0xf8 },
+	{ 0xd044, 0xd4 },
+	{ 0xd045, 0x01 },
+	{ 0xd046, 0x48 },
+	{ 0xd047, 0x00 },
+	{ 0xd048, 0xd4 },
+	{ 0xd049, 0x01 },
+	{ 0xd04a, 0x50 },
+	{ 0xd04b, 0x04 },
+	{ 0xd04c, 0x18 },
+	{ 0xd04d, 0x60 },
+	{ 0xd04e, 0x00 },
+	{ 0xd04f, 0x01 },
+	{ 0xd050, 0xa8 },
+	{ 0xd051, 0x63 },
+	{ 0xd052, 0x02 },
+	{ 0xd053, 0xa4 },
+	{ 0xd054, 0x85 },
+	{ 0xd055, 0x43 },
+	{ 0xd056, 0x00 },
+	{ 0xd057, 0x00 },
+	{ 0xd058, 0x18 },
+	{ 0xd059, 0x60 },
+	{ 0xd05a, 0x00 },
+	{ 0xd05b, 0x01 },
+	{ 0xd05c, 0xa8 },
+	{ 0xd05d, 0x63 },
+	{ 0xd05e, 0x03 },
+	{ 0xd05f, 0xf0 },
+	{ 0xd060, 0x98 },
+	{ 0xd061, 0xa3 },
+	{ 0xd062, 0x00 },
+	{ 0xd063, 0x00 },
+	{ 0xd064, 0x8c },
+	{ 0xd065, 0x6a },
+	{ 0xd066, 0x00 },
+	{ 0xd067, 0x6e },
+	{ 0xd068, 0xe5 },
+	{ 0xd069, 0x85 },
+	{ 0xd06a, 0x18 },
+	{ 0xd06b, 0x00 },
+	{ 0xd06c, 0x10 },
+	{ 0xd06d, 0x00 },
+	{ 0xd06e, 0x00 },
+	{ 0xd06f, 0x10 },
+	{ 0xd070, 0x9c },
+	{ 0xd071, 0x80 },
+	{ 0xd072, 0x00 },
+	{ 0xd073, 0x03 },
+	{ 0xd074, 0x18 },
+	{ 0xd075, 0x60 },
+	{ 0xd076, 0x00 },
+	{ 0xd077, 0x01 },
+	{ 0xd078, 0xa8 },
+	{ 0xd079, 0x63 },
+	{ 0xd07a, 0x07 },
+	{ 0xd07b, 0x80 },
+	{ 0xd07c, 0x07 },
+	{ 0xd07d, 0xff },
+	{ 0xd07e, 0xf9 },
+	{ 0xd07f, 0x03 },
+	{ 0xd080, 0x8c },
+	{ 0xd081, 0x63 },
+	{ 0xd082, 0x00 },
+	{ 0xd083, 0x00 },
+	{ 0xd084, 0xa5 },
+	{ 0xd085, 0x6b },
+	{ 0xd086, 0x00 },
+	{ 0xd087, 0xff },
+	{ 0xd088, 0x18 },
+	{ 0xd089, 0x80 },
+	{ 0xd08a, 0x00 },
+	{ 0xd08b, 0x01 },
+	{ 0xd08c, 0xa8 },
+	{ 0xd08d, 0x84 },
+	{ 0xd08e, 0x01 },
+	{ 0xd08f, 0x04 },
+	{ 0xd090, 0xe1 },
+	{ 0xd091, 0x6b },
+	{ 0xd092, 0x58 },
+	{ 0xd093, 0x00 },
+	{ 0xd094, 0x94 },
+	{ 0xd095, 0x6a },
+	{ 0xd096, 0x00 },
+	{ 0xd097, 0x70 },
+	{ 0xd098, 0xe1 },
+	{ 0xd099, 0x6b },
+	{ 0xd09a, 0x20 },
+	{ 0xd09b, 0x00 },
+	{ 0xd09c, 0x95 },
+	{ 0xd09d, 0x6b },
+	{ 0xd09e, 0x00 },
+	{ 0xd09f, 0x00 },
+	{ 0xd0a0, 0xe4 },
+	{ 0xd0a1, 0x8b },
+	{ 0xd0a2, 0x18 },
+	{ 0xd0a3, 0x00 },
+	{ 0xd0a4, 0x0c },
+	{ 0xd0a5, 0x00 },
+	{ 0xd0a6, 0x00 },
+	{ 0xd0a7, 0x23 },
+	{ 0xd0a8, 0x15 },
+	{ 0xd0a9, 0x00 },
+	{ 0xd0aa, 0x00 },
+	{ 0xd0ab, 0x00 },
+	{ 0xd0ac, 0x18 },
+	{ 0xd0ad, 0x60 },
+	{ 0xd0ae, 0x80 },
+	{ 0xd0af, 0x06 },
+	{ 0xd0b0, 0xa8 },
+	{ 0xd0b1, 0x83 },
+	{ 0xd0b2, 0x40 },
+	{ 0xd0b3, 0x08 },
+	{ 0xd0b4, 0xa8 },
+	{ 0xd0b5, 0xe3 },
+	{ 0xd0b6, 0x38 },
+	{ 0xd0b7, 0x2a },
+	{ 0xd0b8, 0xa8 },
+	{ 0xd0b9, 0xc3 },
+	{ 0xd0ba, 0x40 },
+	{ 0xd0bb, 0x09 },
+	{ 0xd0bc, 0xa8 },
+	{ 0xd0bd, 0xa3 },
+	{ 0xd0be, 0x38 },
+	{ 0xd0bf, 0x29 },
+	{ 0xd0c0, 0x8c },
+	{ 0xd0c1, 0x65 },
+	{ 0xd0c2, 0x00 },
+	{ 0xd0c3, 0x00 },
+	{ 0xd0c4, 0xd8 },
+	{ 0xd0c5, 0x04 },
+	{ 0xd0c6, 0x18 },
+	{ 0xd0c7, 0x00 },
+	{ 0xd0c8, 0x8c },
+	{ 0xd0c9, 0x67 },
+	{ 0xd0ca, 0x00 },
+	{ 0xd0cb, 0x00 },
+	{ 0xd0cc, 0xd8 },
+	{ 0xd0cd, 0x06 },
+	{ 0xd0ce, 0x18 },
+	{ 0xd0cf, 0x00 },
+	{ 0xd0d0, 0x18 },
+	{ 0xd0d1, 0x60 },
+	{ 0xd0d2, 0x80 },
+	{ 0xd0d3, 0x06 },
+	{ 0xd0d4, 0xa8 },
+	{ 0xd0d5, 0xe3 },
+	{ 0xd0d6, 0x67 },
+	{ 0xd0d7, 0x02 },
+	{ 0xd0d8, 0xa9 },
+	{ 0xd0d9, 0x03 },
+	{ 0xd0da, 0x67 },
+	{ 0xd0db, 0x03 },
+	{ 0xd0dc, 0xa8 },
+	{ 0xd0dd, 0xc3 },
+	{ 0xd0de, 0x3d },
+	{ 0xd0df, 0x05 },
+	{ 0xd0e0, 0x8c },
+	{ 0xd0e1, 0x66 },
+	{ 0xd0e2, 0x00 },
+	{ 0xd0e3, 0x00 },
+	{ 0xd0e4, 0xb8 },
+	{ 0xd0e5, 0x63 },
+	{ 0xd0e6, 0x00 },
+	{ 0xd0e7, 0x18 },
+	{ 0xd0e8, 0xb8 },
+	{ 0xd0e9, 0x63 },
+	{ 0xd0ea, 0x00 },
+	{ 0xd0eb, 0x98 },
+	{ 0xd0ec, 0xbc },
+	{ 0xd0ed, 0x03 },
+	{ 0xd0ee, 0x00 },
+	{ 0xd0ef, 0x00 },
+	{ 0xd0f0, 0x10 },
+	{ 0xd0f1, 0x00 },
+	{ 0xd0f2, 0x00 },
+	{ 0xd0f3, 0x16 },
+	{ 0xd0f4, 0xb8 },
+	{ 0xd0f5, 0x83 },
+	{ 0xd0f6, 0x00 },
+	{ 0xd0f7, 0x19 },
+	{ 0xd0f8, 0x8c },
+	{ 0xd0f9, 0x67 },
+	{ 0xd0fa, 0x00 },
+	{ 0xd0fb, 0x00 },
+	{ 0xd0fc, 0xb8 },
+	{ 0xd0fd, 0xa4 },
+	{ 0xd0fe, 0x00 },
+	{ 0xd0ff, 0x98 },
+	{ 0xd100, 0xb8 },
+	{ 0xd101, 0x83 },
+	{ 0xd102, 0x00 },
+	{ 0xd103, 0x08 },
+	{ 0xd104, 0x8c },
+	{ 0xd105, 0x68 },
+	{ 0xd106, 0x00 },
+	{ 0xd107, 0x00 },
+	{ 0xd108, 0xe0 },
+	{ 0xd109, 0x63 },
+	{ 0xd10a, 0x20 },
+	{ 0xd10b, 0x04 },
+	{ 0xd10c, 0xe0 },
+	{ 0xd10d, 0x65 },
+	{ 0xd10e, 0x18 },
+	{ 0xd10f, 0x00 },
+	{ 0xd110, 0xa4 },
+	{ 0xd111, 0x83 },
+	{ 0xd112, 0xff },
+	{ 0xd113, 0xff },
+	{ 0xd114, 0xb8 },
+	{ 0xd115, 0x64 },
+	{ 0xd116, 0x00 },
+	{ 0xd117, 0x48 },
+	{ 0xd118, 0xd8 },
+	{ 0xd119, 0x07 },
+	{ 0xd11a, 0x18 },
+	{ 0xd11b, 0x00 },
+	{ 0xd11c, 0xd8 },
+	{ 0xd11d, 0x08 },
+	{ 0xd11e, 0x20 },
+	{ 0xd11f, 0x00 },
+	{ 0xd120, 0x9c },
+	{ 0xd121, 0x60 },
+	{ 0xd122, 0x00 },
+	{ 0xd123, 0x00 },
+	{ 0xd124, 0xd8 },
+	{ 0xd125, 0x06 },
+	{ 0xd126, 0x18 },
+	{ 0xd127, 0x00 },
+	{ 0xd128, 0x00 },
+	{ 0xd129, 0x00 },
+	{ 0xd12a, 0x00 },
+	{ 0xd12b, 0x08 },
+	{ 0xd12c, 0x15 },
+	{ 0xd12d, 0x00 },
+	{ 0xd12e, 0x00 },
+	{ 0xd12f, 0x00 },
+	{ 0xd130, 0x8c },
+	{ 0xd131, 0x6a },
+	{ 0xd132, 0x00 },
+	{ 0xd133, 0x76 },
+	{ 0xd134, 0xbc },
+	{ 0xd135, 0x23 },
+	{ 0xd136, 0x00 },
+	{ 0xd137, 0x00 },
+	{ 0xd138, 0x13 },
+	{ 0xd139, 0xff },
+	{ 0xd13a, 0xff },
+	{ 0xd13b, 0xe6 },
+	{ 0xd13c, 0x18 },
+	{ 0xd13d, 0x60 },
+	{ 0xd13e, 0x80 },
+	{ 0xd13f, 0x06 },
+	{ 0xd140, 0x03 },
+	{ 0xd141, 0xff },
+	{ 0xd142, 0xff },
+	{ 0xd143, 0xdd },
+	{ 0xd144, 0xa8 },
+	{ 0xd145, 0x83 },
+	{ 0xd146, 0x40 },
+	{ 0xd147, 0x08 },
+	{ 0xd148, 0x85 },
+	{ 0xd149, 0x21 },
+	{ 0xd14a, 0x00 },
+	{ 0xd14b, 0x00 },
+	{ 0xd14c, 0x85 },
+	{ 0xd14d, 0x41 },
+	{ 0xd14e, 0x00 },
+	{ 0xd14f, 0x04 },
+	{ 0xd150, 0x44 },
+	{ 0xd151, 0x00 },
+	{ 0xd152, 0x48 },
+	{ 0xd153, 0x00 },
+	{ 0xd154, 0x9c },
+	{ 0xd155, 0x21 },
+	{ 0xd156, 0x00 },
+	{ 0xd157, 0x08 },
+	{ 0x6f0e, 0x03 },
+	{ 0x6f0f, 0x00 },
+	{ 0x460e, 0x08 },
+	{ 0x460f, 0x01 },
+	{ 0x4610, 0x00 },
+	{ 0x4611, 0x01 },
+	{ 0x4612, 0x00 },
+	{ 0x4613, 0x01 },
+	/* 8 bits */
+	{ 0x4605, 0x08 },
+	/* Swap data bits order [9:0] -> [0:9] */
+	{ 0x4709, 0x10 },
+	{ 0x4608, 0x00 },
+	{ 0x4609, 0x08 },
+	{ 0x6804, 0x00 },
+	{ 0x6805, 0x06 },
+	{ 0x6806, 0x00 },
+	{ 0x5120, 0x00 },
+	{ 0x3510, 0x00 },
+	{ 0x3504, 0x00 },
+	{ 0x6800, 0x00 },
+	{ 0x6f0d, 0x01 },
+	/* PCLK falling edge */
+	{ 0x4708, 0x01 },
+	{ 0x5000, 0xff },
+	{ 0x5001, 0xbf },
+	{ 0x5002, 0x7e },
+	{ 0x503d, 0x00 },
+	{ 0xc450, 0x01 },
+	{ 0xc452, 0x04 },
+	{ 0xc453, 0x00 },
+	{ 0xc454, 0x00 },
+	{ 0xc455, 0x01 },
+	{ 0xc456, 0x01 },
+	{ 0xc457, 0x00 },
+	{ 0xc458, 0x00 },
+	{ 0xc459, 0x00 },
+	{ 0xc45b, 0x00 },
+	{ 0xc45c, 0x01 },
+	{ 0xc45d, 0x00 },
+	{ 0xc45e, 0x00 },
+	{ 0xc45f, 0x00 },
+	{ 0xc460, 0x00 },
+	{ 0xc461, 0x01 },
+	{ 0xc462, 0x01 },
+	{ 0xc464, 0x03 },
+	{ 0xc465, 0x00 },
+	{ 0xc466, 0x8a },
+	{ 0xc467, 0x00 },
+	{ 0xc468, 0x86 },
+	{ 0xc469, 0x00 },
+	{ 0xc46a, 0x40 },
+	{ 0xc46b, 0x50 },
+	{ 0xc46c, 0x30 },
+	{ 0xc46d, 0x28 },
+	{ 0xc46e, 0x60 },
+	{ 0xc46f, 0x40 },
+	{ 0xc47c, 0x01 },
+	{ 0xc47d, 0x38 },
+	{ 0xc47e, 0x00 },
+	{ 0xc47f, 0x00 },
+	{ 0xc480, 0x00 },
+	{ 0xc481, 0xff },
+	{ 0xc482, 0x00 },
+	{ 0xc483, 0x40 },
+	{ 0xc484, 0x00 },
+	{ 0xc485, 0x18 },
+	{ 0xc486, 0x00 },
+	{ 0xc487, 0x18 },
+	{ 0xc488, (OV10635_VTS - 8) * 16 >> 8},
+	{ 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
+	{ 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
+	{ 0xc48b, (OV10635_VTS - 8) * 16 & 0xff},
+	{ 0xc48c, 0x00 },
+	{ 0xc48d, 0x04 },
+	{ 0xc48e, 0x00 },
+	{ 0xc48f, 0x04 },
+	{ 0xc490, 0x03 },
+	{ 0xc492, 0x20 },
+	{ 0xc493, 0x08 },
+	{ 0xc498, 0x02 },
+	{ 0xc499, 0x00 },
+	{ 0xc49a, 0x02 },
+	{ 0xc49b, 0x00 },
+	{ 0xc49c, 0x02 },
+	{ 0xc49d, 0x00 },
+	{ 0xc49e, 0x02 },
+	{ 0xc49f, 0x60 },
+	{ 0xc4a0, 0x03 },
+	{ 0xc4a1, 0x00 },
+	{ 0xc4a2, 0x04 },
+	{ 0xc4a3, 0x00 },
+	{ 0xc4a4, 0x00 },
+	{ 0xc4a5, 0x10 },
+	{ 0xc4a6, 0x00 },
+	{ 0xc4a7, 0x40 },
+	{ 0xc4a8, 0x00 },
+	{ 0xc4a9, 0x80 },
+	{ 0xc4aa, 0x0d },
+	{ 0xc4ab, 0x00 },
+	{ 0xc4ac, 0x0f },
+	{ 0xc4ad, 0xc0 },
+	{ 0xc4b4, 0x01 },
+	{ 0xc4b5, 0x01 },
+	{ 0xc4b6, 0x00 },
+	{ 0xc4b7, 0x01 },
+	{ 0xc4b8, 0x00 },
+	{ 0xc4b9, 0x01 },
+	{ 0xc4ba, 0x01 },
+	{ 0xc4bb, 0x00 },
+	{ 0xc4bc, 0x01 },
+	{ 0xc4bd, 0x60 },
+	{ 0xc4be, 0x02 },
+	{ 0xc4bf, 0x33 },
+	{ 0xc4c8, 0x03 },
+	{ 0xc4c9, 0xd0 },
+	{ 0xc4ca, 0x0e },
+	{ 0xc4cb, 0x00 },
+	{ 0xc4cc, 0x0e },
+	{ 0xc4cd, 0x51 },
+	{ 0xc4ce, 0x0e },
+	{ 0xc4cf, 0x51 },
+	{ 0xc4d0, 0x04 },
+	{ 0xc4d1, 0x80 },
+	{ 0xc4e0, 0x04 },
+	{ 0xc4e1, 0x02 },
+	{ 0xc4e2, 0x01 },
+	{ 0xc4e4, 0x10 },
+	{ 0xc4e5, 0x20 },
+	{ 0xc4e6, 0x30 },
+	{ 0xc4e7, 0x40 },
+	{ 0xc4e8, 0x50 },
+	{ 0xc4e9, 0x60 },
+	{ 0xc4ea, 0x70 },
+	{ 0xc4eb, 0x80 },
+	{ 0xc4ec, 0x90 },
+	{ 0xc4ed, 0xa0 },
+	{ 0xc4ee, 0xb0 },
+	{ 0xc4ef, 0xc0 },
+	{ 0xc4f0, 0xd0 },
+	{ 0xc4f1, 0xe0 },
+	{ 0xc4f2, 0xf0 },
+	{ 0xc4f3, 0x80 },
+	{ 0xc4f4, 0x00 },
+	{ 0xc4f5, 0x20 },
+	{ 0xc4f6, 0x02 },
+	{ 0xc4f7, 0x00 },
+	{ 0xc4f8, 0x00 },
+	{ 0xc4f9, 0x00 },
+	{ 0xc4fa, 0x00 },
+	{ 0xc4fb, 0x01 },
+	{ 0xc4fc, 0x01 },
+	{ 0xc4fd, 0x00 },
+	{ 0xc4fe, 0x04 },
+	{ 0xc4ff, 0x02 },
+	{ 0xc500, 0x48 },
+	{ 0xc501, 0x74 },
+	{ 0xc502, 0x58 },
+	{ 0xc503, 0x80 },
+	{ 0xc504, 0x05 },
+	{ 0xc505, 0x80 },
+	{ 0xc506, 0x03 },
+	{ 0xc507, 0x80 },
+	{ 0xc508, 0x01 },
+	{ 0xc509, 0xc0 },
+	{ 0xc50a, 0x01 },
+	{ 0xc50b, 0xa0 },
+	{ 0xc50c, 0x01 },
+	{ 0xc50d, 0x2c },
+	{ 0xc50e, 0x01 },
+	{ 0xc50f, 0x0a },
+	{ 0xc510, 0x00 },
+	{ 0xc511, 0x00 },
+	{ 0xc512, 0xe5 },
+	{ 0xc513, 0x14 },
+	{ 0xc514, 0x04 },
+	{ 0xc515, 0x00 },
+	{ 0xc518, OV10635_VTS >> 8},
+	{ 0xc519, OV10635_VTS & 0xff},
+	{ 0xc51a, OV10635_HTS >> 8},
+	{ 0xc51b, OV10635_HTS & 0xff},
+	{ 0xc2e0, 0x00 },
+	{ 0xc2e1, 0x51 },
+	{ 0xc2e2, 0x00 },
+	{ 0xc2e3, 0xd6 },
+	{ 0xc2e4, 0x01 },
+	{ 0xc2e5, 0x5e },
+	{ 0xc2e9, 0x01 },
+	{ 0xc2ea, 0x7a },
+	{ 0xc2eb, 0x90 },
+	{ 0xc2ed, 0x00 },
+	{ 0xc2ee, 0x7a },
+	{ 0xc2ef, 0x64 },
+	{ 0xc308, 0x00 },
+	{ 0xc309, 0x00 },
+	{ 0xc30a, 0x00 },
+	{ 0xc30c, 0x00 },
+	{ 0xc30d, 0x01 },
+	{ 0xc30e, 0x00 },
+	{ 0xc30f, 0x00 },
+	{ 0xc310, 0x01 },
+	{ 0xc311, 0x60 },
+	{ 0xc312, 0xff },
+	{ 0xc313, 0x08 },
+	{ 0xc314, 0x01 },
+	{ 0xc315, 0x00 },
+	{ 0xc316, 0xff },
+	{ 0xc317, 0x0b },
+	{ 0xc318, 0x00 },
+	{ 0xc319, 0x0c },
+	{ 0xc31a, 0x00 },
+	{ 0xc31b, 0xe0 },
+	{ 0xc31c, 0x00 },
+	{ 0xc31d, 0x14 },
+	{ 0xc31e, 0x00 },
+	{ 0xc31f, 0xc5 },
+	{ 0xc320, 0xff },
+	{ 0xc321, 0x4b },
+	{ 0xc322, 0xff },
+	{ 0xc323, 0xf0 },
+	{ 0xc324, 0xff },
+	{ 0xc325, 0xe8 },
+	{ 0xc326, 0x00 },
+	{ 0xc327, 0x46 },
+	{ 0xc328, 0xff },
+	{ 0xc329, 0xd2 },
+	{ 0xc32a, 0xff },
+	{ 0xc32b, 0xe4 },
+	{ 0xc32c, 0xff },
+	{ 0xc32d, 0xbb },
+	{ 0xc32e, 0x00 },
+	{ 0xc32f, 0x61 },
+	{ 0xc330, 0xff },
+	{ 0xc331, 0xf9 },
+	{ 0xc332, 0x00 },
+	{ 0xc333, 0xd9 },
+	{ 0xc334, 0x00 },
+	{ 0xc335, 0x2e },
+	{ 0xc336, 0x00 },
+	{ 0xc337, 0xb1 },
+	{ 0xc338, 0xff },
+	{ 0xc339, 0x64 },
+	{ 0xc33a, 0xff },
+	{ 0xc33b, 0xeb },
+	{ 0xc33c, 0xff },
+	{ 0xc33d, 0xe8 },
+	{ 0xc33e, 0x00 },
+	{ 0xc33f, 0x48 },
+	{ 0xc340, 0xff },
+	{ 0xc341, 0xd0 },
+	{ 0xc342, 0xff },
+	{ 0xc343, 0xed },
+	{ 0xc344, 0xff },
+	{ 0xc345, 0xad },
+	{ 0xc346, 0x00 },
+	{ 0xc347, 0x66 },
+	{ 0xc348, 0x01 },
+	{ 0xc349, 0x00 },
+	{ 0x6700, 0x04 },
+	{ 0x6701, 0x7b },
+	{ 0x6702, 0xfd },
+	{ 0x6703, 0xf9 },
+	{ 0x6704, 0x3d },
+	{ 0x6705, 0x71 },
+	{ 0x6706, 0x78 },
+	{ 0x6708, 0x05 },
+	{ 0x6f06, 0x6f },
+	{ 0x6f07, 0x00 },
+	{ 0x6f0a, 0x6f },
+	{ 0x6f0b, 0x00 },
+	{ 0x6f00, 0x03 },
+	{ 0xc34c, 0x01 },
+	{ 0xc34d, 0x00 },
+	{ 0xc34e, 0x46 },
+	{ 0xc34f, 0x55 },
+	{ 0xc350, 0x00 },
+	{ 0xc351, 0x40 },
+	{ 0xc352, 0x00 },
+	{ 0xc353, 0xff },
+	{ 0xc354, 0x04 },
+	{ 0xc355, 0x08 },
+	{ 0xc356, 0x01 },
+	{ 0xc357, 0xef },
+	{ 0xc358, 0x30 },
+	{ 0xc359, 0x01 },
+	{ 0xc35a, 0x64 },
+	{ 0xc35b, 0x46 },
+	{ 0xc35c, 0x00 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0xc261, 0x01 },
+	{ 0x301b, 0xf0 },
+	{ 0x301c, 0xf0 },
+	{ 0x301a, 0xf0 },
+	{ 0x6f00, 0xc3 },
+	{ 0xc46a, 0x30 },
+	{ 0xc46d, 0x20 },
+	{ 0xc464, 0x84 },
+	{ 0xc465, 0x00 },
+	{ 0x6f00, 0x03 },
+	{ 0x6f00, 0x43 },
+	{ 0x381c, 0x00 },
+	{ 0x381d, 0x40 },
+	{ 0xc454, 0x01 },
+	{ 0x6f00, 0xc3 },
+	{ 0xc454, 0x00 },
+	{ 0xc4b1, 0x02 },
+	{ 0xc4b2, 0x01 },
+	{ 0xc4b3, 0x03 },
+	{ 0x6f00, 0x03 },
+	{ 0x6f00, 0x43 },
+	/* enable FSIN (FRAMESYNC input) functionality */
+	{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
+	{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
+	{ 0x3834, OV10635_VTS >> 8 },
+	{ 0x3835, OV10635_VTS & 0xff },
+	{ 0x302e, 0x01 },
+};
+
+#endif /* __RDACM20_OV10635_H__ */
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
new file mode 100644
index 000000000000..d96b2eb5ab1b
--- /dev/null
+++ b/drivers/media/i2c/rdacm20.c
@@ -0,0 +1,635 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMI RDACM20 GMSL Camera Driver
+ *
+ * Copyright (C) 2017-2018 Jacopo Mondi
+ * Copyright (C) 2017-2018 Kieran Bingham
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ * Copyright (C) 2017-2018 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+/*
+ * The camera is mode of an Omnivision OV10635 sensor connected to a Maxim
+ * MAX9271 GMSL serializer.
+ */
+
+#include <linux/delay.h>
+#include <linux/fwnode.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "rdacm20-ov10635.h"
+
+#define RDACM20_SENSOR_HARD_RESET
+
+#define MAX9271_I2C_ADDRESS		0x40
+
+/* Register 0x04 */
+#define MAX9271_SEREN			BIT(7)
+#define MAX9271_CLINKEN			BIT(6)
+#define MAX9271_PRBSEN			BIT(5)
+#define MAX9271_SLEEP			BIT(4)
+#define MAX9271_INTTYPE_I2C		(0 << 2)
+#define MAX9271_INTTYPE_UART		(1 << 2)
+#define MAX9271_INTTYPE_NONE		(2 << 2)
+#define MAX9271_REVCCEN			BIT(1)
+#define MAX9271_FWDCCEN			BIT(0)
+/* Register 0x07 */
+#define MAX9271_DBL			BIT(7)
+#define MAX9271_DRS			BIT(6)
+#define MAX9271_BWS			BIT(5)
+#define MAX9271_ES			BIT(4)
+#define MAX9271_HVEN			BIT(2)
+#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
+#define MAX9271_EDC_6BIT_CRC		(1 << 0)
+#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
+/* Register 0x08 */
+#define MAX9271_INVVS			BIT(7)
+#define MAX9271_INVHS			BIT(6)
+#define MAX9271_REV_LOGAIN		BIT(3)
+#define MAX9271_REV_HIVTH		BIT(0)
+/* Register 0x09 */
+#define MAX9271_ID			0x09
+/* Register 0x0d */
+#define MAX9271_I2CLOCACK		BIT(7)
+#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
+#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
+#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
+#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
+#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
+#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
+#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
+#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
+#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
+#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
+#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
+#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
+#define MAX9271_I2CSLVTO_NONE		(3 << 0)
+#define MAX9271_I2CSLVTO_1024US		(2 << 0)
+#define MAX9271_I2CSLVTO_256US		(1 << 0)
+#define MAX9271_I2CSLVTO_64US		(0 << 0)
+/* Register 0x0f */
+#define MAX9271_GPIO5OUT		BIT(5)
+#define MAX9271_GPIO4OUT		BIT(4)
+#define MAX9271_GPIO3OUT		BIT(3)
+#define MAX9271_GPIO2OUT		BIT(2)
+#define MAX9271_GPIO1OUT		BIT(1)
+#define MAX9271_SETGPO			BIT(0)
+/* Register 0x15 */
+#define MAX9271_PCLKDET			BIT(0)
+
+#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
+#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
+#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
+
+#define OV10635_I2C_ADDRESS		0x30
+
+#define OV10635_SOFTWARE_RESET		0x0103
+#define OV10635_PID			0x300a
+#define OV10635_VER			0x300b
+#define OV10635_SC_CMMN_SCCB_ID		0x300c
+#define OV10635_SC_CMMN_SCCB_ID_SELECT	BIT(0)
+#define OV10635_VERSION			0xa635
+
+#define OV10635_WIDTH			1280
+#define OV10635_HEIGHT			800
+#define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
+/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
+
+struct rdacm20_device {
+	struct i2c_client		*client;
+	struct i2c_client		*sensor;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_ctrl_handler	ctrls;
+};
+
+static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct rdacm20_device, sd);
+}
+
+static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
+{
+	return sd_to_rdacm20(i2c_get_clientdata(client));
+}
+
+static int max9271_read(struct rdacm20_device *dev, u8 reg)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
+
+	ret = i2c_smbus_read_byte_data(dev->client, reg);
+	if (ret < 0)
+		dev_dbg(&dev->client->dev,
+			"%s: register 0x%02x read failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int max9271_write(struct rdacm20_device *dev, u8 reg, u8 val)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
+
+	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
+	if (ret < 0)
+		dev_err(&dev->client->dev,
+			"%s: register 0x%02x write failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
+{
+	u8 buf[2] = { reg >> 8, reg & 0xff };
+	int ret;
+
+	ret = i2c_master_send(dev->sensor, buf, 2);
+	if (ret == 2)
+		ret = i2c_master_recv(dev->sensor, buf, 2);
+
+	if (ret < 0) {
+		dev_dbg(&dev->client->dev,
+			"%s: register 0x%04x read failed (%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	return (buf[0] << 8) | buf[1];
+}
+
+static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+	u8 buf[3] = { reg >> 8, reg & 0xff, val };
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+
+	ret = i2c_master_send(dev->sensor, buf, 3);
+	return ret < 0 ? ret : 0;
+}
+
+static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+	int ret;
+
+	ret = __ov10635_write(dev, reg, val);
+	if (ret < 0)
+		dev_err(&dev->client->dev,
+			"%s: register 0x%04x write failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int ov10635_set_regs(struct rdacm20_device *dev,
+			    const struct ov10635_reg *regs,
+			    unsigned int nr_regs)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < nr_regs; i++) {
+		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
+		if (ret) {
+			dev_err(&dev->client->dev,
+				"%s: register %u (0x%04x) write failed (%d)\n",
+				__func__, i, regs[i].reg, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * rdacm20_pclk_detect() - Detect valid pixel clock from image sensor
+ *
+ * Wait up to 10ms for a valid pixel clock.
+ *
+ * Returns 0 for success, < 0 for pixel clock not properly detected
+ */
+static int rdacm20_pclk_detect(struct rdacm20_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < 100; i++) {
+		ret = max9271_read(dev, 0x15);
+		if (ret < 0)
+			return ret;
+
+		if (ret & MAX9271_PCLKDET)
+			return 0;
+
+		usleep_range(50, 100);
+	}
+
+	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
+	return -EIO;
+}
+
+static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rdacm20_device *dev = sd_to_rdacm20(sd);
+	int ret;
+
+	if (enable) {
+		ret = rdacm20_pclk_detect(dev);
+		if (ret)
+			return ret;
+
+		/* Enable the serial link. */
+		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
+			      MAX9271_FWDCCEN);
+	} else {
+		/* Disable the serial link. */
+		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
+			      MAX9271_FWDCCEN);
+	}
+
+	return 0;
+}
+
+static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2_DPHY;
+
+	return 0;
+}
+
+static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = OV10635_FORMAT;
+
+	return 0;
+}
+
+static int rdacm20_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width		= OV10635_WIDTH;
+	mf->height		= OV10635_HEIGHT;
+	mf->code		= OV10635_FORMAT;
+	mf->colorspace		= V4L2_COLORSPACE_RAW;
+	mf->field		= V4L2_FIELD_NONE;
+	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;
+	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;
+	mf->xfer_func		= V4L2_XFER_FUNC_NONE;
+
+	return 0;
+}
+
+static struct v4l2_subdev_video_ops rdacm20_video_ops = {
+	.s_stream	= rdacm20_s_stream,
+	.g_mbus_config	= rdacm20_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
+	.enum_mbus_code = rdacm20_enum_mbus_code,
+	.get_fmt	= rdacm20_get_fmt,
+	.set_fmt	= rdacm20_get_fmt,
+};
+
+static struct v4l2_subdev_ops rdacm20_subdev_ops = {
+	.video		= &rdacm20_video_ops,
+	.pad		= &rdacm20_subdev_pad_ops,
+};
+
+static int max9271_configure_i2c(struct rdacm20_device *dev)
+{
+	/*
+	 * Configure the I2C bus:
+	 *
+	 * - Enable high thresholds on the reverse channel
+	 * - Disable artificial ACK and set I2C speed
+	 */
+	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
+	usleep_range(5000, 8000);
+
+	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
+		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+
+static int max9271_configure_gmsl_link(struct rdacm20_device *dev)
+{
+	/*
+	 * Disable the serial link and enable the configuration link to allow
+	 * the control channel to operate in a low-speed mode in the absence of
+	 * the serial link clock.
+	 */
+	max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
+		      MAX9271_FWDCCEN);
+
+	/*
+	 * The serializer temporarily disables the reverse control channel for
+	 * 350µs after starting/stopping the forward serial link, but the
+	 * deserializer synchronization time isn't clearly documented.
+	 *
+	 * According to the serializer datasheet we should wait 3ms, while
+	 * according to the deserializer datasheet we should wait 5ms.
+	 *
+	 * Short delays here appear to show bit-errors in the writes following.
+	 * Therefore a conservative delay seems best here.
+	 */
+	usleep_range(5000, 8000);
+
+	/*
+	 * Configure the GMSL link:
+	 *
+	 * - Double input mode, high data rate, 24-bit mode
+	 * - Latch input data on PCLKIN rising edge
+	 * - Enable HS/VS encoding
+	 * - 1-bit parity error detection
+	 */
+	max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
+		      MAX9271_EDC_1BIT_PARITY);
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+
+static int max9271_verify_id(struct rdacm20_device *dev)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x1e);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	if (ret != MAX9271_ID) {
+		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
+{
+	int ret;
+
+	/* Change the MAX9271 I2C address. */
+	ret = max9271_write(dev, 0x00, addr << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	dev->client->addr = addr;
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int rdacm20_initialize(struct rdacm20_device *dev)
+{
+	u32 addrs[2];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
+					 addrs, ARRAY_SIZE(addrs));
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Invalid DT reg property\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * FIXME: The MAX9271 boots at a default address that we will change to
+	 * the address specified in DT. Set the client address back to the
+	 * default for initial communication.
+	 */
+	dev->client->addr = MAX9271_I2C_ADDRESS;
+
+	/* Verify communication with the MAX9271. */
+	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
+
+	/*
+	 *  Ensure that we have a good link configuration before attempting to
+	 *  identify the device.
+	 */
+	max9271_configure_i2c(dev);
+	max9271_configure_gmsl_link(dev);
+
+	ret = max9271_verify_id(dev);
+	if (ret < 0)
+		return ret;
+
+	ret = max9271_configure_address(dev, addrs[0]);
+	if (ret < 0)
+		return ret;
+
+	/* Reset and verify communication with the OV10635. */
+#ifdef RDACM20_SENSOR_HARD_RESET
+	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
+	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
+	mdelay(10);
+	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
+	mdelay(10);
+#else
+	/* Perform a software reset. */
+	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
+		return -ENXIO;
+	}
+
+	udelay(100);
+#endif
+
+	ret = ov10635_read16(dev, OV10635_PID);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	if (ret != OV10635_VERSION) {
+		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
+
+	/* Change the sensor I2C address. */
+	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
+			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"OV10635 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	dev->sensor->addr = addrs[1];
+	usleep_range(3500, 5000);
+
+	/* Program the 0V10635 initial configuration. */
+	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
+			       ARRAY_SIZE(ov10635_regs_wizard));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rdacm20_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct rdacm20_device *dev;
+	struct fwnode_handle *ep;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->client = client;
+
+	/* Create the dummy I2C client for the sensor. */
+	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
+	if (!dev->sensor) {
+		ret = -ENXIO;
+		goto error;
+	}
+
+	/* Initialize the hardware. */
+	ret = rdacm20_initialize(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Initialize and register the subdevice. */
+	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
+	dev->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	v4l2_ctrl_handler_init(&dev->ctrls, 1);
+	/*
+	 * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
+	 * hardcoded frequency in the BSP CSI-2 receiver driver.
+	 */
+	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
+			  50000000, 1, 50000000);
+	dev->sd.ctrl_handler = &dev->ctrls;
+
+	ret = dev->ctrls.error;
+	if (ret)
+		goto error;
+
+	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
+	dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
+	if (ret < 0)
+		goto error;
+
+	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+	if (!ep) {
+		dev_err(&client->dev,
+			"Unable to get endpoint in node %pOF\n",
+			client->dev.of_node);
+		ret = -ENOENT;
+		goto error;
+	}
+	dev->sd.fwnode = ep;
+
+	ret = v4l2_async_register_subdev(&dev->sd);
+	if (ret)
+		goto error_put_node;
+
+	return 0;
+
+error_put_node:
+	fwnode_handle_put(ep);
+error:
+	media_entity_cleanup(&dev->sd.entity);
+	if (dev->sensor)
+		i2c_unregister_device(dev->sensor);
+	kfree(dev);
+
+	dev_err(&client->dev, "probe failed\n");
+
+	return ret;
+}
+
+static int rdacm20_remove(struct i2c_client *client)
+{
+	struct rdacm20_device *dev = i2c_to_rdacm20(client);
+
+	fwnode_handle_put(dev->sd.fwnode);
+	v4l2_async_unregister_subdev(&dev->sd);
+	media_entity_cleanup(&dev->sd.entity);
+	i2c_unregister_device(dev->sensor);
+	kfree(dev);
+
+	return 0;
+}
+
+static void rdacm20_shutdown(struct i2c_client *client)
+{
+	struct rdacm20_device *dev = i2c_to_rdacm20(client);
+
+	/* make sure stream off during shutdown (reset/reboot) */
+	rdacm20_s_stream(&dev->sd, 0);
+}
+
+static const struct i2c_device_id rdacm20_id[] = {
+	{ "rdacm20", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, rdacm20_id);
+
+static const struct of_device_id rdacm20_of_ids[] = {
+	{ .compatible = "imi,rdacm20", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
+
+static struct i2c_driver rdacm20_i2c_driver = {
+	.driver	= {
+		.name	= "rdacm20",
+		.of_match_table = rdacm20_of_ids,
+	},
+	.probe		= rdacm20_probe,
+	.remove		= rdacm20_remove,
+	.shutdown	= rdacm20_shutdown,
+	.id_table	= rdacm20_id,
+};
+
+module_i2c_driver(rdacm20_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");