This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

BEAGLEBOARD-X15: AM5728 beagle board to FPGA connection via P16 connector video not capturing?

Part Number: BEAGLEBOARD-X15

Hi sir,

I am Ramachandra ,

I am using Beagleboard-x15 with ti-processor-sdk-linux-am57xx-evm-06.01.00.08 and u-boot 2019.1 and Linux 4.19.59 using,

Actually Beagle board x-15 of p16 connector is connected our custom fpga board from fpga to rugged camera(1080p30) connected.(Beagle-->custom fpga(I2C3)-->camera)

From beagle board x15 we are able to communicating FPGA device through i2c3 and getting Device ID of FPGA.

Actually FPGA device i am taken like OV5640 device.

In DTS:

 

&vin3a {
        vin3a_ep: endpoint {
                remote-endpoint = <&cam>;
                slave-mode;
        };
};
&vip2 {
        status = "okay";
};
&i2c3 {
	status = "okay";
	clock-frequency = <400000>;
        ov5640@3C {
                compatible = "ovti,ov5640";
                reg = <0x3C>;
                clock-names = "xclk";
        		clock-frequency = <24000000>;

                port {
                        cam: endpoint {
                                bus-width = <16>;
                                hsync-active = <1>;
                                vsync-active = <1>;
                                pclk-sample = <1>;
                                remote-endpoint = <&vin3a_ep>;
                        };
                };
        };
};

OV5640 code some changes i have done: 

/*
 * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
 * Copyright (C) 2014-2017 Mentor Graphics Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>

/* min/typical/max system clock (xclk) frequencies */
#define OV5640_XCLK_MIN  6000000
#define OV5640_XCLK_MAX 74250000

#define OV5640_DEFAULT_SLAVE_ID 0x3c

#define OV5640_REG_SYS_CARAVEL		0x3000
#define OV5640_REG_SYS_RESET02		0x3002
#define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
#define OV5640_REG_SYS_CTRL0		0x3008
#define OV5640_REG_CHIP_ID		0x300a
#define OV5640_REG_IO_MIPI_CTRL00	0x300e
#define OV5640_REG_PAD_OUTPUT_ENABLE01	0x3017
#define OV5640_REG_PAD_OUTPUT_ENABLE02	0x3018
#define OV5640_REG_PAD_OUTPUT00		0x3019
#define OV5640_REG_SYSTEM_CONTROL1	0x302e
#define OV5640_REG_SC_PLL_CTRL0		0x3034
#define OV5640_REG_SC_PLL_CTRL1		0x3035
#define OV5640_REG_SC_PLL_CTRL2		0x3036
#define OV5640_REG_SC_PLL_CTRL3		0x3037
#define OV5640_REG_SLAVE_ID		0x3100
#define OV5640_REG_SCCB_SYS_CTRL1	0x3103
#define OV5640_REG_SYS_ROOT_DIVIDER	0x3108
#define OV5640_REG_AWB_R_GAIN		0x3400
#define OV5640_REG_AWB_G_GAIN		0x3402
#define OV5640_REG_AWB_B_GAIN		0x3404
#define OV5640_REG_AWB_MANUAL_CTRL	0x3406
#define OV5640_REG_AEC_PK_EXPOSURE_HI	0x3500
#define OV5640_REG_AEC_PK_EXPOSURE_MED	0x3501
#define OV5640_REG_AEC_PK_EXPOSURE_LO	0x3502
#define OV5640_REG_AEC_PK_MANUAL	0x3503
#define OV5640_REG_AEC_PK_REAL_GAIN	0x350a
#define OV5640_REG_AEC_PK_VTS		0x350c
#define OV5640_REG_TIMING_DVPHO		0x3808
#define OV5640_REG_TIMING_DVPVO		0x380a
#define OV5640_REG_TIMING_HTS		0x380c
#define OV5640_REG_TIMING_VTS		0x380e
#define OV5640_REG_TIMING_TC_REG20	0x3820
#define OV5640_REG_TIMING_TC_REG21	0x3821
#define OV5640_REG_AEC_CTRL00		0x3a00
#define OV5640_REG_AEC_B50_STEP		0x3a08
#define OV5640_REG_AEC_B60_STEP		0x3a0a
#define OV5640_REG_AEC_CTRL0D		0x3a0d
#define OV5640_REG_AEC_CTRL0E		0x3a0e
#define OV5640_REG_AEC_CTRL0F		0x3a0f
#define OV5640_REG_AEC_CTRL10		0x3a10
#define OV5640_REG_AEC_CTRL11		0x3a11
#define OV5640_REG_AEC_CTRL1B		0x3a1b
#define OV5640_REG_AEC_CTRL1E		0x3a1e
#define OV5640_REG_AEC_CTRL1F		0x3a1f
#define OV5640_REG_HZ5060_CTRL00	0x3c00
#define OV5640_REG_HZ5060_CTRL01	0x3c01
#define OV5640_REG_SIGMADELTA_CTRL0C	0x3c0c
#define OV5640_REG_FRAME_CTRL01		0x4202
#define OV5640_REG_FORMAT_CONTROL00	0x4300
#define OV5640_REG_POLARITY_CTRL00	0x4740
#define OV5640_REG_MIPI_CTRL00		0x4800
#define OV5640_REG_DEBUG_MODE		0x4814
#define OV5640_REG_ISP_FORMAT_MUX_CTRL	0x501f
#define OV5640_REG_PRE_ISP_TEST_SET1	0x503d
#define OV5640_REG_SDE_CTRL0		0x5580
#define OV5640_REG_SDE_CTRL1		0x5581
#define OV5640_REG_SDE_CTRL3		0x5583
#define OV5640_REG_SDE_CTRL4		0x5584
#define OV5640_REG_SDE_CTRL5		0x5585
#define OV5640_REG_AVG_READOUT		0x56a1

enum ov5640_mode_id {
	OV5640_MODE_QCIF_176_144 = 0,
	OV5640_MODE_QVGA_320_240,
	OV5640_MODE_VGA_640_480,
	OV5640_MODE_NTSC_720_480,
	OV5640_MODE_PAL_720_576,
	OV5640_MODE_XGA_1024_768,
	OV5640_MODE_720P_1280_720,
	OV5640_MODE_1080P_1920_1080,
	OV5640_MODE_QSXGA_2592_1944,
	OV5640_NUM_MODES,
};

enum ov5640_frame_rate {
	OV5640_15_FPS = 0,
	OV5640_30_FPS,
	OV5640_60_FPS,
	OV5640_NUM_FRAMERATES,
};

struct ov5640_pixfmt {
	u32 code;
	u32 colorspace;
};

static const struct ov5640_pixfmt ov5640_formats[] = {
	{ MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
	{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
	{ MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
};

/*
 * FIXME: remove this when a subdev API becomes available
 * to set the MIPI CSI-2 virtual channel.
 */
static unsigned int virtual_channel;
module_param(virtual_channel, uint, 0444);
MODULE_PARM_DESC(virtual_channel,
		 "MIPI CSI-2 virtual channel (0..3), default 0");

static const int ov5640_framerates[] = {
	[OV5640_15_FPS] = 15,
	[OV5640_30_FPS] = 30,
	[OV5640_60_FPS] = 60,
};

/* regulator supplies */
static const char * const ov5640_supply_name[] = {
	"DOVDD", /* Digital I/O (1.8V) supply */
	"DVDD",  /* Digital Core (1.5V) supply */
	"AVDD",  /* Analog (2.8V) supply */
};

#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)

/*
 * Image size under 1280 * 960 are SUBSAMPLING
 * Image size upper 1280 * 960 are SCALING
 */
enum ov5640_downsize_mode {
	SUBSAMPLING,
	SCALING,
};

struct reg_value {
	u16 reg_addr;
	u8 val;
	u8 mask;
	u32 delay_ms;
};

struct ov5640_mode_info {
	enum ov5640_mode_id id;
	enum ov5640_downsize_mode dn_mode;
	u32 hact;
	u32 htot;
	u32 vact;
	u32 vtot;
	const struct reg_value *reg_data;
	u32 reg_data_size;
};

struct ov5640_ctrls {
	struct v4l2_ctrl_handler handler;
	struct {
		struct v4l2_ctrl *pixel_rate;
	};
	struct {
		struct v4l2_ctrl *auto_exp;
		struct v4l2_ctrl *exposure;
	};
	struct {
		struct v4l2_ctrl *auto_wb;
		struct v4l2_ctrl *blue_balance;
		struct v4l2_ctrl *red_balance;
	};
	struct {
		struct v4l2_ctrl *auto_gain;
		struct v4l2_ctrl *gain;
	};
	struct v4l2_ctrl *brightness;
	struct v4l2_ctrl *light_freq;
	struct v4l2_ctrl *saturation;
	struct v4l2_ctrl *contrast;
	struct v4l2_ctrl *hue;
	struct v4l2_ctrl *test_pattern;
	struct v4l2_ctrl *hflip;
	struct v4l2_ctrl *vflip;
};

struct ov5640_dev {
	struct i2c_client *i2c_client;
	struct v4l2_subdev sd;
	struct media_pad pad;
	struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
	struct clk *xclk; /* system clock to OV5640 */
	u32 xclk_freq;

	struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
	struct gpio_desc *reset_gpio;
	struct gpio_desc *pwdn_gpio;
	bool   upside_down;

	/* lock to protect all members below */
	struct mutex lock;

	int power_count;

	struct v4l2_mbus_framefmt fmt;
	bool pending_fmt_change;

	const struct ov5640_mode_info *current_mode;
	const struct ov5640_mode_info *last_mode;
	enum ov5640_frame_rate current_fr;
	struct v4l2_fract frame_interval;
	u64 pixel_rate;

	struct ov5640_ctrls ctrls;

	u32 prev_sysclk, prev_hts;
	u32 ae_low, ae_high, ae_target;

	bool pending_mode_change;
	bool streaming;
};

static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
{
	return container_of(sd, struct ov5640_dev, sd);
}

static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
{
	return &container_of(ctrl->handler, struct ov5640_dev,
			     ctrls.handler)->sd;
}

/*
 * FIXME: all of these register tables are likely filled with
 * entries that set the register to their power-on default values,
 * and which are otherwise not touched by this driver. Those entries
 * should be identified and removed to speed register load time
 * over i2c.
 */
/* YUV422 UYVY VGA@30fps */
static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
	{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
	{0x3630, 0x36, 0, 0},
	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
	{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
	{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
	{0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
	{0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
	{0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
	{0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
	{0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
	{0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
	{0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
	{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
	{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
	{0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
	{0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
	{0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0},
	{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
	{0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
	{0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
	{0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
	{0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
	{0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
	{0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
	{0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
	{0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
	{0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
	{0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
	{0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
	{0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
	{0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
	{0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
	{0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
	{0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
	{0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
	{0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
	{0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
	{0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
	{0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
	{0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
	{0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
	{0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
	{0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
	{0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
	{0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
	{0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
	{0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
	{0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
	{0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
	{0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
	{0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
	{0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
	{0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
	{0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
	{0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
	{0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
	{0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
	{0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
	{0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
	{0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
	{0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
	{0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
	{0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
	{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
	{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
	{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
};

static const struct reg_value ov5640_setting_VGA_640_480[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_XGA_1024_768[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_QVGA_320_240[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_QCIF_176_144[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_NTSC_720_480[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_PAL_720_576[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};

static const struct reg_value ov5640_setting_720P_1280_720[] = {
	{0x3c07, 0x07, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x31, 0, 0},
	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
};

static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x11, 0, 0},
	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
	{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
	{0x4005, 0x1a, 0, 0},
};

static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
	{0x3c07, 0x08, 0, 0},
	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
	{0x3814, 0x11, 0, 0},
	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
	{0x3810, 0x00, 0, 0},
	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
};

/* power-on sensor init reg table */
static const struct ov5640_mode_info ov5640_mode_init_data = {
	0, SUBSAMPLING, 640, 1896, 480, 984,
	ov5640_init_setting_30fps_VGA,
	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
};

static const struct ov5640_mode_info
ov5640_mode_data[OV5640_NUM_MODES] = {
	{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
	 176, 1896, 144, 984,
	 ov5640_setting_QCIF_176_144,
	 ARRAY_SIZE(ov5640_setting_QCIF_176_144)},
	{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
	 320, 1896, 240, 984,
	 ov5640_setting_QVGA_320_240,
	 ARRAY_SIZE(ov5640_setting_QVGA_320_240)},
	{OV5640_MODE_VGA_640_480, SUBSAMPLING,
	 640, 1896, 480, 1080,
	 ov5640_setting_VGA_640_480,
	 ARRAY_SIZE(ov5640_setting_VGA_640_480)},
	{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
	 720, 1896, 480, 984,
	 ov5640_setting_NTSC_720_480,
	 ARRAY_SIZE(ov5640_setting_NTSC_720_480)},
	{OV5640_MODE_PAL_720_576, SUBSAMPLING,
	 720, 1896, 576, 984,
	 ov5640_setting_PAL_720_576,
	 ARRAY_SIZE(ov5640_setting_PAL_720_576)},
	{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
	 1024, 1896, 768, 1080,
	 ov5640_setting_XGA_1024_768,
	 ARRAY_SIZE(ov5640_setting_XGA_1024_768)},
	{OV5640_MODE_720P_1280_720, SUBSAMPLING,
	 1280, 1892, 720, 740,
	 ov5640_setting_720P_1280_720,
	 ARRAY_SIZE(ov5640_setting_720P_1280_720)},
	{OV5640_MODE_1080P_1920_1080, SCALING,
	 1920, 2500, 1080, 1120,
	 ov5640_setting_1080P_1920_1080,
	 ARRAY_SIZE(ov5640_setting_1080P_1920_1080)},
	{OV5640_MODE_QSXGA_2592_1944, SCALING,
	 2592, 2844, 1944, 1968,
	 ov5640_setting_QSXGA_2592_1944,
	 ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)},
};

static int ov5640_init_slave_id(struct ov5640_dev *sensor)
{
	struct i2c_client *client = sensor->i2c_client;
	struct i2c_msg msg;
	u8 buf[3];
	int ret;

	dev_err(&client->dev, "%s: client->addr= %x\n", __func__,client->addr );

	if (client->addr == OV5640_DEFAULT_SLAVE_ID)
		return 0;

	buf[0] = OV5640_REG_SLAVE_ID >> 8;
	buf[1] = OV5640_REG_SLAVE_ID & 0xff;
	buf[2] = client->addr << 1;

	msg.addr = OV5640_DEFAULT_SLAVE_ID;
	msg.flags = 0;
	msg.buf = buf;
	msg.len = sizeof(buf);

	ret = i2c_transfer(client->adapter, &msg, 1);
	if (ret < 0) {
		dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
		return ret;
	}
	
	return 0;
}

static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
{
	struct i2c_client *client = sensor->i2c_client;
	struct i2c_msg msg;
	u8 buf[3];
	int ret;

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

	msg.addr = client->addr;
	msg.flags = client->flags;
	msg.buf = buf;
	msg.len = sizeof(buf);

	ret = i2c_transfer(client->adapter, &msg, 1);
	if (ret < 0) {
		dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
			__func__, reg, val);
		return ret;
	}

	return 0;
}
static int ov5640_cispl_read_reg(struct ov5640_dev *sensor, u16 reg, u16 *val)
{
	struct i2c_client *client = sensor->i2c_client;
	struct i2c_msg msg[1];
	u8 buf[2];
	int ret;

	buf[0] = reg & 0xff;
	
	msg[0].addr = client->addr;
	msg[0].flags = client->flags | I2C_M_RD;
	msg[0].buf = buf;
	msg[0].len = 2;

	ret = i2c_transfer(client->adapter, msg, 1);
	if (ret < 0) {
		dev_err(&client->dev, "%s: error: reg=%x\n",
			__func__, reg);
		return ret;
	}

	*val = buf[0]<<8 | buf[1];;
//	*val = buf[0];
	return 0;
}
static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
{
	struct i2c_client *client = sensor->i2c_client;
	struct i2c_msg msg[2];
	u8 buf[2];
	int ret;

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

	msg[0].addr = client->addr;
	msg[0].flags = client->flags;
	msg[0].buf = buf;
	msg[0].len = sizeof(buf);

	msg[1].addr = client->addr;
	msg[1].flags = client->flags | I2C_M_RD;
	msg[1].buf = buf;
	msg[1].len = 1;

	ret = i2c_transfer(client->adapter, msg, 2);
	if (ret < 0) {
		dev_err(&client->dev, "%s: error: reg=%x\n",
			__func__, reg);
		return ret;
	}

	*val = buf[0];
	return 0;
}

static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
{
	u8 hi, lo;
	int ret;

	ret = ov5640_read_reg(sensor, reg, &hi);
	if (ret)
		return ret;
	ret = ov5640_read_reg(sensor, reg + 1, &lo);
	if (ret)
		return ret;

	*val = ((u16)hi << 8) | (u16)lo;
	return 0;
}

static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
{
	int ret;

	ret = ov5640_write_reg(sensor, reg, val >> 8);
	if (ret)
		return ret;

	return ov5640_write_reg(sensor, reg + 1, val & 0xff);
}

static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
			  u8 mask, u8 val)
{
	u8 readval;
	int ret;

	ret = ov5640_read_reg(sensor, reg, &readval);
	if (ret)
		return ret;

	readval &= ~mask;
	val &= mask;
	val |= readval;

	return ov5640_write_reg(sensor, reg, val);
}

/*
 * After trying the various combinations, reading various
 * documentations spreaded around the net, and from the various
 * feedback, the clock tree is probably as follows:
 *
 *   +--------------+
 *   |  Ext. Clock  |
 *   +-+------------+
 *     |  +----------+
 *     +->|   PLL1   | - reg 0x3036, for the multiplier
 *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
 *          |  +--------------+
 *          +->| System Clock |  - reg 0x3035, bits 4-7
 *             +-+------------+
 *               |  +--------------+
 *               +->| MIPI Divider | - reg 0x3035, bits 0-3
 *               |  +-+------------+
 *               |    +----------------> MIPI SCLK
 *               |    +  +-----+
 *               |    +->| / 2 |-------> MIPI BIT CLK
 *               |       +-----+
 *               |  +--------------+
 *               +->| PLL Root Div | - reg 0x3037, bit 4
 *                  +-+------------+
 *                    |  +---------+
 *                    +->| Bit Div | - reg 0x3035, bits 0-3
 *                       +-+-------+
 *                         |  +-------------+
 *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
 *                         |  +-+-----------+
 *                         |    +---------------> SCLK
 *                         |  +-------------+
 *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
 *                         |  +-+-----------+
 *                         |    +---------------> SCLK 2X
 *                         |  +-------------+
 *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
 *                            ++------------+
 *                             +  +-----------+
 *                             +->|   P_DIV   | - reg 0x3035, bits 0-3
 *                                +-----+-----+
 *                                       +------------> PCLK
 *
 * This is deviating from the datasheet at least for the register
 * 0x3108, since it's said here that the PCLK would be clocked from
 * the PLL.
 *
 * There seems to be also (unverified) constraints:
 *  - the PLL pre-divider output rate should be in the 4-27MHz range
 *  - the PLL multiplier output rate should be in the 500-1000MHz range
 *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
 *
 * In the two latter cases, these constraints are met since our
 * factors are hardcoded. If we were to change that, we would need to
 * take this into account. The only varying parts are the PLL
 * multiplier and the system clock divider, which are shared between
 * all these clocks so won't cause any issue.
 */

/*
 * This is supposed to be ranging from 1 to 8, but the value is always
 * set to 3 in the vendor kernels.
 */
#define OV5640_PLL_PREDIV	3

#define OV5640_PLL_MULT_MIN	4
#define OV5640_PLL_MULT_MAX	252

/*
 * This is supposed to be ranging from 1 to 16, but the value is
 * always set to either 1 or 2 in the vendor kernels.
 */
#define OV5640_SYSDIV_MIN	1
#define OV5640_SYSDIV_MAX	16

/*
 * Hardcode these values for scaler and non-scaler modes.
 * FIXME: to be re-calcualted for 1 data lanes setups
 */
#define OV5640_MIPI_DIV_PCLK	2
#define OV5640_MIPI_DIV_SCLK	1

/*
 * This is supposed to be ranging from 1 to 2, but the value is always
 * set to 2 in the vendor kernels.
 */
#define OV5640_PLL_ROOT_DIV			2
#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2		BIT(4)

/*
 * We only supports 8-bit formats at the moment
 */
#define OV5640_BIT_DIV				2
#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT		0x08

/*
 * This is supposed to be ranging from 1 to 8, but the value is always
 * set to 2 in the vendor kernels.
 */
#define OV5640_SCLK_ROOT_DIV	2

/*
 * This is hardcoded so that the consistency is maintained between SCLK and
 * SCLK 2x.
 */
#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)

/*
 * This is supposed to be ranging from 1 to 8, but the value is always
 * set to 1 in the vendor kernels.
 */
#define OV5640_PCLK_ROOT_DIV			1
#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS	0x00

static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
					    u8 pll_prediv, u8 pll_mult,
					    u8 sysdiv)
{
	unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult;

	/* PLL1 output cannot exceed 1GHz. */
	if (sysclk / 1000000 > 1000)
		return 0;

	return sysclk / sysdiv;
}

static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
					 unsigned long rate,
					 u8 *pll_prediv, u8 *pll_mult,
					 u8 *sysdiv)
{
	unsigned long best = ~0;
	u8 best_sysdiv = 1, best_mult = 1;
	u8 _sysdiv, _pll_mult;

	for (_sysdiv = OV5640_SYSDIV_MIN;
	     _sysdiv <= OV5640_SYSDIV_MAX;
	     _sysdiv++) {
		for (_pll_mult = OV5640_PLL_MULT_MIN;
		     _pll_mult <= OV5640_PLL_MULT_MAX;
		     _pll_mult++) {
			unsigned long _rate;

			/*
			 * The PLL multiplier cannot be odd if above
			 * 127.
			 */
			if (_pll_mult > 127 && (_pll_mult % 2))
				continue;

			_rate = ov5640_compute_sys_clk(sensor,
						       OV5640_PLL_PREDIV,
						       _pll_mult, _sysdiv);

			/*
			 * We have reached the maximum allowed PLL1 output,
			 * increase sysdiv.
			 */
			if (!rate)
				break;

			/*
			 * Prefer rates above the expected clock rate than
			 * below, even if that means being less precise.
			 */
			if (_rate < rate)
				continue;

			if (abs(rate - _rate) < abs(rate - best)) {
				best = _rate;
				best_sysdiv = _sysdiv;
				best_mult = _pll_mult;
			}

			if (_rate == rate)
				goto out;
		}
	}

out:
	*sysdiv = best_sysdiv;
	*pll_prediv = OV5640_PLL_PREDIV;
	*pll_mult = best_mult;

	return best;
}

/*
 * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
 *			    for the MIPI CSI-2 output.
 *
 * @rate: The requested bandwidth per lane in bytes per second.
 *	  'Bandwidth Per Lane' is calculated as:
 *	  bpl = HTOT * VTOT * FPS * bpp / num_lanes;
 *
 * This function use the requested bandwidth to calculate:
 * - sample_rate = bpl / (bpp / num_lanes);
 *	         = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
 *
 * - mipi_sclk   = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
 *
 * with these fixed parameters:
 *	PLL_RDIV	= 2;
 *	BIT_DIVIDER	= 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
 *	PCLK_DIV	= 1;
 *
 * The MIPI clock generation differs for modes that use the scaler and modes
 * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
 * BIT CLk, and thus:
 *
 * - mipi_sclk = bpl / MIPI_DIV / 2;
 *   MIPI_DIV = 1;
 *
 * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
 * from the pixel clock, and thus:
 *
 * - sample_rate = bpl / (bpp / num_lanes);
 *	         = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
 *		 = bpl / (4 * MIPI_DIV / num_lanes);
 * - MIPI_DIV	 = bpp / (4 * num_lanes);
 *
 * FIXME: this have been tested with 16bpp and 2 lanes setup only.
 * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
 * above formula for setups with 1 lane or image formats with different bpp.
 *
 * FIXME: this deviates from the sensor manual documentation which is quite
 * thin on the MIPI clock tree generation part.
 */
static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
				unsigned long rate)
{
	const struct ov5640_mode_info *mode = sensor->current_mode;
	u8 prediv, mult, sysdiv;
	u8 mipi_div;
	int ret;

	/*
	 * 1280x720 is reported to use 'SUBSAMPLING' only,
	 * but according to the sensor manual it goes through the
	 * scaler before subsampling.
	 */
	if (mode->dn_mode == SCALING ||
	   (mode->id == OV5640_MODE_720P_1280_720))
		mipi_div = OV5640_MIPI_DIV_SCLK;
	else
		mipi_div = OV5640_MIPI_DIV_PCLK;

	ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
			     0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
			     0xff, sysdiv << 4 | mipi_div);
	if (ret)
		return ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
	if (ret)
		return ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
			     0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
	if (ret)
		return ret;

	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
			      0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
}

static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
				      unsigned long rate,
				      u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
				      u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
{
	unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
				OV5640_PCLK_ROOT_DIV;

	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
				    sysdiv);
	*pll_rdiv = OV5640_PLL_ROOT_DIV;
	*bit_div = OV5640_BIT_DIV;
	*pclk_div = OV5640_PCLK_ROOT_DIV;

	return _rate / *pll_rdiv / *bit_div / *pclk_div;
}

static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
{
	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
	int ret;

	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
			 &bit_div, &pclk_div);

	if (bit_div == 2)
		bit_div = 8;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
			     0x0f, bit_div);
	if (ret)
		return ret;

	/*
	 * We need to set sysdiv according to the clock, and to clear
	 * the MIPI divider.
	 */
	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
			     0xff, sysdiv << 4);
	if (ret)
		return ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
			     0xff, mult);
	if (ret)
		return ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
			     0x1f, prediv | ((pll_rdiv - 1) << 4));
	if (ret)
		return ret;

	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
			      (ilog2(pclk_div) << 4));
}

/* download ov5640 settings to sensor through i2c */
static int ov5640_set_timings(struct ov5640_dev *sensor,
			      const struct ov5640_mode_info *mode)
{
	int ret;

	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
	if (ret < 0)
		return ret;

	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
	if (ret < 0)
		return ret;

	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
	if (ret < 0)
		return ret;

	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
}

static int ov5640_load_regs(struct ov5640_dev *sensor,
			    const struct ov5640_mode_info *mode)
{
	const struct reg_value *regs = mode->reg_data;
	unsigned int i;
	u32 delay_ms;
	u16 reg_addr;
	u8 mask, val;
	int ret = 0;

	for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
		delay_ms = regs->delay_ms;
		reg_addr = regs->reg_addr;
		val = regs->val;
		mask = regs->mask;

		if (mask)
			ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
		else
			ret = ov5640_write_reg(sensor, reg_addr, val);
		if (ret)
			break;

		if (delay_ms)
			usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
	}

	return ov5640_set_timings(sensor, mode);
}

static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
{
	return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
			      BIT(0), on ? 0 : BIT(0));
}

/* read exposure, in number of line periods */
static int ov5640_get_exposure(struct ov5640_dev *sensor)
{
	int exp, ret;
	u8 temp;

	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
	if (ret)
		return ret;
	exp = ((int)temp & 0x0f) << 16;
	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
	if (ret)
		return ret;
	exp |= ((int)temp << 8);
	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
	if (ret)
		return ret;
	exp |= (int)temp;

	return exp >> 4;
}

/* write exposure, given number of line periods */
static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
{
	int ret;

	exposure <<= 4;

	ret = ov5640_write_reg(sensor,
			       OV5640_REG_AEC_PK_EXPOSURE_LO,
			       exposure & 0xff);
	if (ret)
		return ret;
	ret = ov5640_write_reg(sensor,
			       OV5640_REG_AEC_PK_EXPOSURE_MED,
			       (exposure >> 8) & 0xff);
	if (ret)
		return ret;
	return ov5640_write_reg(sensor,
				OV5640_REG_AEC_PK_EXPOSURE_HI,
				(exposure >> 16) & 0x0f);
}

static int ov5640_get_gain(struct ov5640_dev *sensor)
{
	u16 gain;
	int ret;

	ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
	if (ret)
		return ret;

	return gain & 0x3ff;
}

static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
{
	return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
				  (u16)gain & 0x3ff);
}

static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
{
	return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
			      BIT(1), on ? 0 : BIT(1));
}

static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
{
	int ret;
	unsigned int flags = sensor->ep.bus.parallel.flags;
	u8 pclk_pol = 0;
	u8 hsync_pol = 0;
	u8 vsync_pol = 0;

	/*
	 * Note about parallel port configuration.
	 *
	 * When configured in parallel mode, the OV5640 will
	 * output 10 bits data on DVP data lines [9:0].
	 * If only 8 bits data are wanted, the 8 bits data lines
	 * of the camera interface must be physically connected
	 * on the DVP data lines [9:2].
	 *
	 * Control lines polarity can be configured through
	 * devicetree endpoint control lines properties.
	 * If no endpoint control lines properties are set,
	 * polarity will be as below:
	 * - VSYNC:	active high
	 * - HREF:	active low
	 * - PCLK:	active low
	 */

	if (on) {
		/*
		 * configure parallel port control lines polarity
		 *
		 * POLARITY CTRL0
		 * - [5]:	PCLK polarity (0: active low, 1: active high)
		 * - [1]:	HREF polarity (0: active low, 1: active high)
		 * - [0]:	VSYNC polarity (mismatch here between
		 *		datasheet and hardware, 0 is active high
		 *		and 1 is active low...)
		 */
		if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
			pclk_pol = 1;
		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
			hsync_pol = 1;
		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
			vsync_pol = 1;

		ret = ov5640_write_reg(sensor,
				       OV5640_REG_POLARITY_CTRL00,
				       (pclk_pol << 5) |
				       (hsync_pol << 1) |
				       vsync_pol);

		if (ret)
			return ret;
	}

	/*
	 * powerdown MIPI TX/RX PHY & disable MIPI
	 *
	 * MIPI CONTROL 00
	 * 4:	 PWDN PHY TX
	 * 3:	 PWDN PHY RX
	 * 2:	 MIPI enable
	 */
	ret = ov5640_write_reg(sensor,
			       OV5640_REG_IO_MIPI_CTRL00, on ? 0x18 : 0);
	if (ret)
		return ret;

	/*
	 * enable VSYNC/HREF/PCLK DVP control lines
	 * & D[9:6] DVP data lines
	 *
	 * PAD OUTPUT ENABLE 01
	 * - 6:		VSYNC output enable
	 * - 5:		HREF output enable
	 * - 4:		PCLK output enable
	 * - [3:0]:	D[9:6] output enable
	 */
	ret = ov5640_write_reg(sensor,
			       OV5640_REG_PAD_OUTPUT_ENABLE01,
			       on ? 0x7f : 0);
	if (ret)
		return ret;

	/*
	 * enable D[5:0] DVP data lines
	 *
	 * PAD OUTPUT ENABLE 02
	 * - [7:2]:	D[5:0] output enable
	 */
	return ov5640_write_reg(sensor,
				OV5640_REG_PAD_OUTPUT_ENABLE02,
				on ? 0xfc : 0);
}

static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
{
	int ret;

	/*
	 * Enable/disable the MIPI interface
	 *
	 * 0x300e = on ? 0x45 : 0x40
	 *
	 * FIXME: the sensor manual (version 2.03) reports
	 * [7:5] = 000  : 1 data lane mode
	 * [7:5] = 001  : 2 data lanes mode
	 * But this settings do not work, while the following ones
	 * have been validated for 2 data lanes mode.
	 *
	 * [7:5] = 010	: 2 data lanes mode
	 * [4] = 0	: Power up MIPI HS Tx
	 * [3] = 0	: Power up MIPI LS Rx
	 * [2] = 1/0	: MIPI interface enable/disable
	 * [1:0] = 01/00: FIXME: 'debug'
	 */
	ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00,
			       on ? 0x45 : 0x40);
	if (ret)
		return ret;

	return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
				on ? 0x00 : 0x0f);
}

static int ov5640_get_sysclk(struct ov5640_dev *sensor)
{
	 /* calculate sysclk */
	u32 xvclk = sensor->xclk_freq / 10000;
	u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
	u32 sclk_rdiv_map[] = {1, 2, 4, 8};
	u32 bit_div2x = 1, sclk_rdiv, sysclk;
	u8 temp1, temp2;
	int ret;

	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
	if (ret)
		return ret;
	temp2 = temp1 & 0x0f;
	if (temp2 == 8 || temp2 == 10)
		bit_div2x = temp2 / 2;

	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
	if (ret)
		return ret;
	sysdiv = temp1 >> 4;
	if (sysdiv == 0)
		sysdiv = 16;

	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
	if (ret)
		return ret;
	multiplier = temp1;

	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
	if (ret)
		return ret;
	prediv = temp1 & 0x0f;
	pll_rdiv = ((temp1 >> 4) & 0x01) + 1;

	ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
	if (ret)
		return ret;
	temp2 = temp1 & 0x03;
	sclk_rdiv = sclk_rdiv_map[temp2];

	if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
		return -EINVAL;

	VCO = xvclk * multiplier / prediv;

	sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;

	return sysclk;
}

static int ov5640_set_night_mode(struct ov5640_dev *sensor)
{
	 /* read HTS from register settings */
	u8 mode;
	int ret;

	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
	if (ret)
		return ret;
	mode &= 0xfb;
	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
}

static int ov5640_get_hts(struct ov5640_dev *sensor)
{
	/* read HTS from register settings */
	u16 hts;
	int ret;

	ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
	if (ret)
		return ret;
	return hts;
}

static int ov5640_get_vts(struct ov5640_dev *sensor)
{
	u16 vts;
	int ret;

	ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
	if (ret)
		return ret;
	return vts;
}

static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
{
	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
}

static int ov5640_get_light_freq(struct ov5640_dev *sensor)
{
	/* get banding filter value */
	int ret, light_freq = 0;
	u8 temp, temp1;

	ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
	if (ret)
		return ret;

	if (temp & 0x80) {
		/* manual */
		ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
				      &temp1);
		if (ret)
			return ret;
		if (temp1 & 0x04) {
			/* 50Hz */
			light_freq = 50;
		} else {
			/* 60Hz */
			light_freq = 60;
		}
	} else {
		/* auto */
		ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
				      &temp1);
		if (ret)
			return ret;

		if (temp1 & 0x01) {
			/* 50Hz */
			light_freq = 50;
		} else {
			/* 60Hz */
		}
	}

	return light_freq;
}

static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
{
	u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
	int ret;

	/* read preview PCLK */
	ret = ov5640_get_sysclk(sensor);
	if (ret < 0)
		return ret;
	if (ret == 0)
		return -EINVAL;
	sensor->prev_sysclk = ret;
	/* read preview HTS */
	ret = ov5640_get_hts(sensor);
	if (ret < 0)
		return ret;
	if (ret == 0)
		return -EINVAL;
	sensor->prev_hts = ret;

	/* read preview VTS */
	ret = ov5640_get_vts(sensor);
	if (ret < 0)
		return ret;
	prev_vts = ret;

	/* calculate banding filter */
	/* 60Hz */
	band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
	ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
	if (ret)
		return ret;
	if (!band_step60)
		return -EINVAL;
	max_band60 = (int)((prev_vts - 4) / band_step60);
	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
	if (ret)
		return ret;

	/* 50Hz */
	band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
	ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
	if (ret)
		return ret;
	if (!band_step50)
		return -EINVAL;
	max_band50 = (int)((prev_vts - 4) / band_step50);
	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
}

static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
{
	/* stable in high */
	u32 fast_high, fast_low;
	int ret;

	sensor->ae_low = target * 23 / 25;	/* 0.92 */
	sensor->ae_high = target * 27 / 25;	/* 1.08 */

	fast_high = sensor->ae_high << 1;
	if (fast_high > 255)
		fast_high = 255;

	fast_low = sensor->ae_low >> 1;

	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
	if (ret)
		return ret;
	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
	if (ret)
		return ret;
	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
	if (ret)
		return ret;
	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
	if (ret)
		return ret;
	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
	if (ret)
		return ret;
	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
}

static int ov5640_get_binning(struct ov5640_dev *sensor)
{
	u8 temp;
	int ret;

	ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
	if (ret)
		return ret;

	return temp & BIT(0);
}

static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
{
	int ret;

	/*
	 * TIMING TC REG21:
	 * - [0]:	Horizontal binning enable
	 */
	ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
			     BIT(0), enable ? BIT(0) : 0);
	if (ret)
		return ret;
	/*
	 * TIMING TC REG20:
	 * - [0]:	Undocumented, but hardcoded init sequences
	 *		are always setting REG21/REG20 bit 0 to same value...
	 */
	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
			      BIT(0), enable ? BIT(0) : 0);
}

static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
{
	struct i2c_client *client = sensor->i2c_client;
	u8 temp, channel = virtual_channel;
	int ret;

	if (channel > 3) {
		dev_err(&client->dev,
			"%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
			__func__, channel);
		return -EINVAL;
	}

	ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
	if (ret)
		return ret;
	temp &= ~(3 << 6);
	temp |= (channel << 6);
	return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
}

static const struct ov5640_mode_info *
ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
		 int width, int height, bool nearest)
{
	const struct ov5640_mode_info *mode;

	mode = v4l2_find_nearest_size(ov5640_mode_data,
				      ARRAY_SIZE(ov5640_mode_data),
				      hact, vact,
				      width, height);

	if (!mode ||
	    (!nearest && (mode->hact != width || mode->vact != height)))
		return NULL;

	/* Only 640x480 can operate at 60fps (for now) */
	if (fr == OV5640_60_FPS &&
	    !(mode->hact == 640 && mode->vact == 480))
		return NULL;

	if (fr == OV5640_30_FPS &&
	    !(mode->hact == 1920 && mode->vact == 1080))
		return NULL;
	/* 2592x1944 only works at 15fps */
	if (fr != OV5640_15_FPS &&
	    (mode->hact == 2592 && mode->vact == 1944))
		return NULL;

	return mode;
}

/*
 * sensor changes between scaling and subsampling, go through
 * exposure calculation
 */
static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
					 const struct ov5640_mode_info *mode)
{
	u32 prev_shutter, prev_gain16;
	u32 cap_shutter, cap_gain16;
	u32 cap_sysclk, cap_hts, cap_vts;
	u32 light_freq, cap_bandfilt, cap_maxband;
	u32 cap_gain16_shutter;
	u8 average;
	int ret;

	if (!mode->reg_data)
		return -EINVAL;

	/* read preview shutter */
	ret = ov5640_get_exposure(sensor);
	if (ret < 0)
		return ret;
	prev_shutter = ret;
	ret = ov5640_get_binning(sensor);
	if (ret < 0)
		return ret;
	if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
	    mode->id != OV5640_MODE_1080P_1920_1080)
		prev_shutter *= 2;

	/* read preview gain */
	ret = ov5640_get_gain(sensor);
	if (ret < 0)
		return ret;
	prev_gain16 = ret;

	/* get average */
	ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
	if (ret)
		return ret;

	/* turn off night mode for capture */
	ret = ov5640_set_night_mode(sensor);
	if (ret < 0)
		return ret;

	/* Write capture setting */
	ret = ov5640_load_regs(sensor, mode);
	if (ret < 0)
		return ret;

	/* read capture VTS */
	ret = ov5640_get_vts(sensor);
	if (ret < 0)
		return ret;
	cap_vts = ret;
	ret = ov5640_get_hts(sensor);
	if (ret < 0)
		return ret;
	if (ret == 0)
		return -EINVAL;
	cap_hts = ret;

	ret = ov5640_get_sysclk(sensor);
	if (ret < 0)
		return ret;
	if (ret == 0)
		return -EINVAL;
	cap_sysclk = ret;

	/* calculate capture banding filter */
	ret = ov5640_get_light_freq(sensor);
	if (ret < 0)
		return ret;
	light_freq = ret;

	if (light_freq == 60) {
		/* 60Hz */
		cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
	} else {
		/* 50Hz */
		cap_bandfilt = cap_sysclk * 100 / cap_hts;
	}

	if (!sensor->prev_sysclk) {
		ret = ov5640_get_sysclk(sensor);
		if (ret < 0)
			return ret;
		if (ret == 0)
			return -EINVAL;
		sensor->prev_sysclk = ret;
	}

	if (!cap_bandfilt)
		return -EINVAL;

	cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);

	/* calculate capture shutter/gain16 */
	if (average > sensor->ae_low && average < sensor->ae_high) {
		/* in stable range */
		cap_gain16_shutter =
			prev_gain16 * prev_shutter *
			cap_sysclk / sensor->prev_sysclk *
			sensor->prev_hts / cap_hts *
			sensor->ae_target / average;
	} else {
		cap_gain16_shutter =
			prev_gain16 * prev_shutter *
			cap_sysclk / sensor->prev_sysclk *
			sensor->prev_hts / cap_hts;
	}

	/* gain to shutter */
	if (cap_gain16_shutter < (cap_bandfilt * 16)) {
		/* shutter < 1/100 */
		cap_shutter = cap_gain16_shutter / 16;
		if (cap_shutter < 1)
			cap_shutter = 1;

		cap_gain16 = cap_gain16_shutter / cap_shutter;
		if (cap_gain16 < 16)
			cap_gain16 = 16;
	} else {
		if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
			/* exposure reach max */
			cap_shutter = cap_bandfilt * cap_maxband;
			if (!cap_shutter)
				return -EINVAL;

			cap_gain16 = cap_gain16_shutter / cap_shutter;
		} else {
			/* 1/100 < (cap_shutter = n/100) =< max */
			cap_shutter =
				((int)(cap_gain16_shutter / 16 / cap_bandfilt))
				* cap_bandfilt;
			if (!cap_shutter)
				return -EINVAL;

			cap_gain16 = cap_gain16_shutter / cap_shutter;
		}
	}

	/* set capture gain */
	ret = ov5640_set_gain(sensor, cap_gain16);
	if (ret)
		return ret;

	/* write capture shutter */
	if (cap_shutter > (cap_vts - 4)) {
		cap_vts = cap_shutter + 4;
		ret = ov5640_set_vts(sensor, cap_vts);
		if (ret < 0)
			return ret;
	}

	/* set exposure */
	return ov5640_set_exposure(sensor, cap_shutter);
}

/*
 * if sensor changes inside scaling or subsampling
 * change mode directly
 */
static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
				  const struct ov5640_mode_info *mode)
{
	if (!mode->reg_data)
		return -EINVAL;

	/* Write capture setting */
	return ov5640_load_regs(sensor, mode);
}

static int ov5640_set_mode(struct ov5640_dev *sensor)
{
	const struct ov5640_mode_info *mode = sensor->current_mode;
	const struct ov5640_mode_info *orig_mode = sensor->last_mode;
	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
	unsigned long rate;
	int ret;

	dn_mode = mode->dn_mode;
	orig_dn_mode = orig_mode->dn_mode;

	/* auto gain and exposure must be turned off when changing modes */
	if (auto_gain) {
		ret = ov5640_set_autogain(sensor, false);
		if (ret)
			return ret;
	}

	if (auto_exp) {
		ret = ov5640_set_autoexposure(sensor, false);
		if (ret)
			goto restore_auto_gain;
	}

	/*
	 * All the formats we support have 16 bits per pixel, seems to require
	 * the same rate than YUV, so we can just use 16 bpp all the time.
	 */
	rate = mode->vtot * mode->htot * 16;
	rate *= ov5640_framerates[sensor->current_fr];
	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
		ret = ov5640_set_mipi_pclk(sensor, rate);
	} else {
		rate = rate / sensor->ep.bus.parallel.bus_width;
		ret = ov5640_set_dvp_pclk(sensor, rate);
	}

	if (ret < 0)
		return 0;

	if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
	    (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
		/*
		 * change between subsampling and scaling
		 * go through exposure calculation
		 */
		ret = ov5640_set_mode_exposure_calc(sensor, mode);
	} else {
		/*
		 * change inside subsampling or scaling
		 * download firmware directly
		 */
		ret = ov5640_set_mode_direct(sensor, mode);
	}
	if (ret < 0)
		goto restore_auto_exp_gain;

	/* restore auto gain and exposure */
	if (auto_gain)
		ov5640_set_autogain(sensor, true);
	if (auto_exp)
		ov5640_set_autoexposure(sensor, true);

	ret = ov5640_set_binning(sensor, dn_mode != SCALING);
	if (ret < 0)
		return ret;
	ret = ov5640_set_ae_target(sensor, sensor->ae_target);
	if (ret < 0)
		return ret;
	ret = ov5640_get_light_freq(sensor);
	if (ret < 0)
		return ret;
	ret = ov5640_set_bandingfilter(sensor);
	if (ret < 0)
		return ret;
	ret = ov5640_set_virtual_channel(sensor);
	if (ret < 0)
		return ret;

	sensor->pending_mode_change = false;
	sensor->last_mode = mode;

	return 0;

restore_auto_exp_gain:
	if (auto_exp)
		ov5640_set_autoexposure(sensor, true);
restore_auto_gain:
	if (auto_gain)
		ov5640_set_autogain(sensor, true);

	return ret;
}

static int ov5640_set_framefmt(struct ov5640_dev *sensor,
			       struct v4l2_mbus_framefmt *format);

/* restore the last set video mode after chip power-on */
static int ov5640_restore_mode(struct ov5640_dev *sensor)
{
	int ret;

	/* first load the initial register values */
	ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
	if (ret < 0)
		return ret;
	sensor->last_mode = &ov5640_mode_init_data;

	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
			     (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
			     ilog2(OV5640_SCLK_ROOT_DIV));
	if (ret)
		return ret;

	/* now restore the last capture mode */
	ret = ov5640_set_mode(sensor);
	if (ret < 0)
		return ret;

	return ov5640_set_framefmt(sensor, &sensor->fmt);
}

static void ov5640_power(struct ov5640_dev *sensor, bool enable)
{
	gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
}

static void ov5640_reset(struct ov5640_dev *sensor)
{
	if (!sensor->reset_gpio)
		return;

	gpiod_set_value_cansleep(sensor->reset_gpio, 0);

	/* camera power cycle */
	ov5640_power(sensor, false);
	usleep_range(5000, 10000);
	ov5640_power(sensor, true);
	usleep_range(5000, 10000);

	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
	usleep_range(1000, 2000);

	gpiod_set_value_cansleep(sensor->reset_gpio, 0);
	usleep_range(20000, 25000);
}

static int ov5640_set_power_on(struct ov5640_dev *sensor)
{
	struct i2c_client *client = sensor->i2c_client;
	int ret;

	ret = clk_prepare_enable(sensor->xclk);
	if (ret) {
		dev_err(&client->dev, "%s: failed to enable clock\n",
			__func__);
		return ret;
	}

	ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
				    sensor->supplies);
	if (ret) {
		dev_err(&client->dev, "%s: failed to enable regulators\n",
			__func__);
		goto xclk_off;
	}

	ov5640_reset(sensor);
	ov5640_power(sensor, true);

	ret = ov5640_init_slave_id(sensor);
	if (ret)
		goto power_off;

	return 0;

power_off:
	ov5640_power(sensor, false);
	regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
xclk_off:
	clk_disable_unprepare(sensor->xclk);
	return ret;
}

static void ov5640_set_power_off(struct ov5640_dev *sensor)
{
	ov5640_power(sensor, false);
	regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
	clk_disable_unprepare(sensor->xclk);
}

static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
{
	int ret = 0;

	if (on) {
		ret = ov5640_set_power_on(sensor);
		if (ret)
			return ret;

		ret = ov5640_restore_mode(sensor);
		if (ret)
			goto power_off;

		/* We're done here for DVP bus, while CSI-2 needs setup. */
		if (sensor->ep.bus_type != V4L2_MBUS_CSI2)
			return 0;

		/*
		 * Power up MIPI HS Tx and LS Rx; 2 data lanes mode
		 *
		 * 0x300e = 0x40
		 * [7:5] = 010	: 2 data lanes mode (see FIXME note in
		 *		  "ov5640_set_stream_mipi()")
		 * [4] = 0	: Power up MIPI HS Tx
		 * [3] = 0	: Power up MIPI LS Rx
		 * [2] = 0	: MIPI interface disabled
		 */
		ret = ov5640_write_reg(sensor,
				       OV5640_REG_IO_MIPI_CTRL00, 0x40);
		if (ret)
			goto power_off;

		/*
		 * Gate clock and set LP11 in 'no packets mode' (idle)
		 *
		 * 0x4800 = 0x24
		 * [5] = 1	: Gate clock when 'no packets'
		 * [2] = 1	: MIPI bus in LP11 when 'no packets'
		 */
		ret = ov5640_write_reg(sensor,
				       OV5640_REG_MIPI_CTRL00, 0x24);
		if (ret)
			goto power_off;

		/*
		 * Set data lanes and clock in LP11 when 'sleeping'
		 *
		 * 0x3019 = 0x70
		 * [6] = 1	: MIPI data lane 2 in LP11 when 'sleeping'
		 * [5] = 1	: MIPI data lane 1 in LP11 when 'sleeping'
		 * [4] = 1	: MIPI clock lane in LP11 when 'sleeping'
		 */
		ret = ov5640_write_reg(sensor,
				       OV5640_REG_PAD_OUTPUT00, 0x70);
		if (ret)
			goto power_off;

		/* Give lanes some time to coax into LP11 state. */
		usleep_range(500, 1000);

	} else {
		if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
			/* Reset MIPI bus settings to their default values. */
			ov5640_write_reg(sensor,
					 OV5640_REG_IO_MIPI_CTRL00, 0x58);
			ov5640_write_reg(sensor,
					 OV5640_REG_MIPI_CTRL00, 0x04);
			ov5640_write_reg(sensor,
					 OV5640_REG_PAD_OUTPUT00, 0x00);
		}

		ov5640_set_power_off(sensor);
	}

	return 0;

power_off:
	ov5640_set_power_off(sensor);
	return ret;
}

/* --------------- Subdev Operations --------------- */

static int ov5640_s_power(struct v4l2_subdev *sd, int on)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	int ret = 0;

	mutex_lock(&sensor->lock);

	/*
	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
	 * update the power state.
	 */
	if (sensor->power_count == !on) {
		ret = ov5640_set_power(sensor, !!on);
		if (ret)
			goto out;
	}

	/* Update the power count. */
	sensor->power_count += on ? 1 : -1;
	WARN_ON(sensor->power_count < 0);
out:
	mutex_unlock(&sensor->lock);

	if (on && !ret && sensor->power_count == 1) {
		/* restore controls */
		ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
	}

	return ret;
}

static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
				     struct v4l2_fract *fi,
				     u32 width, u32 height)
{
	const struct ov5640_mode_info *mode;
	enum ov5640_frame_rate rate = OV5640_30_FPS;
	int minfps, maxfps, best_fps, fps;
	int i;

	minfps = ov5640_framerates[OV5640_15_FPS];
	maxfps = ov5640_framerates[OV5640_60_FPS];

	if (fi->numerator == 0) {
		fi->denominator = maxfps;
		fi->numerator = 1;
		rate = OV5640_60_FPS;
		goto find_mode;
	}

	fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
			minfps, maxfps);

	best_fps = minfps;
	for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
		int curr_fps = ov5640_framerates[i];

		if (abs(curr_fps - fps) < abs(best_fps - fps)) {
			best_fps = curr_fps;
			rate = i;
		}
	}

	fi->numerator = 1;
	fi->denominator = best_fps;

find_mode:
	mode = ov5640_find_mode(sensor, rate, width, height, false);
	return mode ? rate : -EINVAL;
}

static int ov5640_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *format)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	struct v4l2_mbus_framefmt *fmt;

	if (format->pad != 0)
		return -EINVAL;

	mutex_lock(&sensor->lock);

	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
		fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg,
						 format->pad);
	else
		fmt = &sensor->fmt;

	format->format = *fmt;

	mutex_unlock(&sensor->lock);

	return 0;
}

static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
				   struct v4l2_mbus_framefmt *fmt,
				   enum ov5640_frame_rate fr,
				   const struct ov5640_mode_info **new_mode)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	const struct ov5640_mode_info *mode;
	int i;

	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
	if (!mode)
		return -EINVAL;
	fmt->width = mode->hact;
	fmt->height = mode->vact;

	if (new_mode)
		*new_mode = mode;

	for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
		if (ov5640_formats[i].code == fmt->code)
			break;
	if (i >= ARRAY_SIZE(ov5640_formats))
		i = 0;

	fmt->code = ov5640_formats[i].code;
	fmt->colorspace = ov5640_formats[i].colorspace;
	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);

	return 0;
}

static int ov5640_set_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *format)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	const struct ov5640_mode_info *new_mode;
	struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
	struct v4l2_mbus_framefmt *fmt;
	u64 rate;
	int ret;

	if (format->pad != 0)
		return -EINVAL;

	mutex_lock(&sensor->lock);

	if (sensor->streaming) {
		ret = -EBUSY;
		goto out;
	}

	ret = ov5640_try_fmt_internal(sd, mbus_fmt,
				      sensor->current_fr, &new_mode);
	if (ret)
		goto out;

	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
		fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
	else
		fmt = &sensor->fmt;

	*fmt = *mbus_fmt;

	if (new_mode != sensor->current_mode) {
		sensor->current_mode = new_mode;
		sensor->pending_mode_change = true;
	}
	if (mbus_fmt->code != sensor->fmt.code)
		sensor->pending_fmt_change = true;

	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
	rate *= ov5640_framerates[sensor->current_fr];
	sensor->pixel_rate = rate;

	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,sensor->pixel_rate);
out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static int ov5640_set_framefmt(struct ov5640_dev *sensor,
			       struct v4l2_mbus_framefmt *format)
{
	int ret = 0;
	bool is_rgb = false;
	bool is_jpeg = false;
	u8 val;

	switch (format->code) {
	case MEDIA_BUS_FMT_UYVY8_2X8:
		/* YUV422, UYVY */
		val = 0x3f;
		break;
	case MEDIA_BUS_FMT_YUYV8_2X8:
		/* YUV422, YUYV */
		val = 0x30;
		break;
	case MEDIA_BUS_FMT_RGB565_2X8_LE:
		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
		val = 0x6F;
		is_rgb = true;
		break;
	case MEDIA_BUS_FMT_RGB565_2X8_BE:
		/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
		val = 0x61;
		is_rgb = true;
		break;
	case MEDIA_BUS_FMT_JPEG_1X8:
		/* YUV422, YUYV */
		val = 0x30;
		is_jpeg = true;
		break;
	default:
		return -EINVAL;
	}

	/* FORMAT CONTROL00: YUV and RGB formatting */
	ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val);
	if (ret)
		return ret;

	/* FORMAT MUX CONTROL: ISP YUV or RGB */
	ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
			       is_rgb ? 0x01 : 0x00);
	if (ret)
		return ret;

	/*
	 * TIMING TC REG21:
	 * - [5]:	JPEG enable
	 */
	ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
			     BIT(5), is_jpeg ? BIT(5) : 0);
	if (ret)
		return ret;

	/*
	 * SYSTEM RESET02:
	 * - [4]:	Reset JFIFO
	 * - [3]:	Reset SFIFO
	 * - [2]:	Reset JPEG
	 */
	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
			     BIT(4) | BIT(3) | BIT(2),
			     is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
	if (ret)
		return ret;

	/*
	 * CLOCK ENABLE02:
	 * - [5]:	Enable JPEG 2x clock
	 * - [3]:	Enable JPEG clock
	 */
	return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
			      BIT(5) | BIT(3),
			      is_jpeg ? (BIT(5) | BIT(3)) : 0);
}

/*
 * Sensor Controls.
 */

static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
{
	int ret;

	if (value) {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
				     BIT(0), BIT(0));
		if (ret)
			return ret;
		ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
	} else {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
	}

	return ret;
}

static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
{
	int ret;

	if (value) {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
				     BIT(2), BIT(2));
		if (ret)
			return ret;
		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
				       value & 0xff);
	} else {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
	}

	return ret;
}

static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
{
	int ret;

	if (value) {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
				     BIT(1), BIT(1));
		if (ret)
			return ret;
		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
				       value & 0xff);
		if (ret)
			return ret;
		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
				       value & 0xff);
	} else {
		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
	}

	return ret;
}

static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
{
	int ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
			     BIT(0), awb ? 0 : 1);
	if (ret)
		return ret;

	if (!awb) {
		u16 red = (u16)sensor->ctrls.red_balance->val;
		u16 blue = (u16)sensor->ctrls.blue_balance->val;

		ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
		if (ret)
			return ret;
		ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
	}

	return ret;
}

static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor,
				    enum v4l2_exposure_auto_type auto_exposure)
{
	struct ov5640_ctrls *ctrls = &sensor->ctrls;
	bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
	int ret = 0;

	if (ctrls->auto_exp->is_new) {
		ret = ov5640_set_autoexposure(sensor, auto_exp);
		if (ret)
			return ret;
	}

	if (!auto_exp && ctrls->exposure->is_new) {
		u16 max_exp;

		ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
					&max_exp);
		if (ret)
			return ret;
		ret = ov5640_get_vts(sensor);
		if (ret < 0)
			return ret;
		max_exp += ret;
		ret = 0;

		if (ctrls->exposure->val < max_exp)
			ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
	}

	return ret;
}

static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
{
	struct ov5640_ctrls *ctrls = &sensor->ctrls;
	int ret = 0;

	if (ctrls->auto_gain->is_new) {
		ret = ov5640_set_autogain(sensor, auto_gain);
		if (ret)
			return ret;
	}

	if (!auto_gain && ctrls->gain->is_new)
		ret = ov5640_set_gain(sensor, ctrls->gain->val);

	return ret;
}

static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
{
	return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
			      0xa4, value ? 0xa4 : 0);
}

static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
{
	int ret;

	ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
			     (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
			     0 : BIT(7));
	if (ret)
		return ret;

	return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
			      (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
			      BIT(2) : 0);
}

static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
{
	/*
	 * If sensor is mounted upside down, mirror logic is inversed.
	 *
	 * Sensor is a BSI (Back Side Illuminated) one,
	 * so image captured is physically mirrored.
	 * This is why mirror logic is inversed in
	 * order to cancel this mirror effect.
	 */

	/*
	 * TIMING TC REG21:
	 * - [2]:	ISP mirror
	 * - [1]:	Sensor mirror
	 */
	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
			      BIT(2) | BIT(1),
			      (!(value ^ sensor->upside_down)) ?
			      (BIT(2) | BIT(1)) : 0);
}

static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
{
	/* If sensor is mounted upside down, flip logic is inversed */

	/*
	 * TIMING TC REG20:
	 * - [2]:	ISP vflip
	 * - [1]:	Sensor vflip
	 */
	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
			      BIT(2) | BIT(1),
			      (value ^ sensor->upside_down) ?
			      (BIT(2) | BIT(1)) : 0);
}

static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	int val;

	/* v4l2_ctrl_lock() locks our own mutex */

	switch (ctrl->id) {
	case V4L2_CID_AUTOGAIN:
		val = ov5640_get_gain(sensor);
		if (val < 0)
			return val;
		sensor->ctrls.gain->val = val;
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		val = ov5640_get_exposure(sensor);
		if (val < 0)
			return val;
		sensor->ctrls.exposure->val = val;
		break;
	}

	return 0;
}

static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	int ret;

	/* v4l2_ctrl_lock() locks our own mutex */

	/*
	 * If the device is not powered up by the host driver do
	 * not apply any controls to H/W at this time. Instead
	 * the controls will be restored right after power-up.
	 */
	if (sensor->power_count == 0)
		return 0;

	switch (ctrl->id) {
	case V4L2_CID_AUTOGAIN:
		ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
		break;
	case V4L2_CID_AUTO_WHITE_BALANCE:
		ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
		break;
	case V4L2_CID_HUE:
		ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
		break;
	case V4L2_CID_CONTRAST:
		ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
		break;
	case V4L2_CID_SATURATION:
		ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
		break;
	case V4L2_CID_TEST_PATTERN:
		ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
		break;
	case V4L2_CID_POWER_LINE_FREQUENCY:
		ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
		break;
	case V4L2_CID_HFLIP:
		ret = ov5640_set_ctrl_hflip(sensor, ctrl->val);
		break;
	case V4L2_CID_VFLIP:
		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
	.g_volatile_ctrl = ov5640_g_volatile_ctrl,
	.s_ctrl = ov5640_s_ctrl,
};

static const char * const test_pattern_menu[] = {
	"Disabled",
	"Color bars",
};

static int ov5640_init_controls(struct ov5640_dev *sensor)
{
	const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
	struct ov5640_ctrls *ctrls = &sensor->ctrls;
	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
	int ret;

	v4l2_ctrl_handler_init(hdl, 32);

	/* we can use our own mutex for the ctrl lock */
	hdl->lock = &sensor->lock;

	/* Clock related controls */
	ctrls->pixel_rate =
		v4l2_ctrl_new_std(hdl, ops,
				  V4L2_CID_PIXEL_RATE, 0, INT_MAX, 1,
				  55969920);
	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;

	/* Auto/manual white balance */
	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
					   V4L2_CID_AUTO_WHITE_BALANCE,
					   0, 1, 1, 1);
	ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
						0, 4095, 1, 0);
	ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
					       0, 4095, 1, 0);
	/* Auto/manual exposure */
	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
						 V4L2_CID_EXPOSURE_AUTO,
						 V4L2_EXPOSURE_MANUAL, 0,
						 V4L2_EXPOSURE_AUTO);
	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
					    0, 65535, 1, 0);
	/* Auto/manual gain */
	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
					     0, 1, 1, 1);
	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
					0, 1023, 1, 0);

	ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
					      0, 255, 1, 64);
	ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
				       0, 359, 1, 0);
	ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
					    0, 255, 1, 0);
	ctrls->test_pattern =
		v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
					     ARRAY_SIZE(test_pattern_menu) - 1,
					     0, 0, test_pattern_menu);
	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
					 0, 1, 1, 0);
	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
					 0, 1, 1, 0);

	ctrls->light_freq =
		v4l2_ctrl_new_std_menu(hdl, ops,
				       V4L2_CID_POWER_LINE_FREQUENCY,
				       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
				       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);

	if (hdl->error) {
		ret = hdl->error;
		goto free_ctrls;
	}

	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;

	v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);

	sensor->sd.ctrl_handler = hdl;
	return 0;

free_ctrls:
	v4l2_ctrl_handler_free(hdl);
	return ret;
}

static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
				  struct v4l2_subdev_pad_config *cfg,
				  struct v4l2_subdev_frame_size_enum *fse)
{
	if (fse->pad != 0)
		return -EINVAL;
	if (fse->index >= OV5640_NUM_MODES)
		return -EINVAL;

	fse->min_width =
		ov5640_mode_data[fse->index].hact;
	fse->max_width = fse->min_width;
	fse->min_height =
		ov5640_mode_data[fse->index].vact;
	fse->max_height = fse->min_height;

	return 0;
}

static int ov5640_enum_frame_interval(
	struct v4l2_subdev *sd,
	struct v4l2_subdev_pad_config *cfg,
	struct v4l2_subdev_frame_interval_enum *fie)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	struct v4l2_fract tpf;
	int ret;

	if (fie->pad != 0)
		return -EINVAL;
	if (fie->index >= OV5640_NUM_FRAMERATES)
		return -EINVAL;

	tpf.numerator = 1;
	tpf.denominator = ov5640_framerates[fie->index];

	ret = ov5640_try_frame_interval(sensor, &tpf,
					fie->width, fie->height);
	if (ret < 0)
		return -EINVAL;

	fie->interval = tpf;
	return 0;
}

static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);

	mutex_lock(&sensor->lock);
	fi->interval = sensor->frame_interval;
	mutex_unlock(&sensor->lock);

	return 0;
}

static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	const struct ov5640_mode_info *mode;
	int frame_rate, ret = 0;
	u64 rate;

	if (fi->pad != 0)
		return -EINVAL;

	mutex_lock(&sensor->lock);

	if (sensor->streaming) {
		ret = -EBUSY;
		goto out;
	}

	mode = sensor->current_mode;

	frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
					       mode->hact, mode->vact);
	if (frame_rate < 0) {
		/* Always return a valid frame interval value */
		fi->interval = sensor->frame_interval;
		goto out;
	}

	mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
				mode->vact, true);
	if (!mode) {
		ret = -EINVAL;
		goto out;
	}

	if (mode != sensor->current_mode ||
	    frame_rate != sensor->current_fr) {
		sensor->current_fr = frame_rate;
		sensor->frame_interval = fi->interval;
		sensor->current_mode = mode;
		sensor->pending_mode_change = true;

		rate = sensor->current_mode->vtot * sensor->current_mode->htot;
		rate *= ov5640_framerates[sensor->current_fr];
		sensor->pixel_rate = rate;
		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
					 sensor->pixel_rate);
	}
out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
				 struct v4l2_subdev_pad_config *cfg,
				 struct v4l2_subdev_mbus_code_enum *code)
{
	if (code->pad != 0)
		return -EINVAL;
	if (code->index >= ARRAY_SIZE(ov5640_formats))
		return -EINVAL;

	code->code = ov5640_formats[code->index].code;
	return 0;
}

static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	int ret = 0;

	mutex_lock(&sensor->lock);

	if (sensor->streaming == !enable) {
		if (enable && sensor->pending_mode_change) {
			ret = ov5640_set_mode(sensor);
			if (ret)
				goto out;
		}

		if (enable && sensor->pending_fmt_change) {
			ret = ov5640_set_framefmt(sensor, &sensor->fmt);
			if (ret)
				goto out;
			sensor->pending_fmt_change = false;
		}

		if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
			ret = ov5640_set_stream_mipi(sensor, enable);
		else
			ret = ov5640_set_stream_dvp(sensor, enable);

		if (!ret)
			sensor->streaming = enable;
	}
out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static const struct v4l2_subdev_core_ops ov5640_core_ops = {
	.s_power = ov5640_s_power,
	.log_status = v4l2_ctrl_subdev_log_status,
	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};

static const struct v4l2_subdev_video_ops ov5640_video_ops = {
	.g_frame_interval = ov5640_g_frame_interval,
	.s_frame_interval = ov5640_s_frame_interval,
	.s_stream = ov5640_s_stream,
};

static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
	.enum_mbus_code = ov5640_enum_mbus_code,
	.get_fmt = ov5640_get_fmt,
	.set_fmt = ov5640_set_fmt,
	.enum_frame_size = ov5640_enum_frame_size,
	.enum_frame_interval = ov5640_enum_frame_interval,
};

static const struct v4l2_subdev_ops ov5640_subdev_ops = {
	.core = &ov5640_core_ops,
	.video = &ov5640_video_ops,
	.pad = &ov5640_pad_ops,
};

static int ov5640_get_regulators(struct ov5640_dev *sensor)
{
	int i;

	for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
		sensor->supplies[i].supply = ov5640_supply_name[i];

	return devm_regulator_bulk_get(&sensor->i2c_client->dev,
				       OV5640_NUM_SUPPLIES,
				       sensor->supplies);
}

static int ov5640_check_chip_id(struct ov5640_dev *sensor)
{
	struct i2c_client *client = sensor->i2c_client;
	int ret = 0;
	u16 chip_id;

	ov5640_init_slave_id(sensor);
	/*ret = ov5640_set_power_on(sensor);		//cispl
	if (ret)
		return ret;*/
	ret = ov5640_cispl_read_reg(sensor, OV5640_REG_SYS_CARAVEL, &chip_id);
	//ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
	if (ret) {
		dev_err(&client->dev, "%s: failed to read chip identifier\n",
			__func__);
//		goto power_off;
	}

	if (chip_id != 0x5640) {
		dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
			__func__, chip_id);
		ret = -ENXIO;
	}


//power_off:
//	ov5640_set_power_off(sensor);
//	return ret;
}

static int ov5640_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct fwnode_handle *endpoint;
	struct ov5640_dev *sensor;
	struct v4l2_mbus_framefmt *fmt;
	u32 rotation;
	int ret;

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

	sensor->i2c_client = client;

	/*
	 * default init sequence initialize sensor to
	 * YUV422 UYVY VGA@30fps
	 */
	fmt = &sensor->fmt;
	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
	fmt->colorspace = V4L2_COLORSPACE_JPEG;
	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
	fmt->width = 1920;
	fmt->height = 1080;
	fmt->field = V4L2_FIELD_NONE;
	sensor->frame_interval.numerator = 1;
	sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
	sensor->current_fr = OV5640_30_FPS;
	sensor->current_mode =
		&ov5640_mode_data[OV5640_MODE_1080P_1920_1080];
	sensor->last_mode = sensor->current_mode;

	sensor->ae_target = 52;

	/* optional indication of physical rotation of sensor */
	ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
				       &rotation);
	if (!ret) {
		switch (rotation) {
		case 180:
			sensor->upside_down = true;
			/* fall through */
		case 0:
			break;
		default:
			dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
				 rotation);
		}
	}

	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
						  NULL);
	if (!endpoint) {
		dev_err(dev, "endpoint node not found\n");
		return -EINVAL;
	}

	ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
	fwnode_handle_put(endpoint);
	if (ret) {
		dev_err(dev, "Could not parse endpoint\n");
		return ret;
	}

	/* get system clock (xclk) */
	/*sensor->xclk = devm_clk_get(dev, "xclk");
	if (IS_ERR(sensor->xclk)) {
		dev_err(dev, "failed to get xclk\n");
		return PTR_ERR(sensor->xclk);
	}

	sensor->xclk_freq = clk_get_rate(sensor->xclk);
	if (sensor->xclk_freq < OV5640_XCLK_MIN ||
	    sensor->xclk_freq > OV5640_XCLK_MAX) {
		dev_err(dev, "xclk frequency out of range: %d Hz\n",
			sensor->xclk_freq);
		return -EINVAL;
	}*/
	sensor->xclk_freq = OV5640_XCLK_MAX;
	/* request optional power down pin */
	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
						    GPIOD_OUT_HIGH);
	/* request optional reset pin */
	sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
						     GPIOD_OUT_HIGH);

	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);

	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
			    V4L2_SUBDEV_FL_HAS_EVENTS;
	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
	if (ret)
		return ret;

	ret = ov5640_get_regulators(sensor);
	if (ret)
		return ret;

	mutex_init(&sensor->lock);




	ret = ov5640_check_chip_id(sensor);
//	if (ret)
//		goto entity_cleanup;

//	ret = ov5640_init_controls(sensor);
//	if (ret)
//		goto entity_cleanup;

	ret = v4l2_async_register_subdev(&sensor->sd);
	if (ret)
		goto free_ctrls;

	return 0;

free_ctrls:
	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
entity_cleanup:
	mutex_destroy(&sensor->lock);
	media_entity_cleanup(&sensor->sd.entity);
	return ret;
}

static int ov5640_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct ov5640_dev *sensor = to_ov5640_dev(sd);

	v4l2_async_unregister_subdev(&sensor->sd);
	mutex_destroy(&sensor->lock);
	media_entity_cleanup(&sensor->sd.entity);
	v4l2_ctrl_handler_free(&sensor->ctrls.handler);

	return 0;
}

static const struct i2c_device_id ov5640_id[] = {
	{"ov5640", 0},
	{},
};
MODULE_DEVICE_TABLE(i2c, ov5640_id);

static const struct of_device_id ov5640_dt_ids[] = {
	{ .compatible = "ovti,ov5640" },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ov5640_dt_ids);

static struct i2c_driver ov5640_i2c_driver = {
	.driver = {
		.name  = "ov5640",
		.of_match_table	= ov5640_dt_ids,
	},
	.id_table = ov5640_id,
	.probe    = ov5640_probe,
	.remove   = ov5640_remove,
};

module_i2c_driver(ov5640_i2c_driver);

MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
MODULE_LICENSE("GPL");

Output of :


U-Boot 2019.01 (Dec 23 2021 - 18:29:37 +0530)

CPU  : DRA752-GP ES2.0
Model: TI AM5728 BeagleBoard-X15 rev C
Board: BeagleBoard X15 REV C.00
DRAM:  2 GiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... OK
Net:
Warning: ethernet@48484000 using MAC address from ROM
eth0: ethernet@48484000
Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc0 is current device
SD/MMC found on device 0
** Unable to read file boot.scr **
1490 bytes read in 1 ms (1.4 MiB/s)
Loaded env from uEnv.txt
Importing environment from mmc0 ...
Running uenvcmd ...
1 bytes read in 1 ms (1000 Bytes/s)
Already setup.
switch to partitions #0, OK
mmc0 is current device
SD/MMC found on device 0
4403712 bytes read in 196 ms (21.4 MiB/s)
131858 bytes read in 8 ms (15.7 MiB/s)
## Flattened Device Tree blob at 88000000
   Booting using the fdt blob at 0x88000000
   Loading Device Tree to 8ffdc000, end 8ffff311 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.19.59-g5f8c1c6121 (root@ubuntu-OptiPlex-3040) (gcc version 8.3.0 (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36))) #11 SMP PREEMPT Fri Dec 31 14:58:49 IST 2021
[    0.000000] CPU: ARMv7 Processor [412fc0f2] revision 2 (ARMv7), cr=30c5387d
[    0.000000] CPU: div instructions available: patching division code
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, PIPT instruction cache
[    0.000000] OF: fdt: Machine model: TI AM5728 BeagleBoard-X15 rev C
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] efi: Getting EFI parameters from FDT:
[    0.000000] efi: UEFI not found.
[    0.000000] Reserved memory: created CMA memory pool at 0x0000000095800000, size 56 MiB
[    0.000000] OF: reserved mem: initialized node ipu2-memory@95800000, compatible id shared-dma-pool
[    0.000000] Reserved memory: created CMA memory pool at 0x0000000099000000, size 64 MiB
[    0.000000] OF: reserved mem: initialized node dsp1-memory@99000000, compatible id shared-dma-pool
[    0.000000] Reserved memory: created CMA memory pool at 0x000000009d000000, size 32 MiB
[    0.000000] OF: reserved mem: initialized node ipu1-memory@9d000000, compatible id shared-dma-pool
[    0.000000] Reserved memory: created CMA memory pool at 0x000000009f000000, size 8 MiB
[    0.000000] OF: reserved mem: initialized node dsp2-memory@9f000000, compatible id shared-dma-pool
[    0.000000] cma: Reserved 24 MiB at 0x00000000fe400000
[    0.000000] OMAP4: Map 0x00000000ffd00000 to (ptrval) for dram barrier
[    0.000000] DRA752 ES2.0
[    0.000000] random: get_random_bytes called from start_kernel+0xb4/0x470 with crng_init=0
[    0.000000] percpu: Embedded 16 pages/cpu s36492 r8192 d20852 u65536
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 472640
[    0.000000] Kernel command line: console=ttyS2,115200n8 root=PARTUUID=0f3f03ba-02 rw rootfstype=ext4 rootwait
[    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Memory: 1673648K/1897472K available (10240K kernel code, 333K rwdata, 2772K rodata, 2048K init, 269K bss, 35408K reserved, 188416K cma-reserved, 1283072K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xf0800000 - 0xff800000   ( 240 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xf0000000   ( 768 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0x(ptrval) - 0x(ptrval)   (12256 kB)
[    0.000000]       .init : 0x(ptrval) - 0x(ptrval)   (2048 kB)
[    0.000000]       .data : 0x(ptrval) - 0x(ptrval)   ( 334 kB)
[    0.000000]        .bss : 0x(ptrval) - 0x(ptrval)   ( 270 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
[    0.000000] rcu: Preemptible hierarchical RCU implementation.
[    0.000000]  Tasks RCU enabled.
[    0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[    0.000000] GIC: Using split EOI/Deactivate mode
[    0.000000] OMAP clockevent source: timer1 at 32786 Hz
[    0.000000] arch_timer: cp15 timer(s) running at 6.14MHz (phys).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x16af5adb9, max_idle_ns: 440795202250 ns
[    0.000005] sched_clock: 56 bits at 6MHz, resolution 162ns, wraps every 4398046511023ns
[    0.000018] Switching to timer-based delay loop, resolution 162ns
[    0.000300] clocksource: 32k_counter: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 58327039986419 ns
[    0.000309] OMAP clocksource: 32k_counter at 32768 Hz
[    0.000817] Console: colour dummy device 80x30
[    0.000860] Calibrating delay loop (skipped), value calculated using timer frequency.. 12.29 BogoMIPS (lpj=61475)
[    0.000877] pid_max: default: 32768 minimum: 301
[    0.000998] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001014] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001567] CPU: Testing write buffer coherency: ok
[    0.001606] CPU0: Spectre v2: using ICIALLU workaround
[    0.001816] /cpus/cpu@0 missing clock-frequency property
[    0.001836] /cpus/cpu@1 missing clock-frequency property
[    0.001849] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
[    0.059905] Setting up static identity map for 0x80200000 - 0x80200060
[    0.079916] rcu: Hierarchical SRCU implementation.
[    0.100105] EFI services will not be available.
[    0.119979] smp: Bringing up secondary CPUs ...
[    0.200375] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001
[    0.200380] CPU1: Spectre v2: using ICIALLU workaround
[    0.200503] smp: Brought up 1 node, 2 CPUs
[    0.200515] SMP: Total of 2 processors activated (24.59 BogoMIPS).
[    0.200524] CPU: All CPU(s) started in HYP mode.
[    0.200532] CPU: Virtualization extensions available.
[    0.201085] devtmpfs: initialized
[    0.220091] VFP support v0.3: implementor 41 architecture 4 part 30 variant f rev 0
[    0.220377] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.220422] futex hash table entries: 512 (order: 3, 32768 bytes)
[    0.223526] pinctrl core: initialized pinctrl subsystem
[    0.224026] DMI not present or invalid.
[    0.224299] NET: Registered protocol family 16
[    0.225436] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.226376] omap_hwmod: l3_main_2 using broken dt data from ocp
[    0.328731] wkupaon_cm:clk:0068:0: failed to disable
[    0.428463] cpuidle: using governor ladder
[    0.428498] cpuidle: using governor menu
[    0.438700] OMAP GPIO hardware version 0.1
[    0.462841] No ATAGs?
[    0.462918] hw-breakpoint: found 5 (+1 reserved) breakpoint and 4 watchpoint registers.
[    0.462932] hw-breakpoint: maximum watchpoint size is 8 bytes.
[    0.463461] omap4_sram_init:Unable to allocate sram needed to handle errata I688
[    0.463474] omap4_sram_init:Unable to get sram pool needed to handle errata I688
[    0.464104] OMAP DMA hardware revision 0.0
[    0.475134] edma 43300000.edma: memcpy is disabled
[    0.478391] edma 43300000.edma: TI EDMA DMA engine driver
[    0.485284] omap-dma-engine 4a056000.dma-controller: OMAP DMA engine driver (LinkedList1/2/3 supported)
[    0.486072] evm_5v0: supplied by main_12v0
[    0.489402] omap-iommu 40d01000.mmu: 40d01000.mmu registered
[    0.489581] omap-iommu 40d02000.mmu: 40d02000.mmu registered
[    0.489816] omap-iommu 58882000.mmu: 58882000.mmu registered
[    0.490067] omap-iommu 55082000.mmu: 55082000.mmu registered
[    0.490429] omap-iommu 41501000.mmu: 41501000.mmu registered
[    0.490602] omap-iommu 41502000.mmu: 41502000.mmu registered
[    0.490875] iommu: Adding device 58820000.ipu to group 1
[    0.490954] iommu: Adding device 55020000.ipu to group 2
[    0.491078] iommu: Adding device 40800000.dsp to group 0
[    0.491304] iommu: Adding device 41000000.dsp to group 3
[    0.492530] SCSI subsystem initialized
[    0.493571] palmas 0-0058: Irq flag is 0x00000008
[    0.521299] palmas 0-0058: Muxing GPIO 2f, PWM 0, LED 0
[    0.522891] SMPS12: supplied by regulator-dummy
[    0.524568] SMPS3: supplied by regulator-dummy
[    0.526218] SMPS45: supplied by regulator-dummy
[    0.528014] SMPS6: supplied by regulator-dummy
[    0.529450] SMPS7: supplied by regulator-dummy
[    0.530822] SMPS8: supplied by regulator-dummy
[    0.532078] SMPS9: supplied by regulator-dummy
[    0.532749] LDO1: supplied by regulator-dummy
[    0.539812] LDO2: supplied by regulator-dummy
[    0.540309] random: fast init done
[    0.549682] LDO3: supplied by regulator-dummy
[    0.559688] LDO4: supplied by regulator-dummy
[    0.569715] LDO5: supplied by regulator-dummy
[    0.570388] LDO6: supplied by regulator-dummy
[    0.571077] LDO7: supplied by regulator-dummy
[    0.571760] LDO8: supplied by regulator-dummy
[    0.572457] LDO9: supplied by regulator-dummy
[    0.579736] LDOLN: supplied by regulator-dummy
[    0.589760] LDOUSB: supplied by regulator-dummy
[    0.602133] omap_i2c 48070000.i2c: bus 0 rev0.12 at 400 kHz
[    0.602717] omap_i2c 48060000.i2c: bus 2 rev0.12 at 400 kHz
[    0.602928] media: Linux media interface: v0.10
[    0.602968] videodev: Linux video capture interface: v2.00
[    0.603040] pps_core: LinuxPPS API ver. 1 registered
[    0.603050] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.603071] PTP clock support registered
[    0.603106] EDAC MC: Ver: 3.0.0
[    0.603892] omap-mailbox 48840000.mailbox: omap mailbox rev 0x400
[    0.604137] omap-mailbox 48842000.mailbox: omap mailbox rev 0x400
[    0.604620] Advanced Linux Sound Architecture Driver Initialized.
[    0.605390] clocksource: Switched to clocksource arch_sys_counter
[    0.613605] NET: Registered protocol family 2
[    0.614191] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 6144 bytes)
[    0.614221] TCP established hash table entries: 8192 (order: 3, 32768 bytes)
[    0.614284] TCP bind hash table entries: 8192 (order: 4, 65536 bytes)
[    0.614410] TCP: Hash tables configured (established 8192 bind 8192)
[    0.614481] UDP hash table entries: 512 (order: 2, 16384 bytes)
[    0.614516] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes)
[    0.614644] NET: Registered protocol family 1
[    0.615052] RPC: Registered named UNIX socket transport module.
[    0.615063] RPC: Registered udp transport module.
[    0.615072] RPC: Registered tcp transport module.
[    0.615080] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.616116] hw perfevents: no interrupt-affinity property for /pmu, guessing.
[    0.616303] hw perfevents: enabled with armv7_cortex_a15 PMU driver, 7 counters available
[    0.617276] Initialise system trusted keyrings
[    0.617415] workingset: timestamp_bits=14 max_order=19 bucket_order=5
[    0.622237] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.622702] NFS: Registering the id_resolver key type
[    0.622725] Key type id_resolver registered
[    0.622735] Key type id_legacy registered
[    0.622777] ntfs: driver 2.1.32 [Flags: R/O].
[    0.624328] Key type asymmetric registered
[    0.624340] Asymmetric key parser 'x509' registered
[    0.624390] bounce: pool size: 64 pages
[    0.624424] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 245)
[    0.624435] io scheduler noop registered
[    0.624444] io scheduler deadline registered
[    0.624538] io scheduler cfq registered (default)
[    0.624550] io scheduler mq-deadline registered
[    0.624559] io scheduler kyber registered
[    0.630264] pinctrl-single 4a003400.pinmux: 282 pins, size 1128
[    0.633797] dra7-pcie 51000000.pcie: Linked as a consumer to phy-4a094000.pciephy.1
[    0.634057] dra7-pcie 51000000.pcie: WA for Errata i870 not applied
[    0.634146] dra7-pcie 51000000.pcie: host bridge /ocp/axi@0/pcie@51000000 ranges:
[    0.634187] dra7-pcie 51000000.pcie:    IO 0x20003000..0x20012fff -> 0x00000000
[    0.634216] dra7-pcie 51000000.pcie:   MEM 0x20013000..0x2fffffff -> 0x20013000
[    1.634502] dra7-pcie 51000000.pcie: Phy link never came up
[    1.634640] dra7-pcie 51000000.pcie: PCI host bridge to bus 0000:00
[    1.634655] pci_bus 0000:00: root bus resource [bus 00-ff]
[    1.634667] pci_bus 0000:00: root bus resource [io  0x0000-0xffff]
[    1.634679] pci_bus 0000:00: root bus resource [mem 0x20013000-0x2fffffff]
[    1.641083] PCI: bus0: Fast back to back transfers disabled
[    1.646946] PCI: bus1: Fast back to back transfers enabled
[    1.646990] pci 0000:00:00.0: BAR 0: assigned [mem 0x20100000-0x201fffff 64bit]
[    1.647010] pci 0000:00:00.0: PCI bridge to [bus 01-ff]
[    1.647327] pcieport 0000:00:00.0: Signaling PME with IRQ 172
[    1.647457] pcieport 0000:00:00.0: AER enabled with IRQ 172
[    1.651119] vdd_3v3: supplied by regen1
[    1.651348] aic_dvdd_fixed: supplied by vdd_3v3
[    1.651412] vtt_fixed: supplied by smps3
[    1.695299] Serial: 8250/16550 driver, 10 ports, IRQ sharing enabled
[    1.698822] console [ttyS2] disabled
[    1.698878] 48020000.serial: ttyS2 at MMIO 0x48020000 (irq = 45, base_baud = 3000000) is a 8250
[    2.804396] console [ttyS2] enabled
[    2.810237] omap_rng 48090000.rng: Random Number Generator ver. 20
[    2.810589] random: crng init done
[    2.817362] omapdss_dss 58000000.dss: Linked as a consumer to regulator.22
[    2.826959] DSS: OMAP DSS rev 6.1
[    2.833170] omapdss_hdmi5 58040000.encoder: Linked as a consumer to regulator.16
[    2.840732] omapdss_hdmi5 58040000.encoder: Dropping the link to regulator.16
[    2.861020] brd: module loaded
[    2.870184] loop: module loaded
[    2.877300] libphy: Fixed MDIO Bus: probed
[    2.935417] davinci_mdio 48485000.mdio: davinci mdio revision 1.6, bus freq 1000000
[    2.943113] libphy: 48485000.mdio: probed
[    2.951851] davinci_mdio 48485000.mdio: phy[1]: device 48485000.mdio:01, driver Micrel KSZ9031 Gigabit PHY
[    2.961574] davinci_mdio 48485000.mdio: phy[2]: device 48485000.mdio:02, driver Micrel KSZ9031 Gigabit PHY
[    2.971825] cpsw 48484000.ethernet: Detected MACID = d4:36:39:26:8c:68
[    2.978443] cpsw 48484000.ethernet: initialized cpsw ale version 1.4
[    2.984827] cpsw 48484000.ethernet: ALE Table size 1024
[    2.990134] cpsw 48484000.ethernet: cpts: overflow check period 500 (jiffies)
[    2.998113] cpsw 48484000.ethernet: cpsw: Detected MACID = d4:36:39:26:8c:69
[    3.006393] i2c /dev entries driver
[    3.013031] gpio-fan gpio_fan: GPIO fan initialized
[    3.019094] tmp102 0-0048: initialized
[    3.025888] cpu cpu0: Linked as a consumer to regulator.6
[    3.031383] cpu cpu0: Linked as a consumer to regulator.29
[    3.038524] sdhci: Secure Digital Host Controller Interface driver
[    3.044734] sdhci: Copyright(c) Pierre Ossman
[    3.050359] sdhci-pltfm: SDHCI platform and OF driver helper
[    3.056697] omap_gpio 4805d000.gpio: Could not set line 27 debounce to 200000 microseconds (-22)
[    3.065557] sdhci-omap 4809c000.mmc: Got CD GPIO
[    3.070352] sdhci-omap 4809c000.mmc: Linked as a consumer to regulator.28
[    3.077389] sdhci-omap 4809c000.mmc: Linked as a consumer to regulator.13
[    3.084257] sdhci-omap 4809c000.mmc: Dropping the link to regulator.13
[    3.090899] sdhci-omap 4809c000.mmc: Linked as a consumer to regulator.3
[    3.097825] sdhci-omap 4809c000.mmc: Linked as a consumer to regulator.13
[    3.104763] sdhci-omap 4809c000.mmc: no pinctrl state for ddr_3_3v mode
[    3.135908] mmc0: SDHCI controller on 4809c000.mmc [4809c000.mmc] using ADMA
[    3.144042] sdhci-omap 480b4000.mmc: Linked as a consumer to regulator.3
[    3.150879] sdhci-omap 480b4000.mmc: Dropping the link to regulator.3
[    3.157664] sdhci-omap 480b4000.mmc: Linked as a consumer to regulator.3
[    3.188176] mmc1: SDHCI controller on 480b4000.mmc [480b4000.mmc] using ADMA
[    3.196671] ledtrig-cpu: registered to indicate activity on CPUs
[    3.209023] NET: Registered protocol family 10
[    3.214246] Segment Routing with IPv6
[    3.218024] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    3.224368] NET: Registered protocol family 17
[    3.229057] Key type dns_resolver registered
[    3.233474] Registering SWP/SWPB emulation handler
[    3.238338] omap_voltage_late_init: Voltage driver support not added
[    3.244739] Power Management for TI OMAP4+ devices.
[    3.250243] Loading compiled-in X.509 certificates
[    3.260069] mmc0: host does not support reading read-only switch, assuming write-enable
[    3.271059] mmc0: new high speed SDHC card at address aaaa
[    3.277806] mmc1: new DDR MMC card at address 0001
[    3.282883] dmm 4e000000.dmm: workaround for errata i878 in use
[    3.287314] mmcblk0: mmc0:aaaa SD32G 29.7 GiB
[    3.293764] mmcblk1: mmc1:0001 M62704 3.53 GiB
[    3.298373] dmm 4e000000.dmm: initialized all PAT entries
[    3.298748] mmcblk1boot0: mmc1:0001 M62704 partition 1 2.00 MiB
[    3.310136] omapdss_hdmi5 58040000.encoder: Linked as a consumer to regulator.16
[    3.310650] mmcblk1boot1: mmc1:0001 M62704 partition 2 2.00 MiB
[    3.317667] omapdss_hdmi5 58040000.encoder: Dropping the link to regulator.16
[    3.330915] mmcblk1rpmb: mmc1:0001 M62704 partition 3 512 KiB, chardev (244:0)
[    3.338254]  mmcblk0: p1 p2
[    3.341853] omapdss_hdmi5 58040000.encoder: Linked as a consumer to regulator.16
[    3.342447]  mmcblk1: p1
[    3.353345] omapdss_dss 58000000.dss: bound 58001000.dispc (ops dispc_component_ops)
[    3.361350] omapdss_dss 58000000.dss: bound 58040000.encoder (ops hdmi5_component_ops)
[    3.370063] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[    3.376725] [drm] No driver support for vblank timestamp query.
[    3.382685] [drm] Cannot find any crtc or sizes
[    3.387923] [drm] Initialized omapdrm 1.0.0 20110917 for omapdrm.0 on minor 0
[    3.395522] hctosys: unable to open rtc device (rtc0)
[    3.401184] aic_dvdd_fixed: disabling
[    3.405023] ldousb: disabling
[    3.408306] ALSA device list:
[    3.411285]   No soundcards found.
[    3.434404] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[    3.442580] VFS: Mounted root (ext4 filesystem) on device 179:2.
[    3.456524] devtmpfs: mounted
[    3.460379] Freeing unused kernel memory: 2048K
[    3.465026] Run /sbin/init as init process
[    3.823309] systemd[1]: System time before build time, advancing clock.
[    3.857551] systemd[1]: systemd 239 running in system mode. (+PAM -AUDIT -SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP -LIBCRYPTSETUP -GCRYPT -GNUTLS +ACL +XZ -LZ4 -SECCOMP +BLKID -ELFUTILS +KMOD -IDN2 -IDN -PCRE2 default-hierarchy=hybrid)
[    3.879437] systemd[1]: Detected architecture arm.

Welcome to Arago 2019.07!

[    3.916489] systemd[1]: Set hostname to <am57xx-evm>.
[    4.095030] systemd[1]: File /lib/systemd/system/systemd-journald.service:36 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling.
[    4.112214] systemd[1]: Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.)
[    4.322359] systemd[1]: Listening on initctl Compatibility Named Pipe.
[  OK  ] Listening on initctl Compatibility Named Pipe.
[    4.355755] systemd[1]: Reached target Remote File Systems.
[  OK  ] Reached target Remote File Systems.
[    4.385842] systemd[1]: Listening on udev Control Socket.
[  OK  ] Listening on udev Control Socket.
[    4.415450] [drm] Cannot find any crtc or sizes
[    4.415537] systemd[1]: Reached target Swap.
[  OK  ] Reached target Swap.
[    4.445685] systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
[  OK  ] Started Dispatch Password Requests to Console Directory Watch.
[  OK  ] Listening on Journal Socket.
[  OK  ] Started Hardware RNG Entropy Gatherer Daemon.
         Starting Load Kernel Modules...
         Mounting Kernel Debug File System...
[    4.618329] cmemk: loading out-of-tree module taints kernel.
         [    4.625117] CMEMK module: reference Linux version 4.19.59
Mounting POSIX Message Queue File System...
[    4.631211] allocated heap buffer 0x40500000 of size 0x100000
[    4.640423] cmemk initialized
[    4.650865] cryptodev: driver 1.9 loaded.
         Starting Create list of required st…ce nodes for the current kernel...
         Starting Remount Root and Kernel File Systems...
[    4.704568] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[    4.717326] usbcore: registered new interface driver usbfs
[    4.722868] usbcore: registered new interface driver hub
         [    4.728785] usbcore: registered new device driver usb
Mounting Temporary Directory (/tmp)...
[    4.749212] usbcore: registered new interface driver ftdi_sio
[    4.755020] usbserial: USB Serial support registered for FTDI USB Serial Device
[  OK  ] Started Forward Password Requests to Wall Directory Watch.
[  OK  ] Reached target Paths.
[  OK  ] Listening on Journal Socket (/dev/log).
[  OK  ] Created slice system-getty.slice.
[  OK  ] Listening on udev Kernel Socket.
         Starting udev Coldplug all Devices...
[  OK  ] Created slice User and Session Slice.
[  OK  ] Listening on Process Core Dump Socket.
         Starting Journal Service...
[  OK  ] Reached target Slices.
[  OK  ] Listening on Network Service Netlink Socket.
[  OK  ] Created slice system-serial\x2dgetty.slice.
[  OK  ] Started Journal Service.
[  OK  ] Started Load Kernel Modules.
[  OK  ] Mounted Kernel Debug File System.
[  OK  ] Mounted POSIX Message Queue File System.
[  OK  ] Started Create list of required sta…vice nodes for the current kernel.
[  OK  ] Started Remount Root and Kernel File Systems.
[  OK  ] Mounted Temporary Directory (/tmp).
         Starting Create Static Device Nodes in /dev...
         Mounting Kernel Configuration File System...
         Starting Apply Kernel Variables...
         Starting Flush Journal to Persistent Storage...
[  OK  ] Started Create Static Device Nodes in /dev.
[  OK  ] Mounted Kernel Configuration File System.
[  OK  ] Started Apply Kernel Variables.
[    5.583375] systemd-journald[106]: Received request to flush runtime journal from PID 1
         Starting udev Kernel Device Manager...
[  OK  ] Reached target Local File Systems (Pre).
         Mounting /media/ram...
[  OK  ] Reached target Containers.
         Mounting /var/volatile...
[  OK  ] Started udev Kernel Device Manager.
[  OK  ] Started Flush Journal to Persistent Storage.
[  OK  ] Mounted /media/ram.
[  OK  ] Mounted /var/volatile.
         Starting Load/Save Random Seed...
[  OK  ] Reached target Local File Systems.
         Starting Create Volatile Files and Directories...
[  OK  ] Started Load/Save Random Seed.
[    5.941047] omap-rproc 58820000.ipu: ignoring dependency for device, assuming no driver
[    5.992893] omap-rproc 58820000.ipu: ignoring dependency for device, assuming no driver
[    6.042606] omap-rproc 58820000.ipu: assigned reserved memory node ipu1-memory@9d000000
[  OK  ] Found device /dev/ttyS2.
[    6.105337] remoteproc remoteproc0: 58820000.ipu is available
[  OK  ] Started Create Volatile Files and Directories.
         Starting Update UTMP about System Boot/Shutdown...
[    6.157033] omap-rproc 55020000.ipu: ignoring dependency for device, assuming no driver
         Starting Network Service...
[    6.201717] omap-rproc 55020000.ipu: ignoring dependency for device, assuming no driver
         Starting Network Time Synchronization...
[    6.226820] omap-rproc 55020000.ipu: assigned reserved memory node ipu2-memory@95800000
[  OK  ] Started udev Coldplug all Devices.
[    6.239299] remoteproc remoteproc1: 55020000.ipu is available
[    6.281360] omap-rproc 40800000.dsp: ignoring dependency for device, assuming no driver
[  OK  ] Started Update UTMP about System Boot/Shutdow[    6.304501] omap-rproc 40800000.dsp: ignoring dependency for device, assuming no driver
n.
[    6.352470] omap-rproc 40800000.dsp: assigned reserved memory node dsp1-memory@99000000
[    6.361522] remoteproc remoteproc2: 40800000.dsp is available
[    6.384840] omap-rproc 41000000.dsp: ignoring dependency for device, assuming no driver
[    6.400556] omap-rproc 41000000.dsp: ignoring dependency for device, assuming no driver
[    6.416789] omap-rproc 41000000.dsp: assigned reserved memory node dsp2-memory@9f000000
[    6.425308] remoteproc remoteproc3: 41000000.dsp is available
[    6.511841] palmas-rtc 48070000.i2c:tps659038@58:tps659038_rtc: rtc core: registered 48070000.i2c:tps659038@58:tps659038_rtc as rtc1
[    6.891461] omap_rtc 48838000.rtc: registered as rtc2
[    6.899609] Driver for 1-wire Dallas network protocol.
[    6.912203] remoteproc remoteproc1: powering up 55020000.ipu
[    6.917943] remoteproc remoteproc1: Booting fw image dra7-ipu2-fw.xem4, size 3747220
[    6.926271] omap-iommu 55082000.mmu: 55082000.mmu: version 2.1
[    6.977675] remoteproc remoteproc0: powering up 58820000.ipu
[    6.983371] remoteproc remoteproc0: Booting fw image dra7-ipu1-fw.xem4, size 6726740
[    7.010839] omap-iommu 58882000.mmu: 58882000.mmu: version 2.1
[    7.017845] virtio_rpmsg_bus virtio0: rpmsg host is online
[    7.024273] remoteproc remoteproc1: registered virtio0 (type 7)
[    7.030574] remoteproc remoteproc1: remote processor 55020000.ipu is now up
[    7.038084] virtio_rpmsg_bus virtio0: creating channel rpmsg-rpc addr 0x65
[    7.045642] virtio_rpmsg_bus virtio0: creating channel rpmsg-rpc addr 0x66
[    7.064089] virtio_rpmsg_bus virtio1: rpmsg host is online
[    7.076344] remoteproc remoteproc0: registered virtio1 (type 7)
[    7.082296] remoteproc remoteproc0: remote processor 58820000.ipu is now up
[    7.158409] at24 0-0050: 4096 byte 24c32 EEPROM, writable, 1 bytes/write
[    7.185447] omap_hdq 480b2000.1w: OMAP HDQ Hardware Rev 0.:. Driver in Interrupt mode
[    7.226539] virtio_rpmsg_bus virtio1: creating channel rpmsg-proto addr 0x3d
[    7.243788] virtio_rpmsg_bus virtio1: msg received with no recipient
[    7.253200] tlv320aic3x-codec 0-0018: Linked as a consumer to regulator.3
[    7.261156] tlv320aic3x-codec 0-0018: Linked as a consumer to regulator.4
[    7.372207] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
[    7.386084] ov5640 2-003c: Linked as a consumer to regulator.0
[    7.391975] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
[    7.400288] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
[    7.408385] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
[    7.416019] w1_master_driver w1_bus_master1: Attaching one wire slave 01.000000000000 crc 3d
[    7.431775] w1_master_driver w1_bus_master1: Family 1 for 01.000000000000.3d is not registered.
[  OK  ] Started Network Time Synchronization.
[  OK  ] Reached target System Initialization.
[  OK  ] Listening on RPCbind Server Activation Socket.
[  OK  ] Listening on Avahi mDNS/DNS-SD Stack Activation Socket.
[    7.582720] omap-des 480a5000.des: OMAP DES hw accel rev: 2.2
[    7.590476] net eth1: initializing cpsw version 1.15 (0)
         Starting Reboot and dump vmcore via kexec...
[    7.605431] omap-des 480a5000.des: will run requests pump with realtime priority
[    7.739887] Micrel KSZ9031 Gigabit PHY 48485000.mdio:02: attached PHY driver [Micrel KSZ9031 Gigabit PHY] (mii_bus:phy_addr=48485000.mdio:02, irq=POLL)
[    7.758390] IPv6: ADDRCONF(NETDEV_UP): eth1: link is not ready
[    7.767323] net eth0: initializing cpsw version 1.15 (0)
[    7.827684] phy phy-4a084000.phy.3: Linked as a consumer to regulator.23
[    7.838220] phy phy-4a085000.phy.4: Linked as a consumer to regulator.23
[  OK  ] Listening on dropbear.socket.
[    7.847124] omap-sham 4b101000.sham: hw accel on OMAP rev 4.3
[    7.857185] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
[  OK  ] Started Daily Cleanup of Temporary Directorie[    7.866261] omap_wdt: OMAP Watchdog Timer Rev 0x01: initial timeout 60 sec
s.
[    7.922433] Micrel KSZ9031 Gigabit PHY 48485000.mdio:01: attached PHY driver [Micrel KSZ9031 Gigabit PHY] (mii_bus:phy_addr=48485000.mdio:01, irq=POLL)
[    7.943552] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[    7.985577] vpe 489d0000.vpe: Device registered as /dev/video0
[    8.077066] omap-aes 4b500000.aes: OMAP AES hw accel rev: 3.3
[    8.087269] omap-aes 4b500000.aes: will run requests pump with realtime priority
[    8.101428] omap-aes 4b700000.aes: OMAP AES hw accel rev: 3.3
[    8.107421] omap-aes 4b700000.aes: will run requests pump with realtime priority
[  OK  ] Listening on D-Bus System Message Bus Socket.[    8.166757] omap-hdmi-audio omap-hdmi-audio.0.auto: snd-soc-dummy-dai <-> 58040000.encoder mapping ok

[  OK  ] Reached target Sockets.
[    8.182219] omap-hdmi-audio omap-hdmi-audio.0.auto: ASoC: no DMI vendor name!
[  OK  ] Reached target Basic System.
[    8.243859] virtio_rpmsg_bus virtio1: msg received with no recipient
[    8.322923] [drm] Initialized pvr 1.17.4948957 20110701 for 56000000.gpu on minor 1
[  OK  ] Started Periodic Command Scheduler.
         Starting TI MultiCore Tools Daemon...
         Starting TI IPC Daemon...
[  OK  ] Started Job spooling tools.
[    8.608802] ahci 4a140000.sata: controller can't do 64bit DMA, forcing 32bit
[    8.616232] ahci 4a140000.sata: forcing port_map 0x0 -> 0x1
[    8.623582] ahci 4a140000.sata: AHCI 0001.0300 32 slots 1 ports 3 Gbps 0x1 impl platform mode
[    8.632301] ahci 4a140000.sata: flags: ncq sntf pm led clo only pmp pio slum part ccc apst
         [    8.643142] asoc-simple-card sound0: tlv320aic3x-hifi <-> 48468000.mcasp mapping ok
Starting RPC Bind Service...
[    8.652433] asoc-simple-card sound0: ASoC: no DMI vendor name!
         Starting Print notice about GPLv3 packages...
[    8.683802] scsi host0: ahci
[    8.687059] ata1: SATA max UDMA/133 mmio [mem 0x4a140000-0x4a1410ff] port 0x100 irq 80
[  OK  ] Started D-Bus System Message Bus.
         Starting Login Service...
         Starting uim-sysfs.service...
[  OK  ] Reached target System Time Synchronized.
[  OK  ] Started Daily rotation of log files.
[  OK  ] Reached target Timers.
[  OK  ] Started Network Service.
[    8.883613] vip 48990000.vip: loading firmware vpdma-1b8.bin
         Starting Network Name Resolution...
         Starting Wait for Network to be Configured...
[    9.015406] vip 48990000.vip: VPDMA firmware loaded
[    9.021100] vin3a: Port A: Using subdev ov5640 2-003c for capture
[    9.027849] vin3a-0: device registered as video1
[    9.031957] ata1: SATA link down (SStatus 0 SControl 300)
[    9.068123] remoteproc remoteproc3: powering up 41000000.dsp
[    9.073817] remoteproc remoteproc3: Booting fw image dra7-dsp2-fw.xe66, size 20481364
[    9.093599] remoteproc remoteproc2: powering up 40800000.dsp
[    9.113728] remoteproc remoteproc2: Booting fw image dra7-dsp1-fw.xe66, size 20481364
[    9.135622] omap-iommu 41501000.mmu: 41501000.mmu: version 3.0
[    9.141511] omap-iommu 41502000.mmu: 41502000.mmu: version 3.0
[    9.147935] omap-iommu 40d01000.mmu: 40d01000.mmu: version 3.0
[    9.153834] omap-iommu 40d02000.mmu: 40d02000.mmu: version 3.0
[    9.257165] virtio_rpmsg_bus virtio1: msg received with no recipient
[    9.265591] virtio_rpmsg_bus virtio2: rpmsg host is online
[    9.271737] remoteproc remoteproc3: registered virtio2 (type 7)
[    9.351720] remoteproc remoteproc3: remote processor 41000000.dsp is now up
[    9.458050] virtio_rpmsg_bus virtio2: creating channel rpmsg-proto addr 0x3d
[    9.523601] virtio_rpmsg_bus virtio3: rpmsg host is online
[    9.534728] remoteproc remoteproc2: registered virtio3 (type 7)
[  OK  ] Started Login Service.
[  OK  ] Started Network Name Resolution.
[  OK  ] Started Reboot and dump vmcore via kexec.
[  OK  ] Started TI MultiCore Tools Daemon.
[  OK  ] Started TI IPC Daemon.
[  OK  ] Started RPC Bind Se[    9.578866] remoteproc remoteproc2: remote processor 40800000.dsp is now up
rvice.
[    9.719176] virtio_rpmsg_bus virtio3: creating channel rpmsg-proto addr 0x3d
[   10.248858] virtio_rpmsg_bus virtio1: msg received with no recipient
[   11.070048] dwc3 48890000.usb: Failed to get clk 'ref': -2
[   11.088584] rpmsg_rpc virtio0.rpmsg-rpc.-1.101: probing service dce-callback with src 1024 dst 101
[   11.097917] NET: Registered protocol family 45
[   11.098835] dwc3 488d0000.usb: Failed to get clk 'ref': -2
[  OK  ] Started uim-sysfs.service.
[   11.117845] rpmsg_rpc virtio0.rpmsg-rpc.-1.102: probing service rpmsg-dce with src 1025 dst 102
[   11.118181] rpmsg_rpc virtio0.rpmsg-rpc.-1.101: published functions = 4
[   11.197896] rpmsg_rpc virtio0.rpmsg-rpc.-1.102: published functions = 9
[   11.268470] Unable to handle kernel NULL pointer dereference at virtual address 0000001c
[   11.290303] remoteproc remoteproc4: 4b234000.pru is available
[   11.299818] pgd = a5537d37
[   11.302804] [0000001c] *pgd=acffd003, *pmd=00000000
[   11.322149] Internal error: Oops: 206 [#1] PREEMPT SMP ARM
[   11.327667] Modules linked in: pru_rproc(+) irq_pruss_intc pruss rpmsg_proto rpmsg_rpc dwc3 udc_core ti_vip snd_soc_simple_card ahci_platform snd_soc_simple_card_utils libahci_platform libahci pvrsrvkm(O) snd_soc_omap_hdmi omap_aes_driver libata pruss_soc_bus ti_vpe omap_wdt omap_sham phy_omap_usb2 ti_sc omap_des ti_csc ov5640 des_generic ti_vpdma snd_soc_tlv320aic3x at24 omap_hdq v4l2_fwnode crypto_engine omap_crypto dwc3_omap wire extcon_palmas rtc_omap rtc_palmas omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
[   11.327947] pru-rproc 4b234000.pru: PRU rproc node pru@4b234000 probed successfully
[   11.384102] CPU: 0 PID: 718 Comm: v4l_id Tainted: G           O      4.19.59-g5f8c1c6121 #11
[   11.384106] Hardware name: Generic DRA74X (Flattened Device Tree)
[   11.384118] PC is at __v4l2_ctrl_s_ctrl_int64+0x10/0x5c
[   11.384130] LR is at ov5640_set_fmt+0x1a0/0x288 [ov5640]
[   11.384137] pc : [<c07c34c0>]    lr : [<bf1abfa4>]    psr: 600f0013
[   11.423254] sp : ec5fdb70  ip : ec5fdb90  fp : ec5fdb8c
[   11.428503] r10: 00000004  r9 : 00000000  r8 : 0000001e
[   11.433749] r7 : 00000000  r6 : ed693c44  r5 : 00000000  r4 : 00000000
[   11.440300] r3 : 00000000  r2 : 0501bd00  r1 : 00000000  r0 : 00000000
[   11.441493] remoteproc remoteproc5: 4b238000.pru is available
[   11.446852] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[   11.446857] Control: 30c5387d  Table: ad6b0880  DAC: fffffffd
[   11.446863] Process v4l_id (pid: 718, stack limit = 0x4f6a3e88)
[   11.446867] Stack: (0xec5fdb70 to 0xec5fe000)
[   11.446872] db60:                                     00000000 00000000 ed693c44 ed693d8c
[   11.446878] db80: ec5fdbb4 ec5fdb90 bf1abfa4 c07c34bc ec426040 bf339000 c1204c48 bf339400
[   11.454604] pru-rproc 4b238000.pru: PRU rproc node pru@4b238000 probed successfully
[   11.459801] dba0: 00000000 00000000 ec5fdc4c ec5fdbb8 bf3354d8 bf1abe10 ec5fdbdc ec5fdbc8
[   11.459806] dbc0: c030226c c0300f30 ffefe8e8 00000001 00000000 00000780 00000438 00002006
[   11.459811] dbe0: 00000001 00000008 00010001 00000002 00000000 00000000 00000000 00000000
[   11.459819] dc00: 00000000 ec5fdc10 c0215168 c0215028 c1066e2c 00000014 c0321f48 00000000
[   11.466911] remoteproc remoteproc6: 4b2b4000.pru is available
[   11.471522] dc20: 00000001 a9166ef0 ec426040 c1204c48 00000000 ecd3f000 ed6a43c0 00000000
[   11.476695] pru-rproc 4b2b4000.pru: PRU rproc node pru@4b2b4000 probed successfully
[   11.484096] dc40: ec5fdd54 ec5fdc50 bf33579c bf335320 00000081 ec5fdd04 00000002 a9166ef0
[   11.493271] remoteproc remoteproc7: 4b2b8000.pru is available
[   11.499987] dc60: ec5fdd3c ecd3f000 00000081 006200ca ec52a750 b6e4e000 ecdee400 00000000
[   11.499992] dc80: ec5fdd6c ec5fdc90 c02f915c c02f7a74 000000a0 c1204c48 ec5fdcb4 a9166ef0
[   11.499996] dca0: c02ba800 a00f0013 ffffffff ec5fdcfc ec5fdd98 ec5fc000 ec5fdd34 ec5fdcc8
[   11.500001] dcc0: c0201970 c021512c bf339200 00000001 ec5fc000 bf339200 ed9c77c0 bf339200
[   11.500008] dce0: ee80dc00 ed9c77c0 ec5fdd98 00000000 ed9c77c0 bf339200 ec5fdd14 ec5fdd08
[   11.509436] pru-rproc 4b2b8000.pru: PRU rproc node pru@4b2b8000 probed successfully
[   11.516422] dd00: ec5fdd1c ec5fdd10 c07b2a38 c07b29a8 ec5fdd34 ec5fdd20 c07bb128 c0a13090
[   11.516426] dd20: edbe0780 ed6a43c0 ec5fdd54 ec5fdd38 ec5fdd54 a9166ef0 ecedaca4 ed6a43c0
[   11.516431] dd40: 00000000 ecd3f000 ec5fdd74 ec5fdd58 bf3359a0 bf335778 ecf94000 bf335924
[   11.516438] dd60: ed6a43c0 ecf940f0 ec5fdd94 ec5fdd78 c07b2fe0 bf335930 c0c80048 c1204c48
[   11.649976] dd80: ed9c77c0 ece75470 ec5fddc4 ec5fdd98 c0359060 c07b2f68 00000000 a9166ef0
[   11.658185] dda0: ed6a43c0 ece75470 00000000 00020000 ed6a43c8 c0358fac ec5fddec ec5fddc8
[   11.666397] ddc0: c03509c4 c0358fb8 ec5fde90 00000000 00000000 00020000 ee4023b8 ed6a43c0
[   11.674607] dde0: ec5fddfc ec5fddf0 c0351ed0 c03508b4 ec5fde8c ec5fde00 c036369c c0351ea8
[   11.682816] de00: c0324130 c0219ba0 00000000 200f0013 c0ddac38 00080000 ffffe000 04600000
[   11.691026] de20: 0000075f ffefe000 00000041 c1204c48 00000002 00000000 ed6a43c0 00000000
[   11.699238] de40: 00000002 ece75470 edb05510 ec898c38 00000000 00000000 00000000 a9166ef0
[   11.707447] de60: 00000000 c1204c48 c1204c48 ec5fde90 ec5fdf50 00000001 ec5fc000 00000142
[   11.715656] de80: ec5fdf44 ec5fde90 c0365580 c03633f0 edb05510 ec898c38 ebcda8fb 00000006
[   11.723866] dea0: ecdd2015 c027eab0 00000000 ee6d8000 ece75470 00000101 00000002 00000658
[   11.732076] dec0: 00000000 00000000 00000000 ec5fded0 ec5fdf04 00000003 ed66c500 ed66c518
[   11.740288] dee0: 00000000 00000400 ed66c540 00000020 ec5fdf34 ec5fdf00 c0373e5c c0a13038
[   11.748499] df00: ecdd2000 00000000 00000001 00000002 ffffff9c ffffff9c ecdd2000 a9166ef0
[   11.756708] df20: ec5fc000 00000003 c1204c48 ffffff9c ecdd2000 fffff000 ec5fdf94 ec5fdf48
[   11.764917] df40: c03521cc c0365510 c02411fc c0a1319c 00020000 ec5f0000 00000004 00000100
[   11.773127] df60: 00000001 a9166ef0 00000008 b6f1c968 00417f38 00000000 00000142 c0201204
[   11.781336] df80: ec5fc000 00000142 ec5fdfa4 ec5fdf98 c0352288 c0352068 00000000 ec5fdfa8
[   11.789548] dfa0: c0201000 c0352280 b6f1c968 00417f38 ffffff9c bed52f2c 00020000 00000000
[   11.797757] dfc0: b6f1c968 00417f38 00000000 00000142 bed52e34 00000000 00417f38 00000000
[   11.805966] dfe0: b6e7922c bed52c30 00405b59 b6e62680 600f0010 ffffff9c 00000000 00000000
[   11.814173] Backtrace:
[   11.816640] [<c07c34b0>] (__v4l2_ctrl_s_ctrl_int64) from [<bf1abfa4>] (ov5640_set_fmt+0x1a0/0x288 [ov5640])
[   11.826423]  r7:ed693d8c r6:ed693c44 r5:00000000 r4:00000000
[   11.832121] [<bf1abe04>] (ov5640_set_fmt [ov5640]) from [<bf3354d8>] (vip_init_port+0x1c4/0x458 [ti_vip])
[   11.841727]  r9:00000000 r8:00000000 r7:bf339400 r6:c1204c48 r5:bf339000 r4:ec426040
[   11.849514] [<bf335314>] (vip_init_port [ti_vip]) from [<bf33579c>] (vip_init_stream+0x30/0x1b8 [ti_vip])
[   11.859119]  r9:00000000 r8:ed6a43c0 r7:ecd3f000 r6:00000000 r5:c1204c48 r4:ec426040
[   11.866902] [<bf33576c>] (vip_init_stream [ti_vip]) from [<bf3359a0>] (vip_open+0x7c/0xac [ti_vip])
[   11.875984]  r7:ecd3f000 r6:00000000 r5:ed6a43c0 r4:ecedaca4
[   11.881673] [<bf335924>] (vip_open [ti_vip]) from [<c07b2fe0>] (v4l2_open+0x84/0x110)
[   11.889535]  r7:ecf940f0 r6:ed6a43c0 r5:bf335924 r4:ecf94000
[   11.895220] [<c07b2f5c>] (v4l2_open) from [<c0359060>] (chrdev_open+0xb4/0x19c)
[   11.902557]  r7:ece75470 r6:ed9c77c0 r5:c1204c48 r4:c0c80048
[   11.908241] [<c0358fac>] (chrdev_open) from [<c03509c4>] (do_dentry_open+0x11c/0x3c0)
[   11.916103]  r9:c0358fac r8:ed6a43c8 r7:00020000 r6:00000000 r5:ece75470 r4:ed6a43c0
[   11.923877] [<c03508a8>] (do_dentry_open) from [<c0351ed0>] (vfs_open+0x34/0x38)
[   11.931301]  r9:ed6a43c0 r8:ee4023b8 r7:00020000 r6:00000000 r5:00000000 r4:ec5fde90
[   11.939077] [<c0351e9c>] (vfs_open) from [<c036369c>] (path_openat+0x2b8/0x1114)
[   11.946507] [<c03633e4>] (path_openat) from [<c0365580>] (do_filp_open+0x7c/0xe4)
[   11.954020]  r10:00000142 r9:ec5fc000 r8:00000001 r7:ec5fdf50 r6:ec5fde90 r5:c1204c48
[   11.961878]  r4:c1204c48
[   11.964422] [<c0365504>] (do_filp_open) from [<c03521cc>] (do_sys_open+0x170/0x1f4)
[   11.972108]  r8:fffff000 r7:ecdd2000 r6:ffffff9c r5:c1204c48 r4:00000003
[   11.978836] [<c035205c>] (do_sys_open) from [<c0352288>] (sys_openat+0x14/0x18)
[   11.986174]  r10:00000142 r9:ec5fc000 r8:c0201204 r7:00000142 r6:00000000 r5:00417f38
[   11.994033]  r4:b6f1c968
[   11.996578] [<c0352274>] (sys_openat) from [<c0201000>] (ret_fast_syscall+0x0/0x4c)
[   12.004263] Exception stack(0xec5fdfa8 to 0xec5fdff0)
[   12.009335] dfa0:                   b6f1c968 00417f38 ffffff9c bed52f2c 00020000 00000000
[   12.017545] dfc0: b6f1c968 00417f38 00000000 00000142 bed52e34 00000000 00417f38 00000000
[   12.025752] dfe0: b6e7922c bed52c30 00405b59 b6e62680
[   12.030823] Code: e1a0c00d e92dd8f0 e24cb004 e1a07003 (e1d031dc)
[   12.072625] ---[ end trace ae3e54dd5ff30d07 ]---
[   12.119390] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[   12.131380] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 1
[   12.153294] xhci-hcd xhci-hcd.1.auto: hcc params 0x0220f04c hci version 0x100 quirks 0x0000000002010010
[   12.175837] xhci-hcd xhci-hcd.1.auto: irq 179, io mem 0x48890000
[   12.186668] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 4.19
[   12.197576] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[   12.207876] usb usb1: Product: xHCI Host Controller
[   12.214453] usb usb1: Manufacturer: Linux 4.19.59-g5f8c1c6121 xhci-hcd
[   12.222420] usb usb1: SerialNumber: xhci-hcd.1.auto
[   12.229793] hub 1-0:1.0: USB hub found
[   12.234421] hub 1-0:1.0: 1 port detected
[   12.239838] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[   12.246043] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 2
[   12.254061] xhci-hcd xhci-hcd.1.auto: Host supports USB 3.0 SuperSpeed
[   12.264782] usb usb2: We don't know the algorithms for LPM for this host, disabling LPM.
[   12.273962] usb usb2: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 4.19
[   12.282558] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[   12.290597] usb usb2: Product: xHCI Host Controller
[   12.295911] usb usb2: Manufacturer: Linux 4.19.59-g5f8c1c6121 xhci-hcd
[   12.302713] usb usb2: SerialNumber: xhci-hcd.1.auto
[   12.312160] hub 2-0:1.0: USB hub found
[   12.317390] hub 2-0:1.0: 1 port detected
[   12.605463] usb 1-1: new high-speed USB device number 2 using xhci-hcd
[   12.785975] usb 1-1: New USB device found, idVendor=0451, idProduct=8142, bcdDevice= 1.00
[   12.798274] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=1
[   12.806611] usb 1-1: SerialNumber: E3000079FEB7
[   12.872898] hub 1-1:1.0: USB hub found
[   12.877215] hub 1-1:1.0: 4 ports detected
[   12.936810] usb 2-1: new SuperSpeed Gen 1 USB device number 2 using xhci-hcd
[   12.976023] usb 2-1: New USB device found, idVendor=0451, idProduct=8140, bcdDevice= 1.00
[   12.984612] usb 2-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[   13.032932] hub 2-1:1.0: USB hub found
[   13.037967] hub 2-1:1.0: 4 ports detected
[   13.255434] usb 1-1.3: new low-speed USB device number 3 using xhci-hcd
[   13.401115] usb 1-1.3: New USB device found, idVendor=413c, idProduct=2003, bcdDevice= 3.01
[   13.409752] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[   13.418582] usb 1-1.3: Product: Dell USB Keyboard
[   13.423482] usb 1-1.3: Manufacturer: Dell
[   13.500565] input: Dell Dell USB Keyboard as /devices/platform/44000000.ocp/48880000.omap_dwc3_1/48890000.usb/xhci-hcd.1.auto/usb1/1-1/1-1.3/1-1.3:1.0/0003:413C:2003.0001/input/input0
[   13.588023] hid-generic 0003:413C:2003.0001: input: USB HID v1.10 Keyboard [Dell Dell USB Keyboard] on usb-xhci-hcd.1.auto-1.3/input0
[   13.601544] usbcore: registered new interface driver usbhid
[   13.609241] usbhid: USB HID core driver
***************************************************************
***************************************************************
NOTICE: This file system contains the following GPLv3 packages:
        autoconf
        bash-dev
        bash
        bc
        binutils-dev
        binutils
        bison-dev
        bison
        cifs-utils
        cpio
        cpp-symlinks
        cpp
        dosfstools
        elfutils-dev
        elfutils
        findutils
        g++-symlinks
        g++
        gawk
        gcc-symlinks
        gcc
        gdb
        gdbc6x
        gdbserver
        gettext
        glmark2
        gstreamer1.0-libav
        gzip
        hidapi
        libasm1
        libbfd
        libcairo-perf-utils
        libdw1
        libelf1
        libgdbm-compat4
        libgdbm-dev
        libgdbm6
        libgettextlib
        libgettextsrc
        libgmp10
        libidn2-0
        libmavconn
        libmpc3
        libmpfr6
        libreadline-dev
        libreadline7
        libunistring2
        m4-dev
        m4
        make
        mavlink
        mavros-extras
        mavros-msgs
        mavros
        nettle
        parted
        pdm-anomaly-detection
        socketcan-interface
        swig-dev
        swig
        which

If you do not wish to distribute GPLv3 components please remove
the above packages prior to distribution.  This can be done using
the opkg remove command.  i.e.:
    opkg remove <package>
Where <package> is the name printed in the list above

NOTE: If the package is a dependency of another package you
      will be notified of the dependent packages.  You should
      use the --force-removal-of-dependent-packages option to
      also remove the dependent packages as well
***************************************************************
***************************************************************
         Starting Save/Restore Sound Card State...
[  OK  ] Listening on Load/Save RF Kill Switch Status /dev/rfkill Watch.
         Starting rc.pvr.service...
[  OK  ] Created slice system-systemd\x2dfsck.slice.
[   15.365810] PVR_K: UM DDK-(4948957) and KM DDK-(4948957) match. [ OK ]
         Starting File System Check on /dev/mmcblk0p1...
         Starting File System Check on /dev/mmcblk1p1...
[  OK  ] Reached target Network.
         Starting Enable and configure wl18xx bluetooth stack...
         Starting Lightning Fast Webserver With Light System Requirements...
[  OK  ] Started Redis In-Memory Data Store.
         Starting Permit User Sessions...
         Starting Simple Network Management Protocol (SNMP) Daemon....
[  OK  ] Reached target Host and Network Name Lookups.
[  OK  ] Started NFS status monitor for NFSv2/3 locking..
         Starting Avahi mDNS/DNS-SD Stack...
[  OK  ] Started Print notice about GPLv3 packages.
[  OK  ] Started Save/Restore Sound Card State.
[  OK  ] Started rc.pvr.service.
[  OK  ] Started Enable and configure wl18xx bluetooth stack.
[  OK  ] Started Permit User Sessions.
[  OK  ] Started Serial Getty on ttyS2.
[  OK  ] Started Getty on tty1.
[  OK  ] Reached target Login Prompts.
         Starting Synchronize System and HW clocks...
         Starting weston.service...
[  OK  ] Reached target Sound Card.
[  OK  ] Started File System Check on /dev/mmcblk0p1.
         Mounting /run/media/mmcblk0p1...
[FAILED] Failed to start Synchronize System and HW clocks.
See 'systemctl status sync-clocks.service' for details.
[  OK  ] Started File System Check on /dev/mmcblk1p1.
[  OK  ] Started weston.service.
[  OK  ] Started Lightning Fast Webserver With Light System Requirements.
         Starting Matrix GUI...
         Starting telnetd.service...
         Mounting /run/media/mmcblk1p1...
[  OK  ] Started telnetd.service.
[   15.941483] EXT4-fs (mmcblk1p1): mounted filesystem with ordered data mode. Opts: (null)
[  OK  ] Started Matrix GUI.
[  OK  ] Mounted /run/media/mmcblk0p1.
[  OK  ] Mounted /run/media/mmcblk1p1.
[  OK  ] Started Avahi mDNS/DNS-SD Stack.
         Starting thttpd.service...
[  OK  ] Started thttpd.service.
[   17.159025] l4per_cm:clk:00d0:0: failed to disable
[  OK  ] Started Simple Network Management Protocol (SNMP) Daemon..

 _____                    _____           _         _
|  _  |___ ___ ___ ___   |  _  |___ ___  |_|___ ___| |_
|     |  _| .'| . | . |  |   __|  _| . | | | -_|  _|  _|
|__|__|_| |__,|_  |___|  |__|  |_| |___|_| |___|___|_|
              |___|                    |___|

Arago Project http://arago-project.org am57xx-evm ttyS2

Arago 2019.07 am57xx-evm ttyS2

am57xx-evm login: root
root@am57xx-evm:~# cd ..
root@am57xx-evm:/home# cd ..
root@am57xx-evm:/# sudo dmesg | grep ov5640
[    7.372084] ov5640 2-003c: GPIO lookup for consumer powerdown
[    7.372091] ov5640 2-003c: using device tree for GPIO lookup
[    7.372105] of_get_named_gpiod_flags: can't parse 'powerdown-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
[    7.372116] of_get_named_gpiod_flags: can't parse 'powerdown-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
[    7.372122] ov5640 2-003c: using lookup tables for GPIO lookup
[    7.372127] ov5640 2-003c: No GPIO consumer powerdown found
[    7.372133] ov5640 2-003c: GPIO lookup for consumer reset
[    7.372137] ov5640 2-003c: using device tree for GPIO lookup
[    7.372147] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
[    7.372156] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
[    7.372162] ov5640 2-003c: using lookup tables for GPIO lookup
[    7.372167] ov5640 2-003c: No GPIO consumer reset found
[    7.372207] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
[    7.386084] ov5640 2-003c: Linked as a consumer to regulator.0
[    7.391975] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
[    7.400288] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
[    7.408385] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
[    9.021100] vin3a: Port A: Using subdev ov5640 2-003c for capture
[   11.327667] Modules linked in: pru_rproc(+) irq_pruss_intc pruss rpmsg_proto rpmsg_rpc dwc3 udc_core ti_vip snd_soc_simple_card ahci_platform snd_soc_simple_card_utils libahci_platform libahci pvrsrvkm(O) snd_soc_omap_hdmi omap_aes_driver libata pruss_soc_bus ti_vpe omap_wdt omap_sham phy_omap_usb2 ti_sc omap_des ti_csc ov5640 des_generic ti_vpdma snd_soc_tlv320aic3x at24 omap_hdq v4l2_fwnode crypto_engine omap_crypto dwc3_omap wire extcon_palmas rtc_omap rtc_palmas omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
[   11.384130] LR is at ov5640_set_fmt+0x1a0/0x288 [ov5640]
[   11.816640] [<c07c34b0>] (__v4l2_ctrl_s_ctrl_int64) from [<bf1abfa4>] (ov5640_set_fmt+0x1a0/0x288 [ov5640])
[   11.832121] [<bf1abe04>] (ov5640_set_fmt [ov5640]) from [<bf3354d8>] (vip_init_port+0x1c4/0x458 [ti_vip])
root@am57xx-evm:/# sudo dmesg | grep vip
[    8.883613] vip 48990000.vip: loading firmware vpdma-1b8.bin
[    9.015406] vip 48990000.vip: VPDMA firmware loaded
[   11.327667] Modules linked in: pru_rproc(+) irq_pruss_intc pruss rpmsg_proto rpmsg_rpc dwc3 udc_core ti_vip snd_soc_simple_card ahci_platform snd_soc_simple_card_utils libahci_platform libahci pvrsrvkm(O) snd_soc_omap_hdmi omap_aes_driver libata pruss_soc_bus ti_vpe omap_wdt omap_sham phy_omap_usb2 ti_sc omap_des ti_csc ov5640 des_generic ti_vpdma snd_soc_tlv320aic3x at24 omap_hdq v4l2_fwnode crypto_engine omap_crypto dwc3_omap wire extcon_palmas rtc_omap rtc_palmas omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
[   11.832121] [<bf1abe04>] (ov5640_set_fmt [ov5640]) from [<bf3354d8>] (vip_init_port+0x1c4/0x458 [ti_vip])
[   11.849514] [<bf335314>] (vip_init_port [ti_vip]) from [<bf33579c>] (vip_init_stream+0x30/0x1b8 [ti_vip])
[   11.866902] [<bf33576c>] (vip_init_stream [ti_vip]) from [<bf3359a0>] (vip_open+0x7c/0xac [ti_vip])
[   11.881673] [<bf335924>] (vip_open [ti_vip]) from [<c07b2fe0>] (v4l2_open+0x84/0x110)
root@am57xx-evm:/# sudo dmesg | grep vpe
[    7.857185] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
[    7.985577] vpe 489d0000.vpe: Device registered as /dev/video0
[   11.327667] Modules linked in: pru_rproc(+) irq_pruss_intc pruss rpmsg_proto rpmsg_rpc dwc3 udc_core ti_vip snd_soc_simple_card ahci_platform snd_soc_simple_card_utils libahci_platform libahci pvrsrvkm(O) snd_soc_omap_hdmi omap_aes_driver libata pruss_soc_bus ti_vpe omap_wdt omap_sham phy_omap_usb2 ti_sc omap_des ti_csc ov5640 des_generic ti_vpdma snd_soc_tlv320aic3x at24 omap_hdq v4l2_fwnode crypto_engine omap_crypto dwc3_omap wire extcon_palmas rtc_omap rtc_palmas omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
root@am57xx-evm:/# sudo dmesg | grep video
[    0.602968] videodev: Linux video capture interface: v2.00
[    7.985577] vpe 489d0000.vpe: Device registered as /dev/video0
[    9.027849] vin3a-0: device registered as video1
root@am57xx-evm:/# lsmod 0v5640
Usage: lsmod
root@am57xx-evm:/# lsmod
Module                  Size  Used by
usbhid                 24576  0
xhci_plat_hcd          16384  0
xhci_hcd              143360  1 xhci_plat_hcd
pru_rproc              20480  0
irq_pruss_intc         16384  1 pru_rproc
pruss                  16384  1 pru_rproc
rpmsg_proto            16384  0
rpmsg_rpc              32768  0
dwc3                   73728  0
udc_core               28672  1 dwc3
ti_vip                 49152  1
snd_soc_simple_card    16384  0
ahci_platform          16384  0
snd_soc_simple_card_utils    16384  1 snd_soc_simple_card
libahci_platform       20480  1 ahci_platform
libahci                36864  2 ahci_platform,libahci_platform
pvrsrvkm              401408  2
snd_soc_omap_hdmi      16384  0
omap_aes_driver        24576  0
libata                208896  3 ahci_platform,libahci_platform,libahci
pruss_soc_bus          16384  0
ti_vpe                 28672  0
omap_wdt               16384  0
omap_sham              28672  0
phy_omap_usb2          16384  3
ti_sc                  36864  2 ti_vpe,ti_vip
omap_des               20480  0
ti_csc                 16384  2 ti_vpe,ti_vip
ov5640                 28672  1
des_generic            28672  1 omap_des
ti_vpdma               24576  2 ti_vpe,ti_vip
snd_soc_tlv320aic3x    57344  1
at24                   20480  0
omap_hdq               16384  0
v4l2_fwnode            20480  2 ov5640,ti_vip
crypto_engine          16384  2 omap_des,omap_aes_driver
omap_crypto            16384  2 omap_des,omap_aes_driver
dwc3_omap              16384  0
wire                   32768  1 omap_hdq
extcon_palmas          16384  0
rtc_omap               20480  0
rtc_palmas             16384  1
omap_remoteproc        20480  0
virtio_rpmsg_bus       20480  0
remoteproc             49152  4 pru_rproc,rpmsg_rpc,omap_remoteproc,rpmsg_proto
sch_fq_codel           20480  3
uio_module_drv         16384  0
uio                    20480  1 uio_module_drv
ftdi_sio               40960  0
usbserial              36864  1 ftdi_sio
usbcore               217088  5 usbhid,ftdi_sio,usbserial,xhci_plat_hcd,xhci_hcd
usb_common             16384  3 udc_core,usbcore,dwc3
jailhouse              32768  0
gdbserverproxy         16384  0
cryptodev              49152  0
cmemk                  45056  2

I am getting video1 in /dev/video* but i want to change the frame formats and colour space things.

Could you give me suggestions.

Is this right driver inplace of FPGA taken OV5640 or else any other driver i want to take because so many registers are there in ov5640?

In v4l2 capture frame rate, color space, and 16-bit data and 1080p30fps how to read video data where to change the formats?

Thanking you sir,

Regards,

Ramachandra

  • Hi Ramachandra,

    I would suggest referring to the V4L2 userguide, there are few control APIs, like SET_FMT, SET_STD, using which you would change the data format, color space and standard. 

    I think OV5640 driver is good reference, but somehow you need to connect VIP driver to this sensor driver. I think this is done somewhere in the dts file. Can you please check dts files and make changes accordingly? 

    Regards,

    Brijesh

  • Hi sir,

    Based on our fpga data we changes in vip.c file and set_fmt enabled in ov5640.c code but while running failure occurs due to set_fmt function.

    am57xx-evm login: root
    root@am57xx-evm:~# cd ..
    root@am57xx-evm:/home# cd ..
    root@am57xx-evm:/# sudo dmesg | grep ov5640
    [    8.231683] ov5640 2-003c: GPIO lookup for consumer powerdown
    [    8.231690] ov5640 2-003c: using device tree for GPIO lookup
    [    8.231703] of_get_named_gpiod_flags: can't parse 'powerdown-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [    8.231714] of_get_named_gpiod_flags: can't parse 'powerdown-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [    8.231722] ov5640 2-003c: using lookup tables for GPIO lookup
    [    8.231728] ov5640 2-003c: No GPIO consumer powerdown found
    [    8.231733] ov5640 2-003c: GPIO lookup for consumer reset
    [    8.231738] ov5640 2-003c: using device tree for GPIO lookup
    [    8.231748] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [    8.231758] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [    8.231764] ov5640 2-003c: using lookup tables for GPIO lookup
    [    8.231769] ov5640 2-003c: No GPIO consumer reset found
    [    8.231808] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
    [    8.257837] ov5640 2-003c: Linked as a consumer to regulator.0
    [    8.266855] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
    [    8.275554] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
    [    8.282945] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
    [    9.786620] vin3a: Port A: Using subdev ov5640 2-003c for capture
    root@am57xx-evm:/# sudo dmesg | grep vip
    [    9.752495] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [    9.781004] vip 48990000.vip: VPDMA firmware loaded
    root@am57xx-evm:/# sudo dmesg | grep vpe
    [    8.845550] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
    [    8.981149] vpe 489d0000.vpe: Device registered as /dev/video0
    root@am57xx-evm:~# v4l2-ctl -d /dev/video1 --all
    Driver Info:
            Driver name      : vip
            Card type        : vip
            Bus info         : platform:vip2:vin3a:stream0
            Driver version   : 4.19.59
            Capabilities     : 0x85200001
                    Video Capture
                    Read/Write
                    Streaming
                    Extended Pix Format
                    Device Capabilities
            Device Caps      : 0x05200001
                    Video Capture
                    Read/Write
                    Streaming
                    Extended Pix Format
    Priority: 2
    Video input : 0 (camera 1: ok)
    Video Standard = 0x00ffb0ff
            PAL-B/B1/G/H/I/D/D1/K
            NTSC-M/M-JP/M-KR
            SECAM-B/D/G/H/K/K1/L/Lc
    Format Video Capture:
            Width/Height      : 1920/1080
            Pixel Format      : 'UYVY' (UYVY 4:2:2)
            Field             : None
            Bytes per Line    : 3840
            Size Image        : 4147200
            Colorspace        : BT.2020
            Transfer Function : Default (maps to Rec. 709)
            YCbCr/HSV Encoding: Default (maps to BT.2020)
            Quantization      : Default (maps to Limited Range)
            Flags             :
    Crop Capability Video Capture:
            Bounds      : Left 0, Top 0, Width 1920, Height 1080
            Default     : Left 0, Top 0, Width 1920, Height 1080
            Pixel Aspect: 1/1
    Crop Capability Video Output:
            Bounds      : Left 0, Top 0, Width 1920, Height 1080
            Default     : Left 0, Top 0, Width 1920, Height 1080
            Pixel Aspect: 1/1
    Selection Video Capture: crop, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Streaming Parameters Video Capture:
            Capabilities     : timeperframe
            Frames per second: 30.000 (30/1)
            Read buffers     : 4
    root@am57xx-evm:/# /etc/init.d/weston stop
    Stopping Weston
    root@am57xx-evm:/# gst-launch-1.0 -v -e videotestsrc num-buffers=500 ! queue ! 'video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1' ! queue ! ducatih264enc ! queue ! h264parse ! queue ! ducatih264dec ! queue ! kmssink
    Setting pipeline to PAUSED ...
    [   66.122389] omap-iommu 55082000.mmu: 55082000.mmu: version 2.1
    Pipeline is PREROLLING ...
    /GstPipeline:pipeline0/GstKMSSink:kmssink0: display-width = 1440
    /GstPipeline:pipeline0/GstKMSSink:kmssink0: display-height = 900
    /GstPipeline:pipeline0/GstVideoTestSrc:videotestsrc0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstQueue:queue0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstCapsFilter:capsfilter0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstQueue:queue1.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstCapsFilter:capsfilter0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstQueue:queue1.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstDucatiH264Enc:ducatih264enc0.GstPad:src: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono
    /GstPipeline:pipeline0/GstQueue:queue2.GstPad:sink: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono
    /GstPipeline:pipeline0/GstQueue:queue2.GstPad:src: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono
    /GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:src: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, parsed=(boolean)true
    /GstPipeline:pipeline0/GstQueue:queue3.GstPad:sink: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, parsed=(boolean)true
    /GstPipeline:pipeline0/GstQueue:queue3.GstPad:src: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, parsed=(boolean)true
    /GstPipeline:pipeline0/GstDucatiH264Dec:ducatih264dec0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstDucatiH264Dec:ducatih264dec0.GstPad:sink: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, parsed=(boolean)true
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstKMSSink:kmssink0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:sink: caps = video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)22/1, interlace-mode=(string)progressive, colorimetry=(string)bt709, chroma-site=(string)mpeg2, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono
    /GstPipeline:pipeline0/GstDucatiH264Enc:ducatih264enc0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstQueue:queue0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive
    /GstPipeline:pipeline0/GstDucatiH264Enc:ducatih264enc0.GstPad:src: caps = video/x-h264, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4
    /GstPipeline:pipeline0/GstQueue:queue2.GstPad:sink: caps = video/x-h264, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4
    /GstPipeline:pipeline0/GstQueue:queue2.GstPad:src: caps = video/x-h264, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4
    /GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:sink: caps = video/x-h264, width=(int)1920, height=(int)1080, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4
    /GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:src: caps = video/x-h264, width=(int)1920, height=(int)1088, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true, alignment=(string)au
    /GstPipeline:pipeline0/GstQueue:queue3.GstPad:sink: caps = video/x-h264, width=(int)1920, height=(int)1088, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true, alignment=(string)au
    /GstPipeline:pipeline0/GstQueue:queue3.GstPad:src: caps = video/x-h264, width=(int)1920, height=(int)1088, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true, alignment=(string)au
    /GstPipeline:pipeline0/GstDucatiH264Dec:ducatih264dec0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstDucatiH264Dec:ducatih264dec0.GstPad:sink: caps = video/x-h264, width=(int)1920, height=(int)1088, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)byte-stream, align=(string)au, num-reorder-frames=(int)3, profile=(string)high, level=(string)4, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true, alignment=(string)au
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstKMSSink:kmssink0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true
    /GstPipeline:pipeline0/GstDucatiH264Dec:ducatih264dec0.GstPad:src: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true, max-ref-frames=(int)7
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true, max-ref-frames=(int)7
    /GstPipeline:pipeline0/GstQueue:queue4.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true, max-ref-frames=(int)7
    /GstPipeline:pipeline0/GstKMSSink:kmssink0.GstPad:sink: caps = video/x-raw, format=(string)NV12, width=(int)2048, height=(int)1184, framerate=(fraction)22/1, pixel-aspect-ratio=(fraction)1/1, drm_mem=(boolean)true, max-ref-frames=(int)7
    Pipeline is PREROLLED ...
    Setting pipeline to PLAYING ...
    New clock: GstSystemClock
    Got EOS from element "pipeline0".
    Execution ended after 0:00:22.561167624
    Setting pipeline to PAUSED ...
    Setting pipeline to READY ...
    Setting pipeline to NULL ...
    Freeing pipeline ...
    root@am57xx-evm:/# [  129.465891] NET: Registered protocol family 15
    [  129.537767] Initializing XFRM netlink socket
    
    root@am57xx-evm:/# gst-launch-1.0 v4l2src device=/dev/video1 num-buffers=1000 io-mode=4 ! 'video/x-raw, format=(string)YUY2, width=(int)1920, height=(int)1080' ! queue !  vpe ! queue ! kmssink
    Setting pipeline to PAUSED ...
    Pipeline is live and does not need PREROLL ...
    Setting pipeline to PLAYING ...
    New clock: GstSystemClock
    [  140.568580] Unable to handle kernel NULL pointer dereference at virtual address 0000001c
    [  140.581312] pgd = 23acb535
    [  140.584058] [0000001c] *pgd=90cd6003, *pmd=fe189003
    [  140.589017] Internal error: Oops: 207 [#1] PREEMPT SMP ARM
    [  140.594527] Modules linked in: xfrm_user xfrm4_tunnel ipcomp xfrm_ipcomp esp4 ah4 af_key xfrm_algo usbhid xhci_plat_hcd xhci_hcd pru_rproc irq_pruss_intc pruss rpmsg_proto rpmsg_rpc dwc3 udc_core ahci_platform snd_soc_simple_card snd_soc_simple_card_utils libahci_platform ti_vip snd_soc_omap_hdmi libahci pvrsrvkm(O) libata phy_omap_usb2 pruss_soc_bus omap_aes_driver omap_wdt omap_sham ti_vpe ti_sc ti_csc ti_vpdma ov5640 snd_soc_tlv320aic3x omap_des dwc3_omap rtc_omap des_generic at24 omap_hdq v4l2_fwnode extcon_palmas crypto_engine rtc_palmas omap_crypto wire omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
    [  140.659369] CPU: 0 PID: 1173 Comm: v4l2src0:src Tainted: G           O      4.19.59-g5f8c1c6121 #11
    [  140.668453] Hardware name: Generic DRA74X (Flattened Device Tree)
    [  140.674581] PC is at __v4l2_ctrl_s_ctrl_int64+0x10/0x5c
    [  140.679837] LR is at ov5640_set_fmt+0x1b8/0x2d0 [ov5640]
    [  140.685172] pc : [<c07c34c0>]    lr : [<bf18afcc>]    psr: 600f0013
    [  140.691465] sp : eca45c68  ip : eca45c88  fp : eca45c84
    [  140.696711] r10: d4366100  r9 : 00000000  r8 : 0000001e
    [  140.701958] r7 : 00000000  r6 : ec95d044  r5 : 00000000  r4 : 00000000
    [  140.708512] r3 : 00000000  r2 : 0501bd00  r1 : 00000000  r0 : 00000000
    [  140.715069] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
    [  140.722235] Control: 30c5387d  Table: 95230540  DAC: fffffffd
    [  140.728006] Process v4l2src0:src (pid: 1173, stack limit = 0x23ef0e67)
    [  140.734563] Stack: (0xeca45c68 to 0xeca46000)
    [  140.738939] 5c60:                   00000000 00000000 ec95d044 ec95d18c eca45cac eca45c88
    [  140.747154] 5c80: bf18afcc c07c34bc ec936040 ed399f00 ec0cd000 bf303400 c1204c48 00000000
    [  140.755369] 5ca0: eca45d4c eca45cb0 bf2ff14c bf18ae20 00000000 00000000 eca45cf4 eca45cc8
    [  140.763582] 5cc0: 00000001 00000001 00000000 00000780 00000438 00002006 00000001 00000008
    [  140.771798] 5ce0: 00010001 c1250002 00000081 eca45d9c bf2fa4d0 a9166ef0 00000000 00000000
    [  140.780012] 5d00: 00000081 006200ca d333c8f0 00000055 ffffe000 00000000 eca45e04 a9166ef0
    [  140.788227] 5d20: eca45d4c ed399f00 d3240240 bf3018ec ec935c00 d4366100 ec0e4ea4 d4366100
    [  140.796440] 5d40: eca45d74 eca45d50 c07b61a4 bf2fee8c ec935c00 c0cc5605 c1204c48 c07b6080
    [  140.804654] 5d60: 00000003 ec0e4ea4 eca45ddc eca45d78 c07b5a38 c07b608c fe0da003 00000000
    [  140.812869] 5d80: eca45db4 c124188c 00000000 00000000 ed399f00 bf3018ec 006000c0 000000cc
    [  140.821084] 5da0: ffffffff c07b8ed0 000000cc a9166ef0 eca45ddc c0cc5605 c1204c48 00000000
    [  140.829299] 5dc0: 00000003 000000cc ed399f00 00000000 eca45eb4 eca45de0 c07b9034 c07b584c
    [  140.837512] 5de0: 00000040 00000000 00000000 00000000 c07b5840 d3240240 b52feaf0 ed399f00
    [  140.845726] 5e00: c0324130 c0219ba0 00000000 00000000 00000000 00000000 00000000 00000000
    [  140.853940] 5e20: ffefe000 00000888 00000000 dfb7e800 d3248b60 00000054 006200ca 00000055
    [  140.862154] 5e40: b6a83000 ecb61da8 d5230550 00000000 00000000 00000000 00000000 00000000
    [  140.870367] 5e60: 00000000 00000000 d33a6a38 00000000 d339a880 a9166ef0 eca45ea4 eca45fb0
    [  140.878580] 5e80: d339a880 a9166ef0 b6a83d30 c07b92a4 b52feaf0 d3240240 00000009 b52feaf0
    [  140.886793] 5ea0: eca44000 ec833620 eca45ec4 eca45eb8 c07b92bc c07b8de0 eca45edc eca45ec8
    [  140.895006] 5ec0: c07b2b20 c07b92b0 c1204c48 b52feaf0 eca45f6c eca45ee0 c0368074 c07b2ae8
    [  140.903221] 5ee0: b6af4000 b41085b8 eca45fac eca45ef8 c0215168 c0214c60 eea46768 c1223c84
    [  140.911436] 5f00: eca45f1c eca45f10 c028baf0 c0551770 eca45f2c eca45f20 d3240240 00000009
    [  140.919652] 5f20: eca45f4c eca45f30 c037343c c0299584 b52feaf0 00045cd0 00000009 a9166ef0
    [  140.927865] 5f40: eca45f5c d3240241 d3240240 00000009 c0cc5605 b52feaf0 eca44000 00000036
    [  140.936080] 5f60: eca45f94 eca45f70 c03687b8 c0367fc8 b52feaf0 00045cd0 00000000 00000036
    [  140.944293] 5f80: c0201204 eca44000 eca45fa4 eca45f98 c03687f4 c0368788 00000000 eca45fa8
    [  140.952506] 5fa0: c0201000 c03687f0 b52feaf0 00045cd0 00000009 c0cc5605 b52feaf0 b6d03150
    [  140.960720] 5fc0: b52feaf0 00045cd0 00000000 00000036 00000001 b6af4000 b6fac638 b52fea80
    [  140.968934] 5fe0: 00000000 b52fe9cc b6ac10b1 b6d0315c 600f0010 00000009 00000000 00000000
    [  140.977143] Backtrace:
    [  140.979614] [<c07c34b0>] (__v4l2_ctrl_s_ctrl_int64) from [<bf18afcc>] (ov5640_set_fmt+0x1b8/0x2d0 [ov5640])
    [  140.989398]  r7:ec95d18c r6:ec95d044 r5:00000000 r4:00000000
    [  140.995103] [<bf18ae14>] (ov5640_set_fmt [ov5640]) from [<bf2ff14c>] (vip_s_fmt_vid_cap+0x2cc/0x494 [ti_vip])
    [  141.005063]  r9:00000000 r8:c1204c48 r7:bf303400 r6:ec0cd000 r5:ed399f00 r4:ec936040
    [  141.012855] [<bf2fee80>] (vip_s_fmt_vid_cap [ti_vip]) from [<c07b61a4>] (v4l_s_fmt+0x124/0x3bc)
    [  141.021595]  r10:d4366100 r9:ec0e4ea4 r8:d4366100 r7:ec935c00 r6:bf3018ec r5:d3240240
    [  141.029457]  r4:ed399f00
    [  141.032007] [<c07b6080>] (v4l_s_fmt) from [<c07b5a38>] (__video_do_ioctl+0x1f8/0x4e4)
    [  141.039873]  r9:ec0e4ea4 r8:00000003 r7:c07b6080 r6:c1204c48 r5:c0cc5605 r4:ec935c00
    [  141.047656] [<c07b5840>] (__video_do_ioctl) from [<c07b9034>] (video_usercopy+0x260/0x4d0)
    [  141.055959]  r10:00000000 r9:ed399f00 r8:000000cc r7:00000003 r6:00000000 r5:c1204c48
    [  141.063820]  r4:c0cc5605
    [  141.066369] [<c07b8dd4>] (video_usercopy) from [<c07b92bc>] (video_ioctl2+0x18/0x1c)
    [  141.074149]  r10:ec833620 r9:eca44000 r8:b52feaf0 r7:00000009 r6:d3240240 r5:b52feaf0
    [  141.082011]  r4:c07b92a4
    [  141.084559] [<c07b92a4>] (video_ioctl2) from [<c07b2b20>] (v4l2_ioctl+0x44/0x58)
    [  141.091991] [<c07b2adc>] (v4l2_ioctl) from [<c0368074>] (do_vfs_ioctl+0xb8/0x7c0)
    [  141.099505]  r5:b52feaf0 r4:c1204c48
    [  141.103097] [<c0367fbc>] (do_vfs_ioctl) from [<c03687b8>] (ksys_ioctl+0x3c/0x68)
    [  141.110526]  r10:00000036 r9:eca44000 r8:b52feaf0 r7:c0cc5605 r6:00000009 r5:d3240240
    [  141.118389]  r4:d3240241
    [  141.120934] [<c036877c>] (ksys_ioctl) from [<c03687f4>] (sys_ioctl+0x10/0x14)
    [  141.128103]  r9:eca44000 r8:c0201204 r7:00000036 r6:00000000 r5:00045cd0 r4:b52feaf0
    [  141.135883] [<c03687e4>] (sys_ioctl) from [<c0201000>] (ret_fast_syscall+0x0/0x4c)
    [  141.143484] Exception stack(0xeca45fa8 to 0xeca45ff0)
    [  141.148559] 5fa0:                   b52feaf0 00045cd0 00000009 c0cc5605 b52feaf0 b6d03150
    [  141.156773] 5fc0: b52feaf0 00045cd0 00000000 00000036 00000001 b6af4000 b6fac638 b52fea80
    [  141.164986] 5fe0: 00000000 b52fe9cc b6ac10b1 b6d0315c
    [  141.170062] Code: e1a0c00d e92dd8f0 e24cb004 e1a07003 (e1d031dc)
    [  141.176708] ---[ end trace e7d073923877b0ca ]---
    
    ^Chandling interrupt.
    Interrupt: Stopping pipeline ...
    Execution ended after 0:00:04.220782585
    Setting pipeline to PAUSED ...
    Setting pipeline to READY ...
    
    ^X^C
    root@am57xx-evm:/#

    In __v4l2_ctrl_s_ctrl_int64 it's hanging could you give me some suggestion how to change set_fmt code

    i am attaching my vip.c and ov5640.c codes.

    /*
     * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
     * Copyright (C) 2014-2017 Mentor Graphics Inc.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     */
    
    #include <linux/clk.h>
    #include <linux/clk-provider.h>
    #include <linux/clkdev.h>
    #include <linux/ctype.h>
    #include <linux/delay.h>
    #include <linux/device.h>
    #include <linux/gpio/consumer.h>
    #include <linux/i2c.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/of_device.h>
    #include <linux/regulator/consumer.h>
    #include <linux/slab.h>
    #include <linux/types.h>
    #include <media/v4l2-async.h>
    #include <media/v4l2-ctrls.h>
    #include <media/v4l2-device.h>
    #include <media/v4l2-event.h>
    #include <media/v4l2-fwnode.h>
    #include <media/v4l2-subdev.h>
    
    /* min/typical/max system clock (xclk) frequencies */
    #define OV5640_XCLK_MIN  6000000
    #define OV5640_XCLK_MAX 74250000
    
    #define OV5640_DEFAULT_SLAVE_ID 0x3c
    
    #define OV5640_REG_SYS_CARAVEL		0x3000
    #define OV5640_REG_SYS_RESET02		0x3002
    #define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
    #define OV5640_REG_SYS_CTRL0		0x3008
    #define OV5640_REG_CHIP_ID		0x300a
    #define OV5640_REG_IO_MIPI_CTRL00	0x300e
    #define OV5640_REG_PAD_OUTPUT_ENABLE01	0x3017
    #define OV5640_REG_PAD_OUTPUT_ENABLE02	0x3018
    #define OV5640_REG_PAD_OUTPUT00		0x3019
    #define OV5640_REG_SYSTEM_CONTROL1	0x302e
    #define OV5640_REG_SC_PLL_CTRL0		0x3034
    #define OV5640_REG_SC_PLL_CTRL1		0x3035
    #define OV5640_REG_SC_PLL_CTRL2		0x3036
    #define OV5640_REG_SC_PLL_CTRL3		0x3037
    #define OV5640_REG_SLAVE_ID		0x3100
    #define OV5640_REG_SCCB_SYS_CTRL1	0x3103
    #define OV5640_REG_SYS_ROOT_DIVIDER	0x3108
    #define OV5640_REG_AWB_R_GAIN		0x3400
    #define OV5640_REG_AWB_G_GAIN		0x3402
    #define OV5640_REG_AWB_B_GAIN		0x3404
    #define OV5640_REG_AWB_MANUAL_CTRL	0x3406
    #define OV5640_REG_AEC_PK_EXPOSURE_HI	0x3500
    #define OV5640_REG_AEC_PK_EXPOSURE_MED	0x3501
    #define OV5640_REG_AEC_PK_EXPOSURE_LO	0x3502
    #define OV5640_REG_AEC_PK_MANUAL	0x3503
    #define OV5640_REG_AEC_PK_REAL_GAIN	0x350a
    #define OV5640_REG_AEC_PK_VTS		0x350c
    #define OV5640_REG_TIMING_DVPHO		0x3808
    #define OV5640_REG_TIMING_DVPVO		0x380a
    #define OV5640_REG_TIMING_HTS		0x380c
    #define OV5640_REG_TIMING_VTS		0x380e
    #define OV5640_REG_TIMING_TC_REG20	0x3820
    #define OV5640_REG_TIMING_TC_REG21	0x3821
    #define OV5640_REG_AEC_CTRL00		0x3a00
    #define OV5640_REG_AEC_B50_STEP		0x3a08
    #define OV5640_REG_AEC_B60_STEP		0x3a0a
    #define OV5640_REG_AEC_CTRL0D		0x3a0d
    #define OV5640_REG_AEC_CTRL0E		0x3a0e
    #define OV5640_REG_AEC_CTRL0F		0x3a0f
    #define OV5640_REG_AEC_CTRL10		0x3a10
    #define OV5640_REG_AEC_CTRL11		0x3a11
    #define OV5640_REG_AEC_CTRL1B		0x3a1b
    #define OV5640_REG_AEC_CTRL1E		0x3a1e
    #define OV5640_REG_AEC_CTRL1F		0x3a1f
    #define OV5640_REG_HZ5060_CTRL00	0x3c00
    #define OV5640_REG_HZ5060_CTRL01	0x3c01
    #define OV5640_REG_SIGMADELTA_CTRL0C	0x3c0c
    #define OV5640_REG_FRAME_CTRL01		0x4202
    #define OV5640_REG_FORMAT_CONTROL00	0x4300
    #define OV5640_REG_POLARITY_CTRL00	0x4740
    #define OV5640_REG_MIPI_CTRL00		0x4800
    #define OV5640_REG_DEBUG_MODE		0x4814
    #define OV5640_REG_ISP_FORMAT_MUX_CTRL	0x501f
    #define OV5640_REG_PRE_ISP_TEST_SET1	0x503d
    #define OV5640_REG_SDE_CTRL0		0x5580
    #define OV5640_REG_SDE_CTRL1		0x5581
    #define OV5640_REG_SDE_CTRL3		0x5583
    #define OV5640_REG_SDE_CTRL4		0x5584
    #define OV5640_REG_SDE_CTRL5		0x5585
    #define OV5640_REG_AVG_READOUT		0x56a1
    
    enum ov5640_mode_id {
    	OV5640_MODE_QCIF_176_144 = 0,
    	OV5640_MODE_QVGA_320_240,
    	OV5640_MODE_VGA_640_480,
    	OV5640_MODE_NTSC_720_480,
    	OV5640_MODE_PAL_720_576,
    	OV5640_MODE_XGA_1024_768,
    	OV5640_MODE_720P_1280_720,
    	OV5640_MODE_1080P_1920_1080,
    	OV5640_MODE_QSXGA_2592_1944,
    	OV5640_NUM_MODES,
    };
    
    enum ov5640_frame_rate {
    	OV5640_15_FPS = 0,
    	OV5640_30_FPS,
    	OV5640_60_FPS,
    	OV5640_NUM_FRAMERATES,
    };
    
    struct ov5640_pixfmt {
    	u32 code;
    	u32 colorspace;
    };
    
    static const struct ov5640_pixfmt ov5640_formats[] = {
    	{ MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
    	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
    	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
    	{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
    	{ MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
    	{ MEDIA_BUS_FMT_SBGGR16_1X16, V4L2_COLORSPACE_SMPTE170M },
    	{ MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_BT2020 },
    };
    
    /*
     * FIXME: remove this when a subdev API becomes available
     * to set the MIPI CSI-2 virtual channel.
     */
    static unsigned int virtual_channel;
    module_param(virtual_channel, uint, 0444);
    MODULE_PARM_DESC(virtual_channel,
    		 "MIPI CSI-2 virtual channel (0..3), default 0");
    
    static const int ov5640_framerates[] = {
    	[OV5640_15_FPS] = 15,
    	[OV5640_30_FPS] = 30,
    	[OV5640_60_FPS] = 60,
    };
    
    /* regulator supplies */
    static const char * const ov5640_supply_name[] = {
    	"DOVDD", /* Digital I/O (1.8V) supply */
    	"DVDD",  /* Digital Core (1.5V) supply */
    	"AVDD",  /* Analog (2.8V) supply */
    };
    
    #define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
    
    /*
     * Image size under 1280 * 960 are SUBSAMPLING
     * Image size upper 1280 * 960 are SCALING
     */
    enum ov5640_downsize_mode {
    	SUBSAMPLING,
    	SCALING,
    };
    
    struct reg_value {
    	u16 reg_addr;
    	u8 val;
    	u8 mask;
    	u32 delay_ms;
    };
    
    struct ov5640_mode_info {
    	enum ov5640_mode_id id;
    	enum ov5640_downsize_mode dn_mode;
    	u32 hact;
    	u32 htot;
    	u32 vact;
    	u32 vtot;
    	const struct reg_value *reg_data;
    	u32 reg_data_size;
    };
    
    struct ov5640_ctrls {
    	struct v4l2_ctrl_handler handler;
    	struct {
    		struct v4l2_ctrl *pixel_rate;
    	};
    	struct {
    		struct v4l2_ctrl *auto_exp;
    		struct v4l2_ctrl *exposure;
    	};
    	struct {
    		struct v4l2_ctrl *auto_wb;
    		struct v4l2_ctrl *blue_balance;
    		struct v4l2_ctrl *red_balance;
    	};
    	struct {
    		struct v4l2_ctrl *auto_gain;
    		struct v4l2_ctrl *gain;
    	};
    	struct v4l2_ctrl *brightness;
    	struct v4l2_ctrl *light_freq;
    	struct v4l2_ctrl *saturation;
    	struct v4l2_ctrl *contrast;
    	struct v4l2_ctrl *hue;
    	struct v4l2_ctrl *test_pattern;
    	struct v4l2_ctrl *hflip;
    	struct v4l2_ctrl *vflip;
    };
    
    struct ov5640_dev {
    	struct i2c_client *i2c_client;
    	struct v4l2_subdev sd;
    	struct media_pad pad;
    	struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
    	struct clk *xclk; /* system clock to OV5640 */
    	u32 xclk_freq;
    
    	struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
    	struct gpio_desc *reset_gpio;
    	struct gpio_desc *pwdn_gpio;
    	bool   upside_down;
    
    	/* lock to protect all members below */
    	struct mutex lock;
    
    	int power_count;
    
    	struct v4l2_mbus_framefmt fmt;
    	bool pending_fmt_change;
    
    	const struct ov5640_mode_info *current_mode;
    	const struct ov5640_mode_info *last_mode;
    	enum ov5640_frame_rate current_fr;
    	struct v4l2_fract frame_interval;
    	u64 pixel_rate;
    
    	struct ov5640_ctrls ctrls;
    
    	u32 prev_sysclk, prev_hts;
    	u32 ae_low, ae_high, ae_target;
    
    	bool pending_mode_change;
    	bool streaming;
    };
    
    static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
    {
    	return container_of(sd, struct ov5640_dev, sd);
    }
    
    static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
    {
    	return &container_of(ctrl->handler, struct ov5640_dev,
    			     ctrls.handler)->sd;
    }
    
    /*
     * FIXME: all of these register tables are likely filled with
     * entries that set the register to their power-on default values,
     * and which are otherwise not touched by this driver. Those entries
     * should be identified and removed to speed register load time
     * over i2c.
     */
    /* YUV422 UYVY VGA@30fps */
    static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
    	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
    	{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
    	{0x3630, 0x36, 0, 0},
    	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
    	{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
    	{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
    	{0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
    	{0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
    	{0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
    	{0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
    	{0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
    	{0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
    	{0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
    	{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
    	{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
    	{0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
    	{0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
    	{0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0},
    	{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
    	{0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
    	{0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
    	{0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
    	{0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
    	{0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
    	{0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
    	{0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
    	{0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
    	{0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
    	{0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
    	{0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
    	{0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
    	{0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
    	{0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
    	{0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
    	{0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
    	{0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
    	{0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
    	{0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
    	{0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
    	{0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
    	{0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
    	{0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
    	{0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
    	{0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
    	{0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
    	{0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
    	{0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
    	{0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
    	{0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
    	{0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
    	{0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
    	{0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
    	{0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
    	{0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
    	{0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
    	{0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
    	{0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
    	{0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
    	{0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
    	{0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
    	{0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
    	{0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
    	{0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
    	{0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
    	{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
    	{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
    	{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
    	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
    };
    
    static const struct reg_value ov5640_setting_VGA_640_480[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_XGA_1024_768[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_QVGA_320_240[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_QCIF_176_144[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_NTSC_720_480[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_PAL_720_576[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_720P_1280_720[] = {
    	{0x3c07, 0x07, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x31, 0, 0},
    	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
    	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
    	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
    	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
    	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
    	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x11, 0, 0},
    	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
    	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
    	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
    	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
    	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
    	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
    	{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
    	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
    	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
    	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
    	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
    	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
    	{0x4005, 0x1a, 0, 0},
    };
    
    static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
    	{0x3c07, 0x08, 0, 0},
    	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
    	{0x3814, 0x11, 0, 0},
    	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
    	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
    	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
    	{0x3810, 0x00, 0, 0},
    	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
    	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
    	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
    	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
    	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
    	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
    	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
    	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
    	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
    };
    
    /* power-on sensor init reg table */
    static const struct ov5640_mode_info ov5640_mode_init_data = {
    	0, SUBSAMPLING, 640, 1896, 480, 984,
    	ov5640_init_setting_30fps_VGA,
    	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
    };
    
    static const struct ov5640_mode_info
    ov5640_mode_data[OV5640_NUM_MODES] = {
    	{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
    	 176, 1896, 144, 984,
    	 ov5640_setting_QCIF_176_144,
    	 ARRAY_SIZE(ov5640_setting_QCIF_176_144)},
    	{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
    	 320, 1896, 240, 984,
    	 ov5640_setting_QVGA_320_240,
    	 ARRAY_SIZE(ov5640_setting_QVGA_320_240)},
    	{OV5640_MODE_VGA_640_480, SUBSAMPLING,
    	 640, 1896, 480, 1080,
    	 ov5640_setting_VGA_640_480,
    	 ARRAY_SIZE(ov5640_setting_VGA_640_480)},
    	{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
    	 720, 1896, 480, 984,
    	 ov5640_setting_NTSC_720_480,
    	 ARRAY_SIZE(ov5640_setting_NTSC_720_480)},
    	{OV5640_MODE_PAL_720_576, SUBSAMPLING,
    	 720, 1896, 576, 984,
    	 ov5640_setting_PAL_720_576,
    	 ARRAY_SIZE(ov5640_setting_PAL_720_576)},
    	{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
    	 1024, 1896, 768, 1080,
    	 ov5640_setting_XGA_1024_768,
    	 ARRAY_SIZE(ov5640_setting_XGA_1024_768)},
    	{OV5640_MODE_720P_1280_720, SUBSAMPLING,
    	 1280, 1892, 720, 740,
    	 ov5640_setting_720P_1280_720,
    	 ARRAY_SIZE(ov5640_setting_720P_1280_720)},
    	{OV5640_MODE_1080P_1920_1080, SCALING,
    	 1920, 2500, 1080, 1120,
    	 ov5640_setting_1080P_1920_1080,
    	 ARRAY_SIZE(ov5640_setting_1080P_1920_1080)},
    	{OV5640_MODE_QSXGA_2592_1944, SCALING,
    	 2592, 2844, 1944, 1968,
    	 ov5640_setting_QSXGA_2592_1944,
    	 ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)},
    };
    
    static int ov5640_init_slave_id(struct ov5640_dev *sensor)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	struct i2c_msg msg;
    	u8 buf[3];
    	int ret;
    
    	dev_err(&client->dev, "%s: client->addr= %x\n", __func__,client->addr );
    
    	if (client->addr == OV5640_DEFAULT_SLAVE_ID)
    		return 0;
    
    	buf[0] = OV5640_REG_SLAVE_ID >> 8;
    	buf[1] = OV5640_REG_SLAVE_ID & 0xff;
    	buf[2] = client->addr << 1;
    
    	msg.addr = OV5640_DEFAULT_SLAVE_ID;
    	msg.flags = 0;
    	msg.buf = buf;
    	msg.len = sizeof(buf);
    
    	ret = i2c_transfer(client->adapter, &msg, 1);
    	if (ret < 0) {
    		dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
    		return ret;
    	}
    	
    	return 0;
    }
    
    static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	struct i2c_msg msg;
    	u8 buf[3];
    	int ret;
    
    	buf[0] = reg >> 8;
    	buf[1] = reg & 0xff;
    	buf[2] = val;
    
    	msg.addr = client->addr;
    	msg.flags = client->flags;
    	msg.buf = buf;
    	msg.len = sizeof(buf);
    
    	ret = i2c_transfer(client->adapter, &msg, 1);
    	if (ret < 0) {
    		dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
    			__func__, reg, val);
    		return ret;
    	}
    
    	return 0;
    }
    static int ov5640_cispl_read_reg(struct ov5640_dev *sensor, u16 reg, u16 *val)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	struct i2c_msg msg[1];
    	u8 buf[2];
    	int ret;
    
    	buf[0] = reg & 0xff;
    	
    	msg[0].addr = client->addr;
    	msg[0].flags = client->flags | I2C_M_RD;
    	msg[0].buf = buf;
    	msg[0].len = 2;
    
    	ret = i2c_transfer(client->adapter, msg, 1);
    	if (ret < 0) {
    		dev_err(&client->dev, "%s: error: reg=%x\n",
    			__func__, reg);
    		return ret;
    	}
    
    	*val = buf[0]<<8 | buf[1];;
    //	*val = buf[0];
    	return 0;
    }
    static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	struct i2c_msg msg[2];
    	u8 buf[2];
    	int ret;
    
    	buf[0] = reg >> 8;
    	buf[1] = reg & 0xff;
    
    	msg[0].addr = client->addr;
    	msg[0].flags = client->flags;
    	msg[0].buf = buf;
    	msg[0].len = sizeof(buf);
    
    	msg[1].addr = client->addr;
    	msg[1].flags = client->flags | I2C_M_RD;
    	msg[1].buf = buf;
    	msg[1].len = 1;
    
    	ret = i2c_transfer(client->adapter, msg, 2);
    	if (ret < 0) {
    		dev_err(&client->dev, "%s: error: reg=%x\n",
    			__func__, reg);
    		return ret;
    	}
    
    	*val = buf[0];
    	return 0;
    }
    
    static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
    {
    	u8 hi, lo;
    	int ret;
    
    	ret = ov5640_read_reg(sensor, reg, &hi);
    	if (ret)
    		return ret;
    	ret = ov5640_read_reg(sensor, reg + 1, &lo);
    	if (ret)
    		return ret;
    
    	*val = ((u16)hi << 8) | (u16)lo;
    	return 0;
    }
    
    static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
    {
    	int ret;
    
    	ret = ov5640_write_reg(sensor, reg, val >> 8);
    	if (ret)
    		return ret;
    
    	return ov5640_write_reg(sensor, reg + 1, val & 0xff);
    }
    
    static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
    			  u8 mask, u8 val)
    {
    	u8 readval;
    	int ret;
    
    	ret = ov5640_read_reg(sensor, reg, &readval);
    	if (ret)
    		return ret;
    
    	readval &= ~mask;
    	val &= mask;
    	val |= readval;
    
    	return ov5640_write_reg(sensor, reg, val);
    }
    
    /*
     * After trying the various combinations, reading various
     * documentations spreaded around the net, and from the various
     * feedback, the clock tree is probably as follows:
     *
     *   +--------------+
     *   |  Ext. Clock  |
     *   +-+------------+
     *     |  +----------+
     *     +->|   PLL1   | - reg 0x3036, for the multiplier
     *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
     *          |  +--------------+
     *          +->| System Clock |  - reg 0x3035, bits 4-7
     *             +-+------------+
     *               |  +--------------+
     *               +->| MIPI Divider | - reg 0x3035, bits 0-3
     *               |  +-+------------+
     *               |    +----------------> MIPI SCLK
     *               |    +  +-----+
     *               |    +->| / 2 |-------> MIPI BIT CLK
     *               |       +-----+
     *               |  +--------------+
     *               +->| PLL Root Div | - reg 0x3037, bit 4
     *                  +-+------------+
     *                    |  +---------+
     *                    +->| Bit Div | - reg 0x3035, bits 0-3
     *                       +-+-------+
     *                         |  +-------------+
     *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
     *                         |  +-+-----------+
     *                         |    +---------------> SCLK
     *                         |  +-------------+
     *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
     *                         |  +-+-----------+
     *                         |    +---------------> SCLK 2X
     *                         |  +-------------+
     *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
     *                            ++------------+
     *                             +  +-----------+
     *                             +->|   P_DIV   | - reg 0x3035, bits 0-3
     *                                +-----+-----+
     *                                       +------------> PCLK
     *
     * This is deviating from the datasheet at least for the register
     * 0x3108, since it's said here that the PCLK would be clocked from
     * the PLL.
     *
     * There seems to be also (unverified) constraints:
     *  - the PLL pre-divider output rate should be in the 4-27MHz range
     *  - the PLL multiplier output rate should be in the 500-1000MHz range
     *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
     *
     * In the two latter cases, these constraints are met since our
     * factors are hardcoded. If we were to change that, we would need to
     * take this into account. The only varying parts are the PLL
     * multiplier and the system clock divider, which are shared between
     * all these clocks so won't cause any issue.
     */
    
    /*
     * This is supposed to be ranging from 1 to 8, but the value is always
     * set to 3 in the vendor kernels.
     */
    #define OV5640_PLL_PREDIV	3
    
    #define OV5640_PLL_MULT_MIN	4
    #define OV5640_PLL_MULT_MAX	252
    
    /*
     * This is supposed to be ranging from 1 to 16, but the value is
     * always set to either 1 or 2 in the vendor kernels.
     */
    #define OV5640_SYSDIV_MIN	1
    #define OV5640_SYSDIV_MAX	16
    
    /*
     * Hardcode these values for scaler and non-scaler modes.
     * FIXME: to be re-calcualted for 1 data lanes setups
     */
    #define OV5640_MIPI_DIV_PCLK	2
    #define OV5640_MIPI_DIV_SCLK	1
    
    /*
     * This is supposed to be ranging from 1 to 2, but the value is always
     * set to 2 in the vendor kernels.
     */
    #define OV5640_PLL_ROOT_DIV			2
    #define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2		BIT(4)
    
    /*
     * We only supports 8-bit formats at the moment
     */
    #define OV5640_BIT_DIV				2
    #define OV5640_PLL_CTRL0_MIPI_MODE_8BIT		0x08
    
    /*
     * This is supposed to be ranging from 1 to 8, but the value is always
     * set to 2 in the vendor kernels.
     */
    #define OV5640_SCLK_ROOT_DIV	2
    
    /*
     * This is hardcoded so that the consistency is maintained between SCLK and
     * SCLK 2x.
     */
    #define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
    
    /*
     * This is supposed to be ranging from 1 to 8, but the value is always
     * set to 1 in the vendor kernels.
     */
    #define OV5640_PCLK_ROOT_DIV			1
    #define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS	0x00
    
    static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
    					    u8 pll_prediv, u8 pll_mult,
    					    u8 sysdiv)
    {
    	unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult;
    
    	/* PLL1 output cannot exceed 1GHz. */
    	if (sysclk / 1000000 > 1000)
    		return 0;
    
    	return sysclk / sysdiv;
    }
    
    static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
    					 unsigned long rate,
    					 u8 *pll_prediv, u8 *pll_mult,
    					 u8 *sysdiv)
    {
    	unsigned long best = ~0;
    	u8 best_sysdiv = 1, best_mult = 1;
    	u8 _sysdiv, _pll_mult;
    
    	for (_sysdiv = OV5640_SYSDIV_MIN;
    	     _sysdiv <= OV5640_SYSDIV_MAX;
    	     _sysdiv++) {
    		for (_pll_mult = OV5640_PLL_MULT_MIN;
    		     _pll_mult <= OV5640_PLL_MULT_MAX;
    		     _pll_mult++) {
    			unsigned long _rate;
    
    			/*
    			 * The PLL multiplier cannot be odd if above
    			 * 127.
    			 */
    			if (_pll_mult > 127 && (_pll_mult % 2))
    				continue;
    
    			_rate = ov5640_compute_sys_clk(sensor,
    						       OV5640_PLL_PREDIV,
    						       _pll_mult, _sysdiv);
    
    			/*
    			 * We have reached the maximum allowed PLL1 output,
    			 * increase sysdiv.
    			 */
    			if (!rate)
    				break;
    
    			/*
    			 * Prefer rates above the expected clock rate than
    			 * below, even if that means being less precise.
    			 */
    			if (_rate < rate)
    				continue;
    
    			if (abs(rate - _rate) < abs(rate - best)) {
    				best = _rate;
    				best_sysdiv = _sysdiv;
    				best_mult = _pll_mult;
    			}
    
    			if (_rate == rate)
    				goto out;
    		}
    	}
    
    out:
    	*sysdiv = best_sysdiv;
    	*pll_prediv = OV5640_PLL_PREDIV;
    	*pll_mult = best_mult;
    
    	return best;
    }
    
    /*
     * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
     *			    for the MIPI CSI-2 output.
     *
     * @rate: The requested bandwidth per lane in bytes per second.
     *	  'Bandwidth Per Lane' is calculated as:
     *	  bpl = HTOT * VTOT * FPS * bpp / num_lanes;
     *
     * This function use the requested bandwidth to calculate:
     * - sample_rate = bpl / (bpp / num_lanes);
     *	         = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
     *
     * - mipi_sclk   = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
     *
     * with these fixed parameters:
     *	PLL_RDIV	= 2;
     *	BIT_DIVIDER	= 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
     *	PCLK_DIV	= 1;
     *
     * The MIPI clock generation differs for modes that use the scaler and modes
     * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
     * BIT CLk, and thus:
     *
     * - mipi_sclk = bpl / MIPI_DIV / 2;
     *   MIPI_DIV = 1;
     *
     * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
     * from the pixel clock, and thus:
     *
     * - sample_rate = bpl / (bpp / num_lanes);
     *	         = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
     *		 = bpl / (4 * MIPI_DIV / num_lanes);
     * - MIPI_DIV	 = bpp / (4 * num_lanes);
     *
     * FIXME: this have been tested with 16bpp and 2 lanes setup only.
     * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
     * above formula for setups with 1 lane or image formats with different bpp.
     *
     * FIXME: this deviates from the sensor manual documentation which is quite
     * thin on the MIPI clock tree generation part.
     */
    static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
    				unsigned long rate)
    {
    	const struct ov5640_mode_info *mode = sensor->current_mode;
    	u8 prediv, mult, sysdiv;
    	u8 mipi_div;
    	int ret;
    
    	/*
    	 * 1280x720 is reported to use 'SUBSAMPLING' only,
    	 * but according to the sensor manual it goes through the
    	 * scaler before subsampling.
    	 */
    	if (mode->dn_mode == SCALING ||
    	   (mode->id == OV5640_MODE_720P_1280_720))
    		mipi_div = OV5640_MIPI_DIV_SCLK;
    	else
    		mipi_div = OV5640_MIPI_DIV_PCLK;
    
    	ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
    			     0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
    			     0xff, sysdiv << 4 | mipi_div);
    	if (ret)
    		return ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
    	if (ret)
    		return ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
    			     0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
    	if (ret)
    		return ret;
    
    	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
    			      0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
    }
    
    static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
    				      unsigned long rate,
    				      u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
    				      u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
    {
    	unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
    				OV5640_PCLK_ROOT_DIV;
    
    	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
    				    sysdiv);
    	*pll_rdiv = OV5640_PLL_ROOT_DIV;
    	*bit_div = OV5640_BIT_DIV;
    	*pclk_div = OV5640_PCLK_ROOT_DIV;
    
    	return _rate / *pll_rdiv / *bit_div / *pclk_div;
    }
    
    static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
    {
    	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
    	int ret;
    
    	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
    			 &bit_div, &pclk_div);
    
    	if (bit_div == 2)
    		bit_div = 8;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
    			     0x0f, bit_div);
    	if (ret)
    		return ret;
    
    	/*
    	 * We need to set sysdiv according to the clock, and to clear
    	 * the MIPI divider.
    	 */
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
    			     0xff, sysdiv << 4);
    	if (ret)
    		return ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
    			     0xff, mult);
    	if (ret)
    		return ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
    			     0x1f, prediv | ((pll_rdiv - 1) << 4));
    	if (ret)
    		return ret;
    
    	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
    			      (ilog2(pclk_div) << 4));
    }
    
    /* download ov5640 settings to sensor through i2c */
    static int ov5640_set_timings(struct ov5640_dev *sensor,
    			      const struct ov5640_mode_info *mode)
    {
    	int ret;
    
    	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
    	if (ret < 0)
    		return ret;
    
    	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
    	if (ret < 0)
    		return ret;
    
    	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
    	if (ret < 0)
    		return ret;
    
    	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
    }
    
    static int ov5640_load_regs(struct ov5640_dev *sensor,
    			    const struct ov5640_mode_info *mode)
    {
    	const struct reg_value *regs = mode->reg_data;
    	unsigned int i;
    	u32 delay_ms;
    	u16 reg_addr;
    	u8 mask, val;
    	int ret = 0;
    
    	for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
    		delay_ms = regs->delay_ms;
    		reg_addr = regs->reg_addr;
    		val = regs->val;
    		mask = regs->mask;
    
    		if (mask)
    			ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
    		else
    			ret = ov5640_write_reg(sensor, reg_addr, val);
    		if (ret)
    			break;
    
    		if (delay_ms)
    			usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
    	}
    
    	return ov5640_set_timings(sensor, mode);
    }
    
    static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
    {
    	return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
    			      BIT(0), on ? 0 : BIT(0));
    }
    
    /* read exposure, in number of line periods */
    static int ov5640_get_exposure(struct ov5640_dev *sensor)
    {
    	int exp, ret;
    	u8 temp;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
    	if (ret)
    		return ret;
    	exp = ((int)temp & 0x0f) << 16;
    	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
    	if (ret)
    		return ret;
    	exp |= ((int)temp << 8);
    	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
    	if (ret)
    		return ret;
    	exp |= (int)temp;
    
    	return exp >> 4;
    }
    
    /* write exposure, given number of line periods */
    static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
    {
    	int ret;
    
    	exposure <<= 4;
    
    	ret = ov5640_write_reg(sensor,
    			       OV5640_REG_AEC_PK_EXPOSURE_LO,
    			       exposure & 0xff);
    	if (ret)
    		return ret;
    	ret = ov5640_write_reg(sensor,
    			       OV5640_REG_AEC_PK_EXPOSURE_MED,
    			       (exposure >> 8) & 0xff);
    	if (ret)
    		return ret;
    	return ov5640_write_reg(sensor,
    				OV5640_REG_AEC_PK_EXPOSURE_HI,
    				(exposure >> 16) & 0x0f);
    }
    
    static int ov5640_get_gain(struct ov5640_dev *sensor)
    {
    	u16 gain;
    	int ret;
    
    	ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
    	if (ret)
    		return ret;
    
    	return gain & 0x3ff;
    }
    
    static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
    {
    	return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
    				  (u16)gain & 0x3ff);
    }
    
    static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
    {
    	return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
    			      BIT(1), on ? 0 : BIT(1));
    }
    
    static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
    {
    	int ret;
    	unsigned int flags = sensor->ep.bus.parallel.flags;
    	u8 pclk_pol = 0;
    	u8 hsync_pol = 0;
    	u8 vsync_pol = 0;
    
    	/*
    	 * Note about parallel port configuration.
    	 *
    	 * When configured in parallel mode, the OV5640 will
    	 * output 10 bits data on DVP data lines [9:0].
    	 * If only 8 bits data are wanted, the 8 bits data lines
    	 * of the camera interface must be physically connected
    	 * on the DVP data lines [9:2].
    	 *
    	 * Control lines polarity can be configured through
    	 * devicetree endpoint control lines properties.
    	 * If no endpoint control lines properties are set,
    	 * polarity will be as below:
    	 * - VSYNC:	active high
    	 * - HREF:	active low
    	 * - PCLK:	active low
    	 */
    
    	if (on) {
    		/*
    		 * configure parallel port control lines polarity
    		 *
    		 * POLARITY CTRL0
    		 * - [5]:	PCLK polarity (0: active low, 1: active high)
    		 * - [1]:	HREF polarity (0: active low, 1: active high)
    		 * - [0]:	VSYNC polarity (mismatch here between
    		 *		datasheet and hardware, 0 is active high
    		 *		and 1 is active low...)
    		 */
    		if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
    			pclk_pol = 1;
    		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
    			hsync_pol = 1;
    		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
    			vsync_pol = 1;
    
    		ret = ov5640_write_reg(sensor,
    				       OV5640_REG_POLARITY_CTRL00,
    				       (pclk_pol << 5) |
    				       (hsync_pol << 1) |
    				       vsync_pol);
    
    		if (ret)
    			return ret;
    	}
    
    	/*
    	 * powerdown MIPI TX/RX PHY & disable MIPI
    	 *
    	 * MIPI CONTROL 00
    	 * 4:	 PWDN PHY TX
    	 * 3:	 PWDN PHY RX
    	 * 2:	 MIPI enable
    	 */
    	ret = ov5640_write_reg(sensor,
    			       OV5640_REG_IO_MIPI_CTRL00, on ? 0x18 : 0);
    	if (ret)
    		return ret;
    
    	/*
    	 * enable VSYNC/HREF/PCLK DVP control lines
    	 * & D[9:6] DVP data lines
    	 *
    	 * PAD OUTPUT ENABLE 01
    	 * - 6:		VSYNC output enable
    	 * - 5:		HREF output enable
    	 * - 4:		PCLK output enable
    	 * - [3:0]:	D[9:6] output enable
    	 */
    	ret = ov5640_write_reg(sensor,
    			       OV5640_REG_PAD_OUTPUT_ENABLE01,
    			       on ? 0x7f : 0);
    	if (ret)
    		return ret;
    
    	/*
    	 * enable D[5:0] DVP data lines
    	 *
    	 * PAD OUTPUT ENABLE 02
    	 * - [7:2]:	D[5:0] output enable
    	 */
    	return ov5640_write_reg(sensor,
    				OV5640_REG_PAD_OUTPUT_ENABLE02,
    				on ? 0xfc : 0);
    }
    
    static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
    {
    	int ret;
    
    	/*
    	 * Enable/disable the MIPI interface
    	 *
    	 * 0x300e = on ? 0x45 : 0x40
    	 *
    	 * FIXME: the sensor manual (version 2.03) reports
    	 * [7:5] = 000  : 1 data lane mode
    	 * [7:5] = 001  : 2 data lanes mode
    	 * But this settings do not work, while the following ones
    	 * have been validated for 2 data lanes mode.
    	 *
    	 * [7:5] = 010	: 2 data lanes mode
    	 * [4] = 0	: Power up MIPI HS Tx
    	 * [3] = 0	: Power up MIPI LS Rx
    	 * [2] = 1/0	: MIPI interface enable/disable
    	 * [1:0] = 01/00: FIXME: 'debug'
    	 */
    	ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00,
    			       on ? 0x45 : 0x40);
    	if (ret)
    		return ret;
    
    	return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
    				on ? 0x00 : 0x0f);
    }
    
    static int ov5640_get_sysclk(struct ov5640_dev *sensor)
    {
    	 /* calculate sysclk */
    	u32 xvclk = sensor->xclk_freq / 10000;
    	u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
    	u32 sclk_rdiv_map[] = {1, 2, 4, 8};
    	u32 bit_div2x = 1, sclk_rdiv, sysclk;
    	u8 temp1, temp2;
    	int ret;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
    	if (ret)
    		return ret;
    	temp2 = temp1 & 0x0f;
    	if (temp2 == 8 || temp2 == 10)
    		bit_div2x = temp2 / 2;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
    	if (ret)
    		return ret;
    	sysdiv = temp1 >> 4;
    	if (sysdiv == 0)
    		sysdiv = 16;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
    	if (ret)
    		return ret;
    	multiplier = temp1;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
    	if (ret)
    		return ret;
    	prediv = temp1 & 0x0f;
    	pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
    	if (ret)
    		return ret;
    	temp2 = temp1 & 0x03;
    	sclk_rdiv = sclk_rdiv_map[temp2];
    
    	if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
    		return -EINVAL;
    
    	VCO = xvclk * multiplier / prediv;
    
    	sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
    
    	return sysclk;
    }
    
    static int ov5640_set_night_mode(struct ov5640_dev *sensor)
    {
    	 /* read HTS from register settings */
    	u8 mode;
    	int ret;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
    	if (ret)
    		return ret;
    	mode &= 0xfb;
    	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
    }
    
    static int ov5640_get_hts(struct ov5640_dev *sensor)
    {
    	/* read HTS from register settings */
    	u16 hts;
    	int ret;
    
    	ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
    	if (ret)
    		return ret;
    	return hts;
    }
    
    static int ov5640_get_vts(struct ov5640_dev *sensor)
    {
    	u16 vts;
    	int ret;
    
    	ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
    	if (ret)
    		return ret;
    	return vts;
    }
    
    static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
    {
    	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
    }
    
    static int ov5640_get_light_freq(struct ov5640_dev *sensor)
    {
    	/* get banding filter value */
    	int ret, light_freq = 0;
    	u8 temp, temp1;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
    	if (ret)
    		return ret;
    
    	if (temp & 0x80) {
    		/* manual */
    		ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
    				      &temp1);
    		if (ret)
    			return ret;
    		if (temp1 & 0x04) {
    			/* 50Hz */
    			light_freq = 50;
    		} else {
    			/* 60Hz */
    			light_freq = 60;
    		}
    	} else {
    		/* auto */
    		ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
    				      &temp1);
    		if (ret)
    			return ret;
    
    		if (temp1 & 0x01) {
    			/* 50Hz */
    			light_freq = 50;
    		} else {
    			/* 60Hz */
    		}
    	}
    
    	return light_freq;
    }
    
    static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
    {
    	u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
    	int ret;
    
    	/* read preview PCLK */
    	ret = ov5640_get_sysclk(sensor);
    	if (ret < 0)
    		return ret;
    	if (ret == 0)
    		return -EINVAL;
    	sensor->prev_sysclk = ret;
    	/* read preview HTS */
    	ret = ov5640_get_hts(sensor);
    	if (ret < 0)
    		return ret;
    	if (ret == 0)
    		return -EINVAL;
    	sensor->prev_hts = ret;
    
    	/* read preview VTS */
    	ret = ov5640_get_vts(sensor);
    	if (ret < 0)
    		return ret;
    	prev_vts = ret;
    
    	/* calculate banding filter */
    	/* 60Hz */
    	band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
    	ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
    	if (ret)
    		return ret;
    	if (!band_step60)
    		return -EINVAL;
    	max_band60 = (int)((prev_vts - 4) / band_step60);
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
    	if (ret)
    		return ret;
    
    	/* 50Hz */
    	band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
    	ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
    	if (ret)
    		return ret;
    	if (!band_step50)
    		return -EINVAL;
    	max_band50 = (int)((prev_vts - 4) / band_step50);
    	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
    }
    
    static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
    {
    	/* stable in high */
    	u32 fast_high, fast_low;
    	int ret;
    
    	sensor->ae_low = target * 23 / 25;	/* 0.92 */
    	sensor->ae_high = target * 27 / 25;	/* 1.08 */
    
    	fast_high = sensor->ae_high << 1;
    	if (fast_high > 255)
    		fast_high = 255;
    
    	fast_low = sensor->ae_low >> 1;
    
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
    	if (ret)
    		return ret;
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
    	if (ret)
    		return ret;
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
    	if (ret)
    		return ret;
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
    	if (ret)
    		return ret;
    	ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
    	if (ret)
    		return ret;
    	return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
    }
    
    static int ov5640_get_binning(struct ov5640_dev *sensor)
    {
    	u8 temp;
    	int ret;
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
    	if (ret)
    		return ret;
    
    	return temp & BIT(0);
    }
    
    static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
    {
    	int ret;
    
    	/*
    	 * TIMING TC REG21:
    	 * - [0]:	Horizontal binning enable
    	 */
    	ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
    			     BIT(0), enable ? BIT(0) : 0);
    	if (ret)
    		return ret;
    	/*
    	 * TIMING TC REG20:
    	 * - [0]:	Undocumented, but hardcoded init sequences
    	 *		are always setting REG21/REG20 bit 0 to same value...
    	 */
    	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
    			      BIT(0), enable ? BIT(0) : 0);
    }
    
    static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	u8 temp, channel = virtual_channel;
    	int ret;
    
    	if (channel > 3) {
    		dev_err(&client->dev,
    			"%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
    			__func__, channel);
    		return -EINVAL;
    	}
    
    	ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
    	if (ret)
    		return ret;
    	temp &= ~(3 << 6);
    	temp |= (channel << 6);
    	return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
    }
    
    static const struct ov5640_mode_info *
    ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
    		 int width, int height, bool nearest)
    {
    	const struct ov5640_mode_info *mode;
    
    	mode = v4l2_find_nearest_size(ov5640_mode_data,
    				      ARRAY_SIZE(ov5640_mode_data),
    				      hact, vact,
    				      width, height);
    
    	if (!mode ||
    	    (!nearest && (mode->hact != width || mode->vact != height)))
    		return NULL;
    
    	/* Only 640x480 can operate at 60fps (for now) */
    	if (fr == OV5640_60_FPS &&
    	    !(mode->hact == 640 && mode->vact == 480))
    		return NULL;
    
    	if (fr == OV5640_30_FPS &&
    	    !(mode->hact == 1920 && mode->vact == 1080))
    		return NULL;
    	/* 2592x1944 only works at 15fps */
    	if (fr != OV5640_15_FPS &&
    	    (mode->hact == 2592 && mode->vact == 1944))
    		return NULL;
    
    	return mode;
    }
    
    /*
     * sensor changes between scaling and subsampling, go through
     * exposure calculation
     */
    static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
    					 const struct ov5640_mode_info *mode)
    {
    	u32 prev_shutter, prev_gain16;
    	u32 cap_shutter, cap_gain16;
    	u32 cap_sysclk, cap_hts, cap_vts;
    	u32 light_freq, cap_bandfilt, cap_maxband;
    	u32 cap_gain16_shutter;
    	u8 average;
    	int ret;
    
    	if (!mode->reg_data)
    		return -EINVAL;
    
    	/* read preview shutter */
    	ret = ov5640_get_exposure(sensor);
    	if (ret < 0)
    		return ret;
    	prev_shutter = ret;
    	ret = ov5640_get_binning(sensor);
    	if (ret < 0)
    		return ret;
    	if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
    	    mode->id != OV5640_MODE_1080P_1920_1080)
    		prev_shutter *= 2;
    
    	/* read preview gain */
    	ret = ov5640_get_gain(sensor);
    	if (ret < 0)
    		return ret;
    	prev_gain16 = ret;
    
    	/* get average */
    	ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
    	if (ret)
    		return ret;
    
    	/* turn off night mode for capture */
    	ret = ov5640_set_night_mode(sensor);
    	if (ret < 0)
    		return ret;
    
    	/* Write capture setting */
    	ret = ov5640_load_regs(sensor, mode);
    	if (ret < 0)
    		return ret;
    
    	/* read capture VTS */
    	ret = ov5640_get_vts(sensor);
    	if (ret < 0)
    		return ret;
    	cap_vts = ret;
    	ret = ov5640_get_hts(sensor);
    	if (ret < 0)
    		return ret;
    	if (ret == 0)
    		return -EINVAL;
    	cap_hts = ret;
    
    	ret = ov5640_get_sysclk(sensor);
    	if (ret < 0)
    		return ret;
    	if (ret == 0)
    		return -EINVAL;
    	cap_sysclk = ret;
    
    	/* calculate capture banding filter */
    	ret = ov5640_get_light_freq(sensor);
    	if (ret < 0)
    		return ret;
    	light_freq = ret;
    
    	if (light_freq == 60) {
    		/* 60Hz */
    		cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
    	} else {
    		/* 50Hz */
    		cap_bandfilt = cap_sysclk * 100 / cap_hts;
    	}
    
    	if (!sensor->prev_sysclk) {
    		ret = ov5640_get_sysclk(sensor);
    		if (ret < 0)
    			return ret;
    		if (ret == 0)
    			return -EINVAL;
    		sensor->prev_sysclk = ret;
    	}
    
    	if (!cap_bandfilt)
    		return -EINVAL;
    
    	cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
    
    	/* calculate capture shutter/gain16 */
    	if (average > sensor->ae_low && average < sensor->ae_high) {
    		/* in stable range */
    		cap_gain16_shutter =
    			prev_gain16 * prev_shutter *
    			cap_sysclk / sensor->prev_sysclk *
    			sensor->prev_hts / cap_hts *
    			sensor->ae_target / average;
    	} else {
    		cap_gain16_shutter =
    			prev_gain16 * prev_shutter *
    			cap_sysclk / sensor->prev_sysclk *
    			sensor->prev_hts / cap_hts;
    	}
    
    	/* gain to shutter */
    	if (cap_gain16_shutter < (cap_bandfilt * 16)) {
    		/* shutter < 1/100 */
    		cap_shutter = cap_gain16_shutter / 16;
    		if (cap_shutter < 1)
    			cap_shutter = 1;
    
    		cap_gain16 = cap_gain16_shutter / cap_shutter;
    		if (cap_gain16 < 16)
    			cap_gain16 = 16;
    	} else {
    		if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
    			/* exposure reach max */
    			cap_shutter = cap_bandfilt * cap_maxband;
    			if (!cap_shutter)
    				return -EINVAL;
    
    			cap_gain16 = cap_gain16_shutter / cap_shutter;
    		} else {
    			/* 1/100 < (cap_shutter = n/100) =< max */
    			cap_shutter =
    				((int)(cap_gain16_shutter / 16 / cap_bandfilt))
    				* cap_bandfilt;
    			if (!cap_shutter)
    				return -EINVAL;
    
    			cap_gain16 = cap_gain16_shutter / cap_shutter;
    		}
    	}
    
    	/* set capture gain */
    	ret = ov5640_set_gain(sensor, cap_gain16);
    	if (ret)
    		return ret;
    
    	/* write capture shutter */
    	if (cap_shutter > (cap_vts - 4)) {
    		cap_vts = cap_shutter + 4;
    		ret = ov5640_set_vts(sensor, cap_vts);
    		if (ret < 0)
    			return ret;
    	}
    
    	/* set exposure */
    	return ov5640_set_exposure(sensor, cap_shutter);
    }
    
    /*
     * if sensor changes inside scaling or subsampling
     * change mode directly
     */
    static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
    				  const struct ov5640_mode_info *mode)
    {
    	if (!mode->reg_data)
    		return -EINVAL;
    
    	/* Write capture setting */
    	return ov5640_load_regs(sensor, mode);
    }
    
    static int ov5640_set_mode(struct ov5640_dev *sensor)
    {
    	const struct ov5640_mode_info *mode = sensor->current_mode;
    	const struct ov5640_mode_info *orig_mode = sensor->last_mode;
    	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
    	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
    	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
    	unsigned long rate;
    	int ret;
    
    	dn_mode = mode->dn_mode;
    	orig_dn_mode = orig_mode->dn_mode;
    
    	/* auto gain and exposure must be turned off when changing modes */
    	if (auto_gain) {
    		ret = ov5640_set_autogain(sensor, false);
    		if (ret)
    			return ret;
    	}
    
    	if (auto_exp) {
    		ret = ov5640_set_autoexposure(sensor, false);
    		if (ret)
    			goto restore_auto_gain;
    	}
    
    	/*
    	 * All the formats we support have 16 bits per pixel, seems to require
    	 * the same rate than YUV, so we can just use 16 bpp all the time.
    	 */
    	rate = mode->vtot * mode->htot * 16;
    	rate *= ov5640_framerates[sensor->current_fr];
    	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
    		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
    		ret = ov5640_set_mipi_pclk(sensor, rate);
    	} else {
    		rate = rate / sensor->ep.bus.parallel.bus_width;
    		ret = ov5640_set_dvp_pclk(sensor, rate);
    	}
    
    	if (ret < 0)
    		return 0;
    
    	if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
    	    (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
    		/*
    		 * change between subsampling and scaling
    		 * go through exposure calculation
    		 */
    		ret = ov5640_set_mode_exposure_calc(sensor, mode);
    	} else {
    		/*
    		 * change inside subsampling or scaling
    		 * download firmware directly
    		 */
    		ret = ov5640_set_mode_direct(sensor, mode);
    	}
    	if (ret < 0)
    		goto restore_auto_exp_gain;
    
    	/* restore auto gain and exposure */
    	if (auto_gain)
    		ov5640_set_autogain(sensor, true);
    	if (auto_exp)
    		ov5640_set_autoexposure(sensor, true);
    
    	ret = ov5640_set_binning(sensor, dn_mode != SCALING);
    	if (ret < 0)
    		return ret;
    	ret = ov5640_set_ae_target(sensor, sensor->ae_target);
    	if (ret < 0)
    		return ret;
    	ret = ov5640_get_light_freq(sensor);
    	if (ret < 0)
    		return ret;
    	ret = ov5640_set_bandingfilter(sensor);
    	if (ret < 0)
    		return ret;
    	ret = ov5640_set_virtual_channel(sensor);
    	if (ret < 0)
    		return ret;
    
    	sensor->pending_mode_change = false;
    	sensor->last_mode = mode;
    
    	return 0;
    
    restore_auto_exp_gain:
    	if (auto_exp)
    		ov5640_set_autoexposure(sensor, true);
    restore_auto_gain:
    	if (auto_gain)
    		ov5640_set_autogain(sensor, true);
    
    	return ret;
    }
    
    static int ov5640_set_framefmt(struct ov5640_dev *sensor,
    			       struct v4l2_mbus_framefmt *format);
    
    /* restore the last set video mode after chip power-on */
    static int ov5640_restore_mode(struct ov5640_dev *sensor)
    {
    	int ret;
    
    	/* first load the initial register values */
    	ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
    	if (ret < 0)
    		return ret;
    	sensor->last_mode = &ov5640_mode_init_data;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
    			     (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
    			     ilog2(OV5640_SCLK_ROOT_DIV));
    	if (ret)
    		return ret;
    
    	/* now restore the last capture mode */
    	ret = ov5640_set_mode(sensor);
    	if (ret < 0)
    		return ret;
    
    	return ov5640_set_framefmt(sensor, &sensor->fmt);
    }
    
    static void ov5640_power(struct ov5640_dev *sensor, bool enable)
    {
    	gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
    }
    
    static void ov5640_reset(struct ov5640_dev *sensor)
    {
    	if (!sensor->reset_gpio)
    		return;
    
    	gpiod_set_value_cansleep(sensor->reset_gpio, 0);
    
    	/* camera power cycle */
    	ov5640_power(sensor, false);
    	usleep_range(5000, 10000);
    	ov5640_power(sensor, true);
    	usleep_range(5000, 10000);
    
    	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
    	usleep_range(1000, 2000);
    
    	gpiod_set_value_cansleep(sensor->reset_gpio, 0);
    	usleep_range(20000, 25000);
    }
    
    static int ov5640_set_power_on(struct ov5640_dev *sensor)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	int ret;
    
    	ret = clk_prepare_enable(sensor->xclk);
    	if (ret) {
    		dev_err(&client->dev, "%s: failed to enable clock\n",
    			__func__);
    		return ret;
    	}
    
    	ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
    				    sensor->supplies);
    	if (ret) {
    		dev_err(&client->dev, "%s: failed to enable regulators\n",
    			__func__);
    		goto xclk_off;
    	}
    
    	ov5640_reset(sensor);
    	ov5640_power(sensor, true);
    
    	ret = ov5640_init_slave_id(sensor);
    	if (ret)
    		goto power_off;
    
    	return 0;
    
    power_off:
    	ov5640_power(sensor, false);
    	regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
    xclk_off:
    	clk_disable_unprepare(sensor->xclk);
    	return ret;
    }
    
    static void ov5640_set_power_off(struct ov5640_dev *sensor)
    {
    	ov5640_power(sensor, false);
    	regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
    	clk_disable_unprepare(sensor->xclk);
    }
    
    static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
    {
    	int ret = 0;
    
    	if (on) {
    		ret = ov5640_set_power_on(sensor);
    		if (ret)
    			return ret;
    
    		ret = ov5640_restore_mode(sensor);
    		if (ret)
    			goto power_off;
    
    		/* We're done here for DVP bus, while CSI-2 needs setup. */
    		if (sensor->ep.bus_type != V4L2_MBUS_CSI2)
    			return 0;
    
    		/*
    		 * Power up MIPI HS Tx and LS Rx; 2 data lanes mode
    		 *
    		 * 0x300e = 0x40
    		 * [7:5] = 010	: 2 data lanes mode (see FIXME note in
    		 *		  "ov5640_set_stream_mipi()")
    		 * [4] = 0	: Power up MIPI HS Tx
    		 * [3] = 0	: Power up MIPI LS Rx
    		 * [2] = 0	: MIPI interface disabled
    		 */
    		ret = ov5640_write_reg(sensor,
    				       OV5640_REG_IO_MIPI_CTRL00, 0x40);
    		if (ret)
    			goto power_off;
    
    		/*
    		 * Gate clock and set LP11 in 'no packets mode' (idle)
    		 *
    		 * 0x4800 = 0x24
    		 * [5] = 1	: Gate clock when 'no packets'
    		 * [2] = 1	: MIPI bus in LP11 when 'no packets'
    		 */
    		ret = ov5640_write_reg(sensor,
    				       OV5640_REG_MIPI_CTRL00, 0x24);
    		if (ret)
    			goto power_off;
    
    		/*
    		 * Set data lanes and clock in LP11 when 'sleeping'
    		 *
    		 * 0x3019 = 0x70
    		 * [6] = 1	: MIPI data lane 2 in LP11 when 'sleeping'
    		 * [5] = 1	: MIPI data lane 1 in LP11 when 'sleeping'
    		 * [4] = 1	: MIPI clock lane in LP11 when 'sleeping'
    		 */
    		ret = ov5640_write_reg(sensor,
    				       OV5640_REG_PAD_OUTPUT00, 0x70);
    		if (ret)
    			goto power_off;
    
    		/* Give lanes some time to coax into LP11 state. */
    		usleep_range(500, 1000);
    
    	} else {
    		if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
    			/* Reset MIPI bus settings to their default values. */
    			ov5640_write_reg(sensor,
    					 OV5640_REG_IO_MIPI_CTRL00, 0x58);
    			ov5640_write_reg(sensor,
    					 OV5640_REG_MIPI_CTRL00, 0x04);
    			ov5640_write_reg(sensor,
    					 OV5640_REG_PAD_OUTPUT00, 0x00);
    		}
    
    		ov5640_set_power_off(sensor);
    	}
    
    	return 0;
    
    power_off:
    	ov5640_set_power_off(sensor);
    	return ret;
    }
    
    /* --------------- Subdev Operations --------------- */
    
    static int ov5640_s_power(struct v4l2_subdev *sd, int on)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	int ret = 0;
    
    	mutex_lock(&sensor->lock);
    
    	/*
    	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
    	 * update the power state.
    	 */
    	if (sensor->power_count == !on) {
    		ret = ov5640_set_power(sensor, !!on);
    		if (ret)
    			goto out;
    	}
    
    	/* Update the power count. */
    	sensor->power_count += on ? 1 : -1;
    	WARN_ON(sensor->power_count < 0);
    out:
    	mutex_unlock(&sensor->lock);
    
    	if (on && !ret && sensor->power_count == 1) {
    		/* restore controls */
    		ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
    	}
    
    	return ret;
    }
    
    static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
    				     struct v4l2_fract *fi,
    				     u32 width, u32 height)
    {
    	const struct ov5640_mode_info *mode;
    	enum ov5640_frame_rate rate = OV5640_30_FPS;
    	int minfps, maxfps, best_fps, fps;
    	int i;
    
    	minfps = ov5640_framerates[OV5640_15_FPS];
    	maxfps = ov5640_framerates[OV5640_60_FPS];
    
    	if (fi->numerator == 0) {
    		fi->denominator = maxfps;
    		fi->numerator = 1;
    		rate = OV5640_60_FPS;
    		goto find_mode;
    	}
    
    	fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
    			minfps, maxfps);
    
    	best_fps = minfps;
    	for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
    		int curr_fps = ov5640_framerates[i];
    
    		if (abs(curr_fps - fps) < abs(best_fps - fps)) {
    			best_fps = curr_fps;
    			rate = i;
    		}
    	}
    
    	fi->numerator = 1;
    	fi->denominator = best_fps;
    
    find_mode:
    	mode = ov5640_find_mode(sensor, rate, width, height, false);
    	return mode ? rate : -EINVAL;
    }
    
    static int ov5640_get_fmt(struct v4l2_subdev *sd,
    			  struct v4l2_subdev_pad_config *cfg,
    			  struct v4l2_subdev_format *format)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	struct v4l2_mbus_framefmt *fmt;
    
    	if (format->pad != 0)
    		return -EINVAL;
    
    	mutex_lock(&sensor->lock);
    
    	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
    		fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg,
    						 format->pad);
    	else
    		fmt = &sensor->fmt;
    
    	format->format = *fmt;
    
    	mutex_unlock(&sensor->lock);
    
    	return 0;
    }
    
    static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
    				   struct v4l2_mbus_framefmt *fmt,
    				   enum ov5640_frame_rate fr,
    				   const struct ov5640_mode_info **new_mode)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	const struct ov5640_mode_info *mode;
    	int i;
    
    	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
    	if (!mode)
    		return -EINVAL;
    	fmt->width = mode->hact;
    	fmt->height = mode->vact;
    
    	if (new_mode)
    		*new_mode = mode;
    
    	for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
    		if (ov5640_formats[i].code == fmt->code)
    			break;
    	if (i >= ARRAY_SIZE(ov5640_formats))
    		i = 0;
    
    	fmt->code = ov5640_formats[i].code;
    	fmt->colorspace = ov5640_formats[i].colorspace;
    	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
    	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
    	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
    
    	return 0;
    }
    
    static int ov5640_set_fmt(struct v4l2_subdev *sd,
    			  struct v4l2_subdev_pad_config *cfg,
    			  struct v4l2_subdev_format *format)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	const struct ov5640_mode_info *new_mode;
    	struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
    	struct v4l2_mbus_framefmt *fmt;
    	u64 rate;
    	int ret;
    
    	if (format->pad != 0)
    		return -EINVAL;
    
    	mutex_lock(&sensor->lock);
    
    	if (sensor->streaming) {
    		ret = -EBUSY;
    		goto out;
    	}
    
    	ret = ov5640_try_fmt_internal(sd, mbus_fmt,
    				      sensor->current_fr, &new_mode);
    	if (ret)
    		goto out;
    
    	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
    		fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
    	else
    		fmt = &sensor->fmt;
    
    	*fmt = *mbus_fmt;
    
    	if (new_mode != sensor->current_mode) {
    		sensor->current_mode = new_mode;
    		sensor->pending_mode_change = true;
    	}
    	if (mbus_fmt->code != sensor->fmt.code)
    		sensor->pending_fmt_change = true;
    
    	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
    	rate *= ov5640_framerates[sensor->current_fr];
    	sensor->pixel_rate = rate;
    
    	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,sensor->pixel_rate);
    out:
    	mutex_unlock(&sensor->lock);
    	return ret;
    }
    
    static int ov5640_set_framefmt(struct ov5640_dev *sensor,
    			       struct v4l2_mbus_framefmt *format)
    {
    	int ret = 0;
    	bool is_rgb = false;
    	bool is_jpeg = false;
    	u8 val;
    
    	switch (format->code) {
    	case MEDIA_BUS_FMT_UYVY8_2X8:
    		/* YUV422, UYVY */
    		val = 0x3f;
    		break;
    	case MEDIA_BUS_FMT_YUYV8_2X8:
    		/* YUV422, YUYV */
    		val = 0x30;
    		break;
    	case MEDIA_BUS_FMT_RGB565_2X8_LE:
    		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
    		val = 0x6F;
    		is_rgb = true;
    		break;
    	case MEDIA_BUS_FMT_RGB565_2X8_BE:
    		/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
    		val = 0x61;
    		is_rgb = true;
    		break;
    	case MEDIA_BUS_FMT_JPEG_1X8:
    		/* YUV422, YUYV */
    		val = 0x30;
    		is_jpeg = true;
    		break;
    	case MEDIA_BUS_FMT_UYVY8_1X16:
    		/* YUV422, YUYV */
    		val = 0x30;
    		break;
    
    	default:
    		return -EINVAL;
    	}
    
    	/* FORMAT CONTROL00: YUV and RGB formatting */
    	ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val);
    	if (ret)
    		return ret;
    
    	/* FORMAT MUX CONTROL: ISP YUV or RGB */
    	ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
    			       is_rgb ? 0x01 : 0x00);
    	if (ret)
    		return ret;
    
    	/*
    	 * TIMING TC REG21:
    	 * - [5]:	JPEG enable
    	 */
    	ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
    			     BIT(5), is_jpeg ? BIT(5) : 0);
    	if (ret)
    		return ret;
    
    	/*
    	 * SYSTEM RESET02:
    	 * - [4]:	Reset JFIFO
    	 * - [3]:	Reset SFIFO
    	 * - [2]:	Reset JPEG
    	 */
    	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
    			     BIT(4) | BIT(3) | BIT(2),
    			     is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
    	if (ret)
    		return ret;
    
    	/*
    	 * CLOCK ENABLE02:
    	 * - [5]:	Enable JPEG 2x clock
    	 * - [3]:	Enable JPEG clock
    	 */
    	return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
    			      BIT(5) | BIT(3),
    			      is_jpeg ? (BIT(5) | BIT(3)) : 0);
    }
    
    /*
     * Sensor Controls.
     */
    
    static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
    {
    	int ret;
    
    	if (value) {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
    				     BIT(0), BIT(0));
    		if (ret)
    			return ret;
    		ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
    	} else {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
    	}
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
    {
    	int ret;
    
    	if (value) {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
    				     BIT(2), BIT(2));
    		if (ret)
    			return ret;
    		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
    				       value & 0xff);
    	} else {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
    	}
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
    {
    	int ret;
    
    	if (value) {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
    				     BIT(1), BIT(1));
    		if (ret)
    			return ret;
    		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
    				       value & 0xff);
    		if (ret)
    			return ret;
    		ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
    				       value & 0xff);
    	} else {
    		ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
    	}
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
    {
    	int ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
    			     BIT(0), awb ? 0 : 1);
    	if (ret)
    		return ret;
    
    	if (!awb) {
    		u16 red = (u16)sensor->ctrls.red_balance->val;
    		u16 blue = (u16)sensor->ctrls.blue_balance->val;
    
    		ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
    		if (ret)
    			return ret;
    		ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
    	}
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor,
    				    enum v4l2_exposure_auto_type auto_exposure)
    {
    	struct ov5640_ctrls *ctrls = &sensor->ctrls;
    	bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
    	int ret = 0;
    
    	if (ctrls->auto_exp->is_new) {
    		ret = ov5640_set_autoexposure(sensor, auto_exp);
    		if (ret)
    			return ret;
    	}
    
    	if (!auto_exp && ctrls->exposure->is_new) {
    		u16 max_exp;
    
    		ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
    					&max_exp);
    		if (ret)
    			return ret;
    		ret = ov5640_get_vts(sensor);
    		if (ret < 0)
    			return ret;
    		max_exp += ret;
    		ret = 0;
    
    		if (ctrls->exposure->val < max_exp)
    			ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
    	}
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
    {
    	struct ov5640_ctrls *ctrls = &sensor->ctrls;
    	int ret = 0;
    
    	if (ctrls->auto_gain->is_new) {
    		ret = ov5640_set_autogain(sensor, auto_gain);
    		if (ret)
    			return ret;
    	}
    
    	if (!auto_gain && ctrls->gain->is_new)
    		ret = ov5640_set_gain(sensor, ctrls->gain->val);
    
    	return ret;
    }
    
    static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
    {
    	return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
    			      0xa4, value ? 0xa4 : 0);
    }
    
    static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
    {
    	int ret;
    
    	ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
    			     (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
    			     0 : BIT(7));
    	if (ret)
    		return ret;
    
    	return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
    			      (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
    			      BIT(2) : 0);
    }
    
    static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
    {
    	/*
    	 * If sensor is mounted upside down, mirror logic is inversed.
    	 *
    	 * Sensor is a BSI (Back Side Illuminated) one,
    	 * so image captured is physically mirrored.
    	 * This is why mirror logic is inversed in
    	 * order to cancel this mirror effect.
    	 */
    
    	/*
    	 * TIMING TC REG21:
    	 * - [2]:	ISP mirror
    	 * - [1]:	Sensor mirror
    	 */
    	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
    			      BIT(2) | BIT(1),
    			      (!(value ^ sensor->upside_down)) ?
    			      (BIT(2) | BIT(1)) : 0);
    }
    
    static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
    {
    	/* If sensor is mounted upside down, flip logic is inversed */
    
    	/*
    	 * TIMING TC REG20:
    	 * - [2]:	ISP vflip
    	 * - [1]:	Sensor vflip
    	 */
    	return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
    			      BIT(2) | BIT(1),
    			      (value ^ sensor->upside_down) ?
    			      (BIT(2) | BIT(1)) : 0);
    }
    
    static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
    {
    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	int val;
    
    	/* v4l2_ctrl_lock() locks our own mutex */
    
    	switch (ctrl->id) {
    	case V4L2_CID_AUTOGAIN:
    		val = ov5640_get_gain(sensor);
    		if (val < 0)
    			return val;
    		sensor->ctrls.gain->val = val;
    		break;
    	case V4L2_CID_EXPOSURE_AUTO:
    		val = ov5640_get_exposure(sensor);
    		if (val < 0)
    			return val;
    		sensor->ctrls.exposure->val = val;
    		break;
    	}
    
    	return 0;
    }
    
    static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
    {
    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	int ret;
    
    	/* v4l2_ctrl_lock() locks our own mutex */
    
    	/*
    	 * If the device is not powered up by the host driver do
    	 * not apply any controls to H/W at this time. Instead
    	 * the controls will be restored right after power-up.
    	 */
    	if (sensor->power_count == 0)
    		return 0;
    
    	switch (ctrl->id) {
    	case V4L2_CID_AUTOGAIN:
    		ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
    		break;
    	case V4L2_CID_EXPOSURE_AUTO:
    		ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
    		break;
    	case V4L2_CID_AUTO_WHITE_BALANCE:
    		ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
    		break;
    	case V4L2_CID_HUE:
    		ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
    		break;
    	case V4L2_CID_CONTRAST:
    		ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
    		break;
    	case V4L2_CID_SATURATION:
    		ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
    		break;
    	case V4L2_CID_TEST_PATTERN:
    		ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
    		break;
    	case V4L2_CID_POWER_LINE_FREQUENCY:
    		ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
    		break;
    	case V4L2_CID_HFLIP:
    		ret = ov5640_set_ctrl_hflip(sensor, ctrl->val);
    		break;
    	case V4L2_CID_VFLIP:
    		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
    		break;
    	default:
    		ret = -EINVAL;
    		break;
    	}
    
    	return ret;
    }
    
    static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
    	.g_volatile_ctrl = ov5640_g_volatile_ctrl,
    	.s_ctrl = ov5640_s_ctrl,
    };
    
    static const char * const test_pattern_menu[] = {
    	"Disabled",
    	"Color bars",
    };
    
    static int ov5640_init_controls(struct ov5640_dev *sensor)
    {
    	const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
    	struct ov5640_ctrls *ctrls = &sensor->ctrls;
    	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
    	int ret;
    
    	v4l2_ctrl_handler_init(hdl, 32);
    
    	/* we can use our own mutex for the ctrl lock */
    	hdl->lock = &sensor->lock;
    
    	/* Clock related controls */
    	ctrls->pixel_rate =
    		v4l2_ctrl_new_std(hdl, ops,
    				  V4L2_CID_PIXEL_RATE, 0, INT_MAX, 1,
    				  55969920);
    	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
    
    	/* Auto/manual white balance */
    	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
    					   V4L2_CID_AUTO_WHITE_BALANCE,
    					   0, 1, 1, 1);
    	ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
    						0, 4095, 1, 0);
    	ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
    					       0, 4095, 1, 0);
    	/* Auto/manual exposure */
    	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
    						 V4L2_CID_EXPOSURE_AUTO,
    						 V4L2_EXPOSURE_MANUAL, 0,
    						 V4L2_EXPOSURE_AUTO);
    	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
    					    0, 65535, 1, 0);
    	/* Auto/manual gain */
    	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
    					     0, 1, 1, 1);
    	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
    					0, 1023, 1, 0);
    
    	ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
    					      0, 255, 1, 64);
    	ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
    				       0, 359, 1, 0);
    	ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
    					    0, 255, 1, 0);
    	ctrls->test_pattern =
    		v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
    					     ARRAY_SIZE(test_pattern_menu) - 1,
    					     0, 0, test_pattern_menu);
    	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
    					 0, 1, 1, 0);
    	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
    					 0, 1, 1, 0);
    
    	ctrls->light_freq =
    		v4l2_ctrl_new_std_menu(hdl, ops,
    				       V4L2_CID_POWER_LINE_FREQUENCY,
    				       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
    				       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
    
    	if (hdl->error) {
    		ret = hdl->error;
    		goto free_ctrls;
    	}
    
    	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
    	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
    
    	v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
    	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
    	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
    
    	sensor->sd.ctrl_handler = hdl;
    	return 0;
    
    free_ctrls:
    	v4l2_ctrl_handler_free(hdl);
    	return ret;
    }
    
    static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
    				  struct v4l2_subdev_pad_config *cfg,
    				  struct v4l2_subdev_frame_size_enum *fse)
    {
    	if (fse->pad != 0)
    		return -EINVAL;
    	if (fse->index >= OV5640_NUM_MODES)
    		return -EINVAL;
    
    	fse->min_width =
    		ov5640_mode_data[fse->index].hact;
    	fse->max_width = fse->min_width;
    	fse->min_height =
    		ov5640_mode_data[fse->index].vact;
    	fse->max_height = fse->min_height;
    
    	return 0;
    }
    
    static int ov5640_enum_frame_interval(
    	struct v4l2_subdev *sd,
    	struct v4l2_subdev_pad_config *cfg,
    	struct v4l2_subdev_frame_interval_enum *fie)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	struct v4l2_fract tpf;
    	int ret;
    
    	if (fie->pad != 0)
    		return -EINVAL;
    	if (fie->index >= OV5640_NUM_FRAMERATES)
    		return -EINVAL;
    
    	tpf.numerator = 1;
    	tpf.denominator = ov5640_framerates[fie->index];
    
    	ret = ov5640_try_frame_interval(sensor, &tpf,
    					fie->width, fie->height);
    	if (ret < 0)
    		return -EINVAL;
    
    	fie->interval = tpf;
    	return 0;
    }
    
    static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
    				   struct v4l2_subdev_frame_interval *fi)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    
    	mutex_lock(&sensor->lock);
    	fi->interval = sensor->frame_interval;
    	mutex_unlock(&sensor->lock);
    
    	return 0;
    }
    
    static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
    				   struct v4l2_subdev_frame_interval *fi)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	const struct ov5640_mode_info *mode;
    	int frame_rate, ret = 0;
    	u64 rate;
    
    	if (fi->pad != 0)
    		return -EINVAL;
    
    	mutex_lock(&sensor->lock);
    
    	if (sensor->streaming) {
    		ret = -EBUSY;
    		goto out;
    	}
    
    	mode = sensor->current_mode;
    
    	frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
    					       mode->hact, mode->vact);
    	if (frame_rate < 0) {
    		/* Always return a valid frame interval value */
    		fi->interval = sensor->frame_interval;
    		goto out;
    	}
    
    	mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
    				mode->vact, true);
    	if (!mode) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    	if (mode != sensor->current_mode ||
    	    frame_rate != sensor->current_fr) {
    		sensor->current_fr = frame_rate;
    		sensor->frame_interval = fi->interval;
    		sensor->current_mode = mode;
    		sensor->pending_mode_change = true;
    
    		rate = sensor->current_mode->vtot * sensor->current_mode->htot;
    		rate *= ov5640_framerates[sensor->current_fr];
    		sensor->pixel_rate = rate;
    		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
    					 sensor->pixel_rate);
    	}
    out:
    	mutex_unlock(&sensor->lock);
    	return ret;
    }
    
    static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
    				 struct v4l2_subdev_pad_config *cfg,
    				 struct v4l2_subdev_mbus_code_enum *code)
    {
    	if (code->pad != 0)
    		return -EINVAL;
    	if (code->index >= ARRAY_SIZE(ov5640_formats))
    		return -EINVAL;
    
    	code->code = ov5640_formats[code->index].code;
    	return 0;
    }
    
    static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
    {
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    	int ret = 0;
    
    	mutex_lock(&sensor->lock);
    
    	if (sensor->streaming == !enable) {
    		if (enable && sensor->pending_mode_change) {
    			ret = ov5640_set_mode(sensor);
    			if (ret)
    				goto out;
    		}
    
    		if (enable && sensor->pending_fmt_change) {
    			ret = ov5640_set_framefmt(sensor, &sensor->fmt);
    			if (ret)
    				goto out;
    			sensor->pending_fmt_change = false;
    		}
    
    		if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
    			ret = ov5640_set_stream_mipi(sensor, enable);
    		else
    			ret = ov5640_set_stream_dvp(sensor, enable);
    
    		if (!ret)
    			sensor->streaming = enable;
    	}
    out:
    	mutex_unlock(&sensor->lock);
    	return ret;
    }
    
    static const struct v4l2_subdev_core_ops ov5640_core_ops = {
    	.s_power = ov5640_s_power,
    	.log_status = v4l2_ctrl_subdev_log_status,
    	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
    	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
    };
    
    static const struct v4l2_subdev_video_ops ov5640_video_ops = {
    	.g_frame_interval = ov5640_g_frame_interval,
    	.s_frame_interval = ov5640_s_frame_interval,
    	.s_stream = ov5640_s_stream,
    };
    
    static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
    	.enum_mbus_code = ov5640_enum_mbus_code,
    	.get_fmt = ov5640_get_fmt,
    	.set_fmt = ov5640_set_fmt,
    	.enum_frame_size = ov5640_enum_frame_size,
    	.enum_frame_interval = ov5640_enum_frame_interval,
    };
    
    static const struct v4l2_subdev_ops ov5640_subdev_ops = {
    	.core = &ov5640_core_ops,
    	.video = &ov5640_video_ops,
    	.pad = &ov5640_pad_ops,
    };
    
    static int ov5640_get_regulators(struct ov5640_dev *sensor)
    {
    	int i;
    
    	for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
    		sensor->supplies[i].supply = ov5640_supply_name[i];
    
    	return devm_regulator_bulk_get(&sensor->i2c_client->dev,
    				       OV5640_NUM_SUPPLIES,
    				       sensor->supplies);
    }
    
    static int ov5640_check_chip_id(struct ov5640_dev *sensor)
    {
    	struct i2c_client *client = sensor->i2c_client;
    	int ret = 0;
    	u16 chip_id;
    
    	ov5640_init_slave_id(sensor);
    	/*ret = ov5640_set_power_on(sensor);		//cispl
    	if (ret)
    		return ret;*/
    	ret = ov5640_cispl_read_reg(sensor, OV5640_REG_SYS_CARAVEL, &chip_id);
    	//ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
    	if (ret) {
    		dev_err(&client->dev, "%s: failed to read chip identifier\n",
    			__func__);
    //		goto power_off;
    	}
    
    	if (chip_id != 0x5640) {
    		dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
    			__func__, chip_id);
    		ret = -ENXIO;
    	}
    
    
    //power_off:
    //	ov5640_set_power_off(sensor);
    //	return ret;
    }
    
    static int ov5640_probe(struct i2c_client *client,
    			const struct i2c_device_id *id)
    {
    	struct device *dev = &client->dev;
    	struct fwnode_handle *endpoint;
    	struct ov5640_dev *sensor;
    	struct v4l2_mbus_framefmt *fmt;
    	u32 rotation;
    	int ret;
    
    	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
    	if (!sensor)
    		return -ENOMEM;
    
    	sensor->i2c_client = client;
    
    	/*
    	 * default init sequence initialize sensor to
    	 * YUV422 UYVY VGA@30fps
    	 */
    	fmt = &sensor->fmt;
    //	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
    	fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
    //	fmt->colorspace = V4L2_COLORSPACE_JPEG;
    	fmt->colorspace = V4L2_COLORSPACE_BT2020;
    	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
    	fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
    	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
    	fmt->width = 1920;
    	fmt->height = 1080;
    	fmt->field = V4L2_FIELD_ANY;
    	sensor->frame_interval.numerator = 1;
    	sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
    	sensor->current_fr = OV5640_30_FPS;
    	sensor->current_mode =
    		&ov5640_mode_data[OV5640_MODE_1080P_1920_1080];
    	sensor->last_mode = sensor->current_mode;
    
    	sensor->ae_target = 52;
    
    	/* optional indication of physical rotation of sensor */
    	ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
    				       &rotation);
    	if (!ret) {
    		switch (rotation) {
    		case 180:
    			sensor->upside_down = true;
    			/* fall through */
    		case 0:
    			break;
    		default:
    			dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
    				 rotation);
    		}
    	}
    
    	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
    						  NULL);
    	if (!endpoint) {
    		dev_err(dev, "endpoint node not found\n");
    		return -EINVAL;
    	}
    
    	ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
    	fwnode_handle_put(endpoint);
    	if (ret) {
    		dev_err(dev, "Could not parse endpoint\n");
    		return ret;
    	}
    
    	/* get system clock (xclk) */
    	/*sensor->xclk = devm_clk_get(dev, "xclk");
    	if (IS_ERR(sensor->xclk)) {
    		dev_err(dev, "failed to get xclk\n");
    		return PTR_ERR(sensor->xclk);
    	}
    
    	sensor->xclk_freq = clk_get_rate(sensor->xclk);
    	if (sensor->xclk_freq < OV5640_XCLK_MIN ||
    	    sensor->xclk_freq > OV5640_XCLK_MAX) {
    		dev_err(dev, "xclk frequency out of range: %d Hz\n",
    			sensor->xclk_freq);
    		return -EINVAL;
    	}*/
    	sensor->xclk_freq = OV5640_XCLK_MAX;
    	/* request optional power down pin */
    	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
    						    GPIOD_OUT_HIGH);
    	/* request optional reset pin */
    	sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
    						     GPIOD_OUT_HIGH);
    
    	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
    
    	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
    			    V4L2_SUBDEV_FL_HAS_EVENTS;
    	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
    	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
    	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
    	if (ret)
    		return ret;
    
    	ret = ov5640_get_regulators(sensor);
    	if (ret)
    		return ret;
    
    	mutex_init(&sensor->lock);
    
    
    
    
    	ret = ov5640_check_chip_id(sensor);
    //	if (ret)
    //		goto entity_cleanup;
    
    //	ret = ov5640_init_controls(sensor);
    //	if (ret)
    //		goto entity_cleanup;
    
    	ret = v4l2_async_register_subdev(&sensor->sd);
    	if (ret)
    		goto free_ctrls;
    
    	return 0;
    
    free_ctrls:
    	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
    entity_cleanup:
    	mutex_destroy(&sensor->lock);
    	media_entity_cleanup(&sensor->sd.entity);
    	return ret;
    }
    
    static int ov5640_remove(struct i2c_client *client)
    {
    	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
    
    	v4l2_async_unregister_subdev(&sensor->sd);
    	mutex_destroy(&sensor->lock);
    	media_entity_cleanup(&sensor->sd.entity);
    	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
    
    	return 0;
    }
    
    static const struct i2c_device_id ov5640_id[] = {
    	{"ov5640", 0},
    	{},
    };
    MODULE_DEVICE_TABLE(i2c, ov5640_id);
    
    static const struct of_device_id ov5640_dt_ids[] = {
    	{ .compatible = "ovti,ov5640" },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
    
    static struct i2c_driver ov5640_i2c_driver = {
    	.driver = {
    		.name  = "ov5640",
    		.of_match_table	= ov5640_dt_ids,
    	},
    	.id_table = ov5640_id,
    	.probe    = ov5640_probe,
    	.remove   = ov5640_remove,
    };
    
    module_i2c_driver(ov5640_i2c_driver);
    
    MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
    MODULE_LICENSE("GPL");

    // SPDX-License-Identifier: GPL-2.0
    /*
     * TI VIP capture driver
     *
     * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
     * David Griego, <dagriego@biglakesoftware.com>
     * Dale Farnsworth, <dale@farnsworth.org>
     * Nikhil Devshatwar, <nikhil.nd@ti.com>
     * Benoit Parrot, <bparrot@ti.com>
     */
    
    #include <linux/clk.h>
    #include <linux/delay.h>
    #include <linux/dma-mapping.h>
    #include <linux/err.h>
    #include <linux/fs.h>
    #include <linux/interrupt.h>
    #include <linux/io.h>
    #include <linux/ioctl.h>
    #include <linux/i2c.h>
    #include <linux/module.h>
    #include <linux/workqueue.h>
    #include <linux/platform_device.h>
    #include <linux/pm_runtime.h>
    #include <linux/sched.h>
    #include <linux/slab.h>
    #include <linux/mfd/syscon.h>
    #include <linux/regmap.h>
    
    #include <linux/pinctrl/consumer.h>
    #include <linux/of_device.h>
    #include <linux/of_graph.h>
    
    #include <media/v4l2-fwnode.h>
    #include <media/v4l2-async.h>
    
    #include "vip.h"
    
    #define VIP_MODULE_NAME "vip"
    #define VIP_INPUT_NAME "Vin0"
    
    static int debug;
    module_param(debug, int, 0644);
    MODULE_PARM_DESC(debug, "debug level (0-8)");
    
    /*
     * Minimum and maximum frame sizes
     */
    #define MIN_W		128
    #define MIN_H		128
    #define MAX_W		2048
    #define MAX_H		1536
    
    /*
     * Required alignments
     */
    #define S_ALIGN		0 /* multiple of 1 */
    #define H_ALIGN		1 /* multiple of 2 */
    #define W_ALIGN		1 /* multiple of 2 */
    #define L_ALIGN		7 /* multiple of 128, line stride, 16 bytes */
    
    /*
     * Need a descriptor entry for each of up to 15 outputs,
     * and up to 2 control transfers.
     */
    #define VIP_DESC_LIST_SIZE	(17 * sizeof(struct vpdma_dtd))
    
    #define vip_dbg(level, dev, fmt, arg...)	\
    		v4l2_dbg(level, debug, dev, fmt, ##arg)
    #define vip_err(dev, fmt, arg...)	\
    		v4l2_err(dev, fmt, ##arg)
    #define vip_info(dev, fmt, arg...)	\
    		v4l2_info(dev, fmt, ##arg)
    
    #define CTRL_CORE_SMA_SW_1      0x534
    /*
     * The srce_info structure contains per-srce data.
     */
    struct vip_srce_info {
    	u8	base_channel;	/* the VPDMA channel nummber */
    	u8	vb_index;	/* input frame f, f-1, f-2 index */
    	u8	vb_part;	/* identifies section of co-planar formats */
    };
    
    #define VIP_VPDMA_FIFO_SIZE	2
    #define VIP_DROPQ_SIZE		3
    
    /*
     * Define indices into the srce_info tables
     */
    
    #define VIP_SRCE_MULT_PORT		0
    #define VIP_SRCE_MULT_ANC		1
    #define VIP_SRCE_LUMA		2
    #define VIP_SRCE_CHROMA		3
    #define VIP_SRCE_RGB		4
    
    static struct vip_srce_info srce_info[5] = {
    	[VIP_SRCE_MULT_PORT] = {
    		.base_channel	= VIP1_CHAN_NUM_MULT_PORT_A_SRC0,
    		.vb_index	= 0,
    		.vb_part	= VIP_CHROMA,
    	},
    	[VIP_SRCE_MULT_ANC] = {
    		.base_channel	= VIP1_CHAN_NUM_MULT_ANC_A_SRC0,
    		.vb_index	= 0,
    		.vb_part	= VIP_LUMA,
    	},
    	[VIP_SRCE_LUMA] = {
    		.base_channel	= VIP1_CHAN_NUM_PORT_A_LUMA,
    		.vb_index	= 1,
    		.vb_part	= VIP_LUMA,
    	},
    	[VIP_SRCE_CHROMA] = {
    		.base_channel	= VIP1_CHAN_NUM_PORT_A_CHROMA,
    		.vb_index	= 1,
    		.vb_part	= VIP_CHROMA,
    	},
    	[VIP_SRCE_RGB] = {
    		.base_channel	= VIP1_CHAN_NUM_PORT_A_RGB,
    		.vb_part	= VIP_LUMA,
    	},
    };
    
    static struct vip_fmt vip_formats[VIP_MAX_ACTIVE_FMT] = {
    	{
    		.fourcc		= V4L2_PIX_FMT_UYVY,
    		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
    		.colorspace	= V4L2_COLORSPACE_BT2020,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16],
    				  },
    	},
    /*	{
    		.fourcc		= V4L2_PIX_FMT_NV12,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 1,
    		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
    				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
    				  },
    	},*/
    	{
    		.fourcc		= V4L2_PIX_FMT_UYVY,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_YUYV,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_VYUY,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_YVYU,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_RGB24,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_RGB32,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_BGR24,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_BGR32,
    		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_RGB24,
    		.code		= MEDIA_BUS_FMT_RGB888_1X24,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_RGB32,
    		.code		= MEDIA_BUS_FMT_ARGB8888_1X32,
    		.colorspace	= V4L2_COLORSPACE_SRGB,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_SBGGR8,
    		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_SGBRG8,
    		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_SGRBG8,
    		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
    				  },
    	},
    	{
    		.fourcc		= V4L2_PIX_FMT_SRGGB8,
    		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
    				  },
    	},
    	{
    		/* V4L2 currently only defines one 16 bit variant */
    		.fourcc		= V4L2_PIX_FMT_SBGGR16,
    		.code		= MEDIA_BUS_FMT_SBGGR16_1X16,
    		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
    		.coplanar	= 0,
    		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16],
    				  },
    	},
    //	{
    //		/* V4L2 currently only defines one 16 bit variant */
    //		.fourcc		= V4L2_PIX_FMT_YUYV,
    //		.code		= MEDIA_BUS_FMT_YUYV8_1X16,
    //		.colorspace	= V4L2_COLORSPACE_BT2020,
    //		.coplanar	= 0,
    //		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16],
    //				  },
    //	},
    };
    
    /*  Print Four-character-code (FOURCC) */
    static char *fourcc_to_str(u32 fmt)
    {
    	static char code[5];
    
    	code[0] = (unsigned char)(fmt & 0xff);
    	code[1] = (unsigned char)((fmt >> 8) & 0xff);
    	code[2] = (unsigned char)((fmt >> 16) & 0xff);
    	code[3] = (unsigned char)((fmt >> 24) & 0xff);
    	code[4] = '\0';
    
    	return code;
    }
    
    /*
     * Find our format description corresponding to the passed v4l2_format
     */
    
    static struct vip_fmt *find_port_format_by_pix(struct vip_port *port,
    					       u32 pixelformat)
    {
    	struct vip_fmt *fmt;
    	unsigned int k;
    
    	for (k = 0; k < port->num_active_fmt; k++) {
    		fmt = port->active_fmt[k];
    		if (fmt->fourcc == pixelformat)
    			return fmt;
    	}
    
    	return NULL;
    }
    
    static struct vip_fmt *find_port_format_by_code(struct vip_port *port,
    						u32 code)
    {
    	struct vip_fmt *fmt;
    	unsigned int k;
    
    	for (k = 0; k < port->num_active_fmt; k++) {
    		fmt = port->active_fmt[k];
    		if (fmt->code == code)
    			return fmt;
    	}
    
    	return NULL;
    }
    
    inline struct vip_port *notifier_to_vip_port(struct v4l2_async_notifier *n)
    {
    	return container_of(n, struct vip_port, notifier);
    }
    
    static bool vip_is_fmt_yuv(u32 fourcc)
    {
    	if (fourcc == V4L2_PIX_FMT_NV12 ||
    	    fourcc == V4L2_PIX_FMT_UYVY ||
    	    fourcc == V4L2_PIX_FMT_YUYV ||
    	    fourcc == V4L2_PIX_FMT_VYUY ||
    	    fourcc == V4L2_PIX_FMT_YVYU)
    		return true;
    
    	return false;
    }
    
    static bool vip_is_fmt_rgb(u32 fourcc)
    {
    	if (fourcc == V4L2_PIX_FMT_RGB24 ||
    	    fourcc == V4L2_PIX_FMT_BGR24 ||
    	    fourcc == V4L2_PIX_FMT_RGB32 ||
    	    fourcc == V4L2_PIX_FMT_BGR32)
    		return true;
    
    	return false;
    }
    
    static bool vip_is_mbuscode_yuv(u32 code)
    {
    	return ((code & 0xFF00) == 0x2000);
    }
    
    static bool vip_is_mbuscode_rgb(u32 code)
    {
    	return ((code & 0xFF00) == 0x1000);
    }
    
    static bool vip_is_mbuscode_raw(u32 code)
    {
    	return ((code & 0xFF00) == 0x3000);
    }
    
    static enum  v4l2_colorspace vip_fourcc_to_colorspace(u32 fourcc)
    {
    	if (vip_is_fmt_rgb(fourcc))
    		return V4L2_COLORSPACE_SRGB;
    
    	return V4L2_COLORSPACE_SMPTE170M;
    }
    
    static enum  v4l2_colorspace vip_code_to_colorspace(u32 code)
    {
    	if (vip_is_mbuscode_rgb(code))
    		return V4L2_COLORSPACE_SRGB;
    
    	return V4L2_COLORSPACE_SMPTE170M;
    }
    
    static enum vip_csc_state vip_csc_direction(u32 src_code, u32 dst_fourcc)
    {
    	if (vip_is_mbuscode_yuv(src_code) && vip_is_fmt_rgb(dst_fourcc))
    		return VIP_CSC_Y2R;
    	else if (vip_is_mbuscode_rgb(src_code) && vip_is_fmt_yuv(dst_fourcc))
    		return VIP_CSC_R2Y;
    	else
    		return VIP_CSC_NA;
    }
    
    /*
     * port flag bits
     */
    #define FLAG_FRAME_1D		BIT(0)
    #define FLAG_EVEN_LINE_SKIP	BIT(1)
    #define FLAG_ODD_LINE_SKIP	BIT(2)
    #define FLAG_MODE_TILED		BIT(3)
    #define FLAG_INTERLACED		BIT(4)
    #define FLAG_MULTIPLEXED	BIT(5)
    #define FLAG_MULT_PORT		BIT(6)
    #define FLAG_MULT_ANC		BIT(7)
    
    /*
     * Function prototype declarations
     */
    static int alloc_port(struct vip_dev *, int);
    static void free_port(struct vip_port *);
    static int vip_setup_parser(struct vip_port *port);
    static int vip_setup_scaler(struct vip_stream *stream);
    static void vip_enable_parser(struct vip_port *port, bool on);
    static void vip_reset_parser(struct vip_port *port, bool on);
    static void vip_parser_stop_imm(struct vip_port *port, bool on);
    static void stop_dma(struct vip_stream *stream, bool clear_list);
    static int vip_load_vpdma_list_fifo(struct vip_stream *stream);
    static inline bool is_scaler_available(struct vip_port *port);
    static inline bool allocate_scaler(struct vip_port *port);
    static inline void free_scaler(struct vip_port *port);
    static bool is_csc_available(struct vip_port *port);
    static bool allocate_csc(struct vip_port *port,
    				enum vip_csc_state csc_direction);
    static void free_csc(struct vip_port *port);
    
    #define reg_read(dev, offset) ioread32(dev->base + offset)
    #define reg_write(dev, offset, val) iowrite32(val, dev->base + offset)
    
    /*
     * Insert a masked field into a 32-bit field
     */
    static void insert_field(u32 *valp, u32 field, u32 mask, int shift)
    {
    	u32 val = *valp;
    
    	val &= ~(mask << shift);
    	val |= (field & mask) << shift;
    	*valp = val;
    }
    
    /*
     * DMA address/data block for the shadow registers
     */
    struct vip_mmr_adb {
    	struct vpdma_adb_hdr	sc_hdr0;
    	u32			sc_regs0[7];
    	u32			sc_pad0[1];
    	struct vpdma_adb_hdr	sc_hdr8;
    	u32			sc_regs8[6];
    	u32			sc_pad8[2];
    	struct vpdma_adb_hdr	sc_hdr17;
    	u32			sc_regs17[9];
    	u32			sc_pad17[3];
    	struct vpdma_adb_hdr	csc_hdr;
    	u32			csc_regs[6];
    	u32			csc_pad[2];
    };
    
    #define GET_OFFSET_TOP(port, obj, reg)	\
    	((obj)->res->start - port->dev->res->start + reg)
    
    #define VIP_SET_MMR_ADB_HDR(port, hdr, regs, offset_a)	\
    	VPDMA_SET_MMR_ADB_HDR(port->mmr_adb, vip_mmr_adb, hdr, regs, offset_a)
    
    /*
     * Set the headers for all of the address/data block structures.
     */
    static void init_adb_hdrs(struct vip_port *port)
    {
    	VIP_SET_MMR_ADB_HDR(port, sc_hdr0, sc_regs0,
    			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC0));
    	VIP_SET_MMR_ADB_HDR(port, sc_hdr8, sc_regs8,
    			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC8));
    	VIP_SET_MMR_ADB_HDR(port, sc_hdr17, sc_regs17,
    			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC17));
    	VIP_SET_MMR_ADB_HDR(port, csc_hdr, csc_regs,
    			    GET_OFFSET_TOP(port, port->dev->csc, CSC_CSC00));
    
    };
    
    /*
     * These represent the module resets bit for slice 1
     * Upon detecting slice2 we simply left shift by 1
     */
    #define VIP_DP_RST	BIT(16)
    #define VIP_PARSER_RST	BIT(18)
    #define VIP_CSC_RST	BIT(20)
    #define VIP_SC_RST	BIT(22)
    #define VIP_DS0_RST	BIT(25)
    #define VIP_DS1_RST	BIT(27)
    
    static void vip_module_reset(struct vip_dev *dev, uint32_t module, bool on)
    {
    	u32 val = 0;
    
    	val = reg_read(dev, VIP_CLK_RESET);
    
    	if (dev->slice_id == VIP_SLICE2)
    		module <<= 1;
    
    	if (on)
    		val |= module;
    	else
    		val &= ~module;
    
    	reg_write(dev, VIP_CLK_RESET, val);
    }
    
    /*
     * Enable or disable the VIP clocks
     */
    static void vip_set_clock_enable(struct vip_dev *dev, bool on)
    {
    	u32 val = 0;
    
    	val = reg_read(dev, VIP_CLK_ENABLE);
    	if (on) {
    		val |= VIP_VPDMA_CLK_ENABLE;
    		if (dev->slice_id == VIP_SLICE1)
    			val |= VIP_VIP1_DATA_PATH_CLK_ENABLE;
    		else
    			val |= VIP_VIP2_DATA_PATH_CLK_ENABLE;
    	} else {
    		if (dev->slice_id == VIP_SLICE1)
    			val &= ~VIP_VIP1_DATA_PATH_CLK_ENABLE;
    		else
    			val &= ~VIP_VIP2_DATA_PATH_CLK_ENABLE;
    
    		/* Both VIP are disabled then shutdown VPDMA also */
    		if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE |
    			     VIP_VIP2_DATA_PATH_CLK_ENABLE)))
    			val = 0;
    	}
    
    	reg_write(dev, VIP_CLK_ENABLE, val);
    }
    
    /* This helper function is used to enable the clock early on to
     * enable vpdma firmware loading before the slice device are created
     */
    static void vip_shared_set_clock_enable(struct vip_shared *shared, bool on)
    {
    	u32 val = 0;
    
    	if (on)
    		val = VIP_VIP1_DATA_PATH_CLK_ENABLE | VIP_VPDMA_CLK_ENABLE;
    
    	reg_write(shared, VIP_CLK_ENABLE, val);
    }
    
    static void vip_top_reset(struct vip_dev *dev)
    {
    	u32 val = 0;
    
    	val = reg_read(dev, VIP_CLK_RESET);
    
    	if (dev->slice_id == VIP_SLICE1)
    		insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
    			     VIP_VIP1_DATA_PATH_RESET_SHIFT);
    	else
    		insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
    			     VIP_VIP2_DATA_PATH_RESET_SHIFT);
    
    	reg_write(dev, VIP_CLK_RESET, val);
    
    	usleep_range(200, 250);
    
    	val = reg_read(dev, VIP_CLK_RESET);
    
    	if (dev->slice_id == VIP_SLICE1)
    		insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
    			     VIP_VIP1_DATA_PATH_RESET_SHIFT);
    	else
    		insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
    			     VIP_VIP2_DATA_PATH_RESET_SHIFT);
    	reg_write(dev, VIP_CLK_RESET, val);
    }
    
    static void vip_top_vpdma_reset(struct vip_shared *shared)
    {
    	u32 val;
    
    	val = reg_read(shared, VIP_CLK_RESET);
    	insert_field(&val, 1, VIP_VPDMA_CLK_RESET_MASK,
    		     VIP_VPDMA_CLK_RESET_SHIFT);
    	reg_write(shared, VIP_CLK_RESET, val);
    
    	usleep_range(200, 250);
    
    	val = reg_read(shared, VIP_CLK_RESET);
    	insert_field(&val, 0, VIP_VPDMA_CLK_RESET_MASK,
    		     VIP_VPDMA_CLK_RESET_SHIFT);
    	reg_write(shared, VIP_CLK_RESET, val);
    }
    
    static void vip_set_pclk_invert(struct vip_port *port)
    {
    	u32 offset;
    	/*
    	 * When the VIP parser is configured to so that the pixel clock
    	 * is to be sampled at falling edge, the pixel clock needs to be
    	 * inverted before it is given to the VIP module. This is done
    	 * by setting a bit in the CTRL_CORE_SMA_SW1 register.
    	 */
    
    	if (port->dev->instance_id == VIP_INSTANCE1) {
    		offset = 0 + 2 * port->port_id + port->dev->slice_id;
    	} else if (port->dev->instance_id == VIP_INSTANCE2) {
    		offset = 4 + 2 * port->port_id + port->dev->slice_id;
    	} else if (port->dev->instance_id == VIP_INSTANCE3) {
    		offset = 10 - port->dev->slice_id;
    	} else {
    		vip_err(port, "%s: VIP instance id out of range...\n",
    			__func__);
    		return;
    	}
    
    	if (port->dev->syscon_pol)
    		regmap_update_bits(port->dev->syscon_pol,
    				   port->dev->syscon_pol_offset,
    				   1 << offset, 1 << offset);
    }
    
    #define VIP_PARSER_PORT(p)	(VIP_PARSER_PORTA_0 + (p * 0x8U))
    #define VIP_PARSER_EXTRA_PORT(p)	(VIP_PARSER_PORTA_1 + (p * 0x8U))
    #define VIP_PARSER_CROP_H_PORT(p)	(VIP_PARSER_PORTA_EXTRA4 + (p * 0x10U))
    #define VIP_PARSER_CROP_V_PORT(p)	(VIP_PARSER_PORTA_EXTRA5 + (p * 0x10U))
    #define VIP_PARSER_STOP_IMM_PORT(p)	(VIP_PARSER_PORTA_EXTRA6 + (p * 0x4U))
    
    static void vip_set_data_interface(struct vip_port *port,
    				   enum data_interface_modes mode)
    {
    	u32 val = 0;
    
    	insert_field(&val, mode, VIP_DATA_INTERFACE_MODE_MASK,
    		     VIP_DATA_INTERFACE_MODE_SHFT);
    
    	reg_write(port->dev->parser, VIP_PARSER_MAIN_CFG, val);
    }
    
    static void vip_set_slice_path(struct vip_dev *dev,
    			       enum data_path_select data_path, u32 path_val)
    {
    	u32 val = 0;
    	int data_path_reg;
    
    	vip_dbg(3, dev, "%s:\n", __func__);
    
    	data_path_reg = VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id;
    
    	switch (data_path) {
    	case ALL_FIELDS_DATA_SELECT:
    		val |= path_val;
    		break;
    	case VIP_CSC_SRC_DATA_SELECT:
    		insert_field(&val, path_val, VIP_CSC_SRC_SELECT_MASK,
    			     VIP_CSC_SRC_SELECT_SHFT);
    		break;
    	case VIP_SC_SRC_DATA_SELECT:
    		insert_field(&val, path_val, VIP_SC_SRC_SELECT_MASK,
    			     VIP_SC_SRC_SELECT_SHFT);
    		break;
    	case VIP_RGB_SRC_DATA_SELECT:
    		val |= (path_val) ? VIP_RGB_SRC_SELECT : 0;
    		break;
    	case VIP_RGB_OUT_LO_DATA_SELECT:
    		val |= (path_val) ? VIP_RGB_OUT_LO_SRC_SELECT : 0;
    		break;
    	case VIP_RGB_OUT_HI_DATA_SELECT:
    		val |= (path_val) ? VIP_RGB_OUT_HI_SRC_SELECT : 0;
    		break;
    	case VIP_CHR_DS_1_SRC_DATA_SELECT:
    		insert_field(&val, path_val, VIP_DS1_SRC_SELECT_MASK,
    			     VIP_DS1_SRC_SELECT_SHFT);
    		break;
    	case VIP_CHR_DS_2_SRC_DATA_SELECT:
    		insert_field(&val, path_val, VIP_DS2_SRC_SELECT_MASK,
    			     VIP_DS2_SRC_SELECT_SHFT);
    		break;
    	case VIP_MULTI_CHANNEL_DATA_SELECT:
    		val |= (path_val) ? VIP_MULTI_CHANNEL_SELECT : 0;
    		break;
    	case VIP_CHR_DS_1_DATA_BYPASS:
    		val |= (path_val) ? VIP_DS1_BYPASS : 0;
    		break;
    	case VIP_CHR_DS_2_DATA_BYPASS:
    		val |= (path_val) ? VIP_DS2_BYPASS : 0;
    		break;
    	default:
    		vip_err(dev, "%s: data_path 0x%x is not valid\n",
    			__func__, data_path);
    		return;
    	}
    	insert_field(&val, data_path, VIP_DATAPATH_SELECT_MASK,
    		     VIP_DATAPATH_SELECT_SHFT);
    	reg_write(dev, data_path_reg, val);
    	vip_dbg(3, dev, "%s: DATA_PATH_SELECT(%08X): %08X\n", __func__,
    		data_path_reg, reg_read(dev, data_path_reg));
    }
    
    /*
     * Return the vip_stream structure for a given struct file
     */
    static inline struct vip_stream *file2stream(struct file *file)
    {
    	return video_drvdata(file);
    }
    
    /*
     * Append a destination descriptor to the current descriptor list,
     * setting up dma to the given srce.
     */
    static int add_out_dtd(struct vip_stream *stream, int srce_type)
    {
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	struct vip_srce_info *sinfo = &srce_info[srce_type];
    	struct v4l2_rect *c_rect = &port->c_rect;
    	struct vip_fmt *fmt = port->fmt;
    	int channel, plane = 0;
    	int max_width, max_height;
    	dma_addr_t dma_addr;
    	u32 flags;
    	u32 width = stream->width;
    
    	channel = sinfo->base_channel;
    
    	switch (srce_type) {
    	case VIP_SRCE_MULT_PORT:
    	case VIP_SRCE_MULT_ANC:
    		if (port->port_id == VIP_PORTB)
    			channel += VIP_CHAN_MULT_PORTB_OFFSET;
    		channel += stream->stream_id;
    		flags = 0;
    		break;
    	case VIP_SRCE_CHROMA:
    		plane = 1;
    	case VIP_SRCE_LUMA:
    		if (port->port_id == VIP_PORTB) {
    			if (port->scaler && !port->fmt->coplanar)
    				/*
    				 * In this case Port A Chroma channel
    				 * is used to carry Port B scaled YUV422
    				 */
    				channel += 1;
    			else
    				channel += VIP_CHAN_YUV_PORTB_OFFSET;
    		}
    		flags = port->flags;
    		break;
    	case VIP_SRCE_RGB:
    		if ((port->port_id == VIP_PORTB) ||
    		    ((port->port_id == VIP_PORTA) &&
    		     (port->csc == VIP_CSC_NA) &&
    		     vip_is_fmt_rgb(port->fmt->fourcc)))
    			/*
    			 * RGB sensor only connect to Y_LO
    			 * channel i.e. port B channel.
    			 */
    			channel += VIP_CHAN_RGB_PORTB_OFFSET;
    		flags = port->flags;
    		break;
    	default:
    		vip_err(stream, "%s: srce_type 0x%x is not valid\n",
    			__func__, srce_type);
    		return -1;
    	}
    
    	if (dev->slice_id == VIP_SLICE2)
    		channel += VIP_CHAN_VIP2_OFFSET;
    
    	/* This is just for initialization purposes.
    	 * The actual dma_addr will be configured in vpdma_update_dma_addr
    	 */
    	dma_addr = 0;
    
    	if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
    		/*
    		 * Special case since we are faking a YUV422 16bit format
    		 * to have the vpdma perform the needed byte swap
    		 * we need to adjust the pixel width accordingly
    		 * otherwise the parser will attempt to collect more pixels
    		 * then available and the vpdma transfer will exceed the
    		 * allocated frame buffer.
    		 */
    		width >>= 1;
    		vip_dbg(1, stream, "%s: 8 bit raw detected, adjusting width to %d\n",
    			__func__, width);
    	}
    
    	/*
    	 * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1
    	 */
    
    	if (dev->slice_id == VIP_SLICE1) {
    		vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1,
    				   width, stream->height);
    
    		max_width = MAX_OUT_WIDTH_REG1;
    		max_height = MAX_OUT_HEIGHT_REG1;
    	} else {
    		vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2,
    				   width, stream->height);
    
    		max_width = MAX_OUT_WIDTH_REG2;
    		max_height = MAX_OUT_HEIGHT_REG2;
    	}
    
    	/*
    	 * Mark this channel to be cleared while cleaning up resources
    	 * This will make sure that an abort descriptor for this channel
    	 * would be submitted to VPDMA causing any ongoing  transaction to be
    	 * aborted and cleanup the VPDMA FSM for this channel
    	 */
    	stream->vpdma_channels[channel] = 1;
    
    	vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width,
    				  stream->bytesperline, c_rect,
    				  fmt->vpdma_fmt[plane], dma_addr,
    				  max_width, max_height, channel, flags);
    
    	return 0;
    }
    
    /*
     * add_stream_dtds - prepares and starts DMA for pending transfers
     */
    static void add_stream_dtds(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	int srce_type;
    
    	if (port->flags & FLAG_MULT_PORT)
    		srce_type = VIP_SRCE_MULT_PORT;
    	else if (port->flags & FLAG_MULT_ANC)
    		srce_type = VIP_SRCE_MULT_ANC;
    	else if (vip_is_fmt_rgb(port->fmt->fourcc))
    		srce_type = VIP_SRCE_RGB;
    	else
    		srce_type = VIP_SRCE_LUMA;
    
    	add_out_dtd(stream, srce_type);
    
    	if (srce_type == VIP_SRCE_LUMA && port->fmt->coplanar)
    		add_out_dtd(stream, VIP_SRCE_CHROMA);
    }
    
    #define PARSER_IRQ_MASK (VIP_PORTA_OUTPUT_FIFO_YUV | \
    			 VIP_PORTB_OUTPUT_FIFO_YUV)
    
    static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num)
    {
    	struct vip_parser_data *parser = dev->parser;
    	u32 reg_addr = VIP_INT0_ENABLE0_SET +
    			VIP_INTC_INTX_OFFSET * irq_num;
    	u32 irq_val = (1 << (list_num * 2)) |
    		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
    
    	/* Enable Parser Interrupt */
    	reg_write(parser, VIP_PARSER_FIQ_MASK, ~PARSER_IRQ_MASK);
    
    	reg_write(dev->shared, reg_addr, irq_val);
    
    	vpdma_enable_list_complete_irq(dev->shared->vpdma,
    				       irq_num, list_num, true);
    }
    
    static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num)
    {
    	struct vip_parser_data *parser = dev->parser;
    	u32 reg_addr = VIP_INT0_ENABLE0_CLR +
    			VIP_INTC_INTX_OFFSET * irq_num;
    	u32 irq_val = (1 << (list_num * 2)) |
    		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
    
    	/* Disable all Parser Interrupt */
    	reg_write(parser, VIP_PARSER_FIQ_MASK, 0xffffffff);
    
    	reg_write(dev->shared, reg_addr, irq_val);
    
    	vpdma_enable_list_complete_irq(dev->shared->vpdma,
    				       irq_num, list_num, false);
    }
    
    static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num)
    {
    	struct vip_parser_data *parser = dev->parser;
    	u32 reg_addr = VIP_INT0_STATUS0_CLR +
    			VIP_INTC_INTX_OFFSET * irq_num;
    	u32 irq_val = (1 << (list_num * 2)) |
    		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
    
    	/* Clear all Parser Interrupt */
    	reg_write(parser, VIP_PARSER_FIQ_CLR, 0xffffffff);
    	reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
    
    	reg_write(dev->shared, reg_addr, irq_val);
    
    	vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
    }
    
    static void populate_desc_list(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	unsigned int list_length;
    
    	stream->desc_next = stream->desc_list.buf.addr;
    	add_stream_dtds(stream);
    
    	list_length = stream->desc_next - stream->desc_list.buf.addr;
    	vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    }
    
    /*
     * start_dma - adds descriptors to the dma list and submits them.
     * Should be called after a new vb is queued and on a vpdma list
     * completion interrupt.
     */
    static void start_dma(struct vip_stream *stream, struct vip_buffer *buf)
    {
    	struct vip_dev *dev = stream->port->dev;
    	struct vpdma_data *vpdma = dev->shared->vpdma;
    	int list_num = stream->list_num;
    	dma_addr_t dma_addr;
    	int drop_data;
    
    	if (vpdma_list_busy(vpdma, list_num)) {
    		vip_err(stream, "vpdma list busy, cannot post\n");
    		return;				/* nothing to do */
    	}
    
    	if (buf) {
    		dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
    		drop_data = 0;
    		vip_dbg(4, stream, "%s: vb2 buf idx:%d, dma_addr:%pad\n",
    			__func__, buf->vb.vb2_buf.index, &dma_addr);
    	} else {
    		dma_addr = 0;
    		drop_data = 1;
    		vip_dbg(4, stream, "%s: dropped\n", __func__);
    	}
    
    	vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
    			      dma_addr, stream->write_desc, drop_data, 0);
    
    	if (stream->port->fmt->coplanar) {
    		dma_addr += stream->bytesperline * stream->height;
    		vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
    				      dma_addr, stream->write_desc + 1,
    				      drop_data, 1);
    	}
    
    	vpdma_submit_descs(dev->shared->vpdma,
    			   &stream->desc_list, stream->list_num);
    }
    
    static void vip_schedule_next_buffer(struct vip_stream *stream)
    {
    	struct vip_dev *dev = stream->port->dev;
    	struct vip_buffer *buf;
    	unsigned long flags;
    
    	spin_lock_irqsave(&dev->slock, flags);
    	if (list_empty(&stream->vidq)) {
    		vip_dbg(4, stream, "Dropping frame\n");
    		if (list_empty(&stream->dropq)) {
    			vip_err(stream, "No dropq buffer left!");
    			spin_unlock_irqrestore(&dev->slock, flags);
    			return;
    		}
    		buf = list_entry(stream->dropq.next,
    				 struct vip_buffer, list);
    
    		buf->drop = true;
    		list_move_tail(&buf->list, &stream->post_bufs);
    		buf = NULL;
    	} else {
    		buf = list_entry(stream->vidq.next,
    				 struct vip_buffer, list);
    		buf->drop = false;
    		list_move_tail(&buf->list, &stream->post_bufs);
    		vip_dbg(4, stream, "added next buffer\n");
    	}
    
    	spin_unlock_irqrestore(&dev->slock, flags);
    	start_dma(stream, buf);
    }
    
    static void vip_process_buffer_complete(struct vip_stream *stream)
    {
    	struct vip_dev *dev = stream->port->dev;
    	struct vip_buffer *buf;
    	struct vb2_v4l2_buffer *vb = NULL;
    	unsigned long flags, fld;
    
    	buf = list_first_entry(&stream->post_bufs, struct vip_buffer, list);
    
    	if (stream->port->flags & FLAG_INTERLACED) {
    		vpdma_unmap_desc_buf(dev->shared->vpdma,
    				     &stream->desc_list.buf);
    
    		fld = dtd_get_field(stream->write_desc);
    		stream->field = fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
    
    		vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    	}
    
    	if (buf) {
    		vip_dbg(4, stream, "vip buffer complete 0x%x, 0x%x\n",
    			(unsigned int)buf, buf->drop);
    
    		vb = &buf->vb;
    		vb->field = stream->field;
    		vb->sequence = stream->sequence;
    		vb->vb2_buf.timestamp = ktime_get_ns();
    
    		if (buf->drop) {
    			spin_lock_irqsave(&dev->slock, flags);
    			list_move_tail(&buf->list, &stream->dropq);
    			spin_unlock_irqrestore(&dev->slock, flags);
    		} else {
    			spin_lock_irqsave(&dev->slock, flags);
    			list_del(&buf->list);
    			spin_unlock_irqrestore(&dev->slock, flags);
    			vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
    		}
    	} else {
    		vip_err(stream, "%s: buf is null!!!\n", __func__);
    		return;
    	}
    
    	stream->sequence++;
    }
    
    static int vip_reset_vpdma(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	struct vip_buffer *buf;
    	unsigned long flags;
    
    	stop_dma(stream, false);
    
    	spin_lock_irqsave(&dev->slock, flags);
    	/* requeue all active buffers in the opposite order */
    	while (!list_empty(&stream->post_bufs)) {
    		buf = list_last_entry(&stream->post_bufs,
    				      struct vip_buffer, list);
    		list_del(&buf->list);
    		if (buf->drop == 1) {
    			list_add_tail(&buf->list, &stream->dropq);
    			vip_dbg(4, stream, "requeueing drop buffer on dropq\n");
    		} else {
    			list_add(&buf->list, &stream->vidq);
    			vip_dbg(4, stream, "requeueing vb2 buf idx:%d on vidq\n",
    				buf->vb.vb2_buf.index);
    		}
    	}
    	spin_unlock_irqrestore(&dev->slock, flags);
    
    	/* Make sure the desc_list is unmapped */
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    
    	return 0;
    }
    
    static void vip_overflow_recovery_work(struct work_struct *work)
    {
    	struct vip_stream *stream = container_of(work, struct vip_stream,
    						 recovery_work);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    
    	vip_err(stream, "%s: Port %c\n", __func__,
    		port->port_id == VIP_PORTA ? 'A' : 'B');
    
    	disable_irqs(dev, dev->slice_id, stream->list_num);
    	clear_irqs(dev, dev->slice_id, stream->list_num);
    
    	/* 1.	Set VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMMEDIATELY */
    	/* 2.	Set VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMMEDIATELY */
    	vip_parser_stop_imm(port, 1);
    
    	/* 3.	Clear VIP_PORT_A[8] ENABLE */
    	/*
    	 * 4.	Set VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
    	 *      Set VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
    	 */
    	vip_enable_parser(port, false);
    
    	/* 5.	Set VIP_PORT_A[23] SW_RESET */
    	vip_reset_parser(port, 1);
    
    	/*
    	 * 6.	Reset other VIP modules
    	 *	For each module used downstream of VIP_PARSER, write 1 to the
    	 *      bit location of the VIP_CLKC_RST register which is connected
    	 *      to VIP_PARSER
    	 */
    	vip_module_reset(dev, VIP_DP_RST, true);
    
    	usleep_range(200, 250);
    
    	/*
    	 * 7.	Abort VPDMA channels
    	 *	Write to list attribute to stop list 0
    	 *	Write to list address register location of abort list
    	 *	Write to list attribute register list 0 and size of abort list
    	 */
    	vip_reset_vpdma(stream);
    
    	/* 8.	Clear VIP_PORT_A[23] SW_RESET */
    	vip_reset_parser(port, 0);
    
    	/*
    	 * 9.	Un-reset other VIP modules
    	 *	For each module used downstream of VIP_PARSER, write 0 to
    	 *	the bit location of the VIP_CLKC_RST register which is
    	 *	connected to VIP_PARSER
    	 */
    	vip_module_reset(dev, VIP_DP_RST, false);
    
    	/* 10.	(Delay) */
    	/* 11.	SC coeff downloaded (if VIP_SCALER is being used) */
    	vip_setup_scaler(stream);
    
    	/* 12.	(Delay) */
    		/* the above are not needed here yet */
    
    	populate_desc_list(stream);
    	stream->num_recovery++;
    	if (stream->num_recovery < 5) {
    		/* Reload the vpdma */
    		vip_load_vpdma_list_fifo(stream);
    
    		enable_irqs(dev, dev->slice_id, stream->list_num);
    		vip_schedule_next_buffer(stream);
    
    		/* 13.	Clear VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMM */
    		/* 14.	Clear VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMM */
    
    		vip_parser_stop_imm(port, 0);
    
    		/* 15.	Set VIP_PORT_A[8] ENABLE */
    		/*
    		 * 16.	Clear VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
    		 *	Clear VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
    		 */
    		vip_enable_parser(port, true);
    	} else {
    		vip_err(stream, "%s: num_recovery limit exceeded leaving disabled\n",
    			__func__);
    	}
    }
    
    static void handle_parser_irqs(struct vip_dev *dev)
    {
    	struct vip_parser_data *parser = dev->parser;
    	struct vip_port *porta = dev->ports[VIP_PORTA];
    	struct vip_port *portb = dev->ports[VIP_PORTB];
    	struct vip_stream *stream = NULL;
    	u32 irq_stat = reg_read(parser, VIP_PARSER_FIQ_STATUS);
    	int i;
    
    	vip_dbg(3, dev, "%s: FIQ_STATUS: 0x%08x\n", __func__, irq_stat);
    
    	/* Clear all Parser Interrupt */
    	reg_write(parser, VIP_PARSER_FIQ_CLR, irq_stat);
    	reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
    
    	if (irq_stat & VIP_PORTA_VDET)
    		vip_dbg(3, dev, "VIP_PORTA_VDET\n");
    	if (irq_stat & VIP_PORTB_VDET)
    		vip_dbg(3, dev, "VIP_PORTB_VDET\n");
    	if (irq_stat & VIP_PORTA_ASYNC_FIFO_OF)
    		vip_err(dev, "VIP_PORTA_ASYNC_FIFO_OF\n");
    	if (irq_stat & VIP_PORTB_ASYNC_FIFO_OF)
    		vip_err(dev, "VIP_PORTB_ASYNC_FIFO_OF\n");
    	if (irq_stat & VIP_PORTA_OUTPUT_FIFO_YUV)
    		vip_err(dev, "VIP_PORTA_OUTPUT_FIFO_YUV\n");
    	if (irq_stat & VIP_PORTA_OUTPUT_FIFO_ANC)
    		vip_err(dev, "VIP_PORTA_OUTPUT_FIFO_ANC\n");
    	if (irq_stat & VIP_PORTB_OUTPUT_FIFO_YUV)
    		vip_err(dev, "VIP_PORTB_OUTPUT_FIFO_YUV\n");
    	if (irq_stat & VIP_PORTB_OUTPUT_FIFO_ANC)
    		vip_err(dev, "VIP_PORTB_OUTPUT_FIFO_ANC\n");
    	if (irq_stat & VIP_PORTA_CONN)
    		vip_dbg(3, dev, "VIP_PORTA_CONN\n");
    	if (irq_stat & VIP_PORTA_DISCONN)
    		vip_dbg(3, dev, "VIP_PORTA_DISCONN\n");
    	if (irq_stat & VIP_PORTB_CONN)
    		vip_dbg(3, dev, "VIP_PORTB_CONN\n");
    	if (irq_stat & VIP_PORTB_DISCONN)
    		vip_dbg(3, dev, "VIP_PORTB_DISCONN\n");
    	if (irq_stat & VIP_PORTA_SRC0_SIZE)
    		vip_dbg(3, dev, "VIP_PORTA_SRC0_SIZE\n");
    	if (irq_stat & VIP_PORTB_SRC0_SIZE)
    		vip_dbg(3, dev, "VIP_PORTB_SRC0_SIZE\n");
    	if (irq_stat & VIP_PORTA_YUV_PROTO_VIOLATION)
    		vip_dbg(3, dev, "VIP_PORTA_YUV_PROTO_VIOLATION\n");
    	if (irq_stat & VIP_PORTA_ANC_PROTO_VIOLATION)
    		vip_dbg(3, dev, "VIP_PORTA_ANC_PROTO_VIOLATION\n");
    	if (irq_stat & VIP_PORTB_YUV_PROTO_VIOLATION)
    		vip_dbg(3, dev, "VIP_PORTB_YUV_PROTO_VIOLATION\n");
    	if (irq_stat & VIP_PORTB_ANC_PROTO_VIOLATION)
    		vip_dbg(3, dev, "VIP_PORTB_ANC_PROTO_VIOLATION\n");
    	if (irq_stat & VIP_PORTA_CFG_DISABLE_COMPLETE)
    		vip_dbg(3, dev, "VIP_PORTA_CFG_DISABLE_COMPLETE\n");
    	if (irq_stat & VIP_PORTB_CFG_DISABLE_COMPLETE)
    		vip_dbg(3, dev, "VIP_PORTB_CFG_DISABLE_COMPLETE\n");
    
    	if (irq_stat & (VIP_PORTA_ASYNC_FIFO_OF |
    			VIP_PORTA_OUTPUT_FIFO_YUV |
    			VIP_PORTA_OUTPUT_FIFO_ANC)) {
    		for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
    			if (porta->cap_streams[i] &&
    			    porta->cap_streams[i]->port->port_id ==
    			    porta->port_id) {
    				stream = porta->cap_streams[i];
    				break;
    			}
    		}
    		if (stream) {
    			disable_irqs(dev, dev->slice_id,
    				     stream->list_num);
    			schedule_work(&stream->recovery_work);
    			return;
    		}
    	}
    	if (irq_stat & (VIP_PORTB_ASYNC_FIFO_OF |
    			VIP_PORTB_OUTPUT_FIFO_YUV |
    			VIP_PORTB_OUTPUT_FIFO_ANC)) {
    		for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
    			if (portb->cap_streams[i] &&
    			    portb->cap_streams[i]->port->port_id ==
    			    portb->port_id) {
    				stream = portb->cap_streams[i];
    				break;
    			}
    		}
    		if (stream) {
    			disable_irqs(dev, dev->slice_id,
    				     stream->list_num);
    			schedule_work(&stream->recovery_work);
    			return;
    		}
    	}
    }
    
    static irqreturn_t vip_irq(int irq_vip, void *data)
    {
    	struct vip_dev *dev = (struct vip_dev *)data;
    	struct vpdma_data *vpdma;
    	struct vip_stream *stream;
    	int list_num;
    	int irq_num = dev->slice_id;
    	u32 irqst, irqst_saved, reg_addr;
    
    	if (!dev->shared)
    		return IRQ_HANDLED;
    
    	vpdma = dev->shared->vpdma;
    	reg_addr = VIP_INT0_STATUS0 +
    			VIP_INTC_INTX_OFFSET * irq_num;
    	irqst_saved = reg_read(dev->shared, reg_addr);
    	irqst = irqst_saved;
    
    	vip_dbg(8, dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n",
    		irq_vip, irq_num, irqst);
    	if (irqst) {
    		if (irqst & (VIP_VIP1_PARSER_INT << (irq_num * 1))) {
    			irqst &= ~(VIP_VIP1_PARSER_INT << (irq_num * 1));
    			handle_parser_irqs(dev);
    		}
    
    		for (list_num = 0; irqst && (list_num < 8);  list_num++) {
    			/* Check for LIST_COMPLETE IRQ */
    			if (!(irqst & (1 << list_num * 2)))
    				continue;
    
    			vip_dbg(8, dev, "IRQ %d: handling LIST%d_COMPLETE\n",
    				irq_num, list_num);
    
    			stream = vpdma_hwlist_get_priv(vpdma, list_num);
    			if (!stream || stream->list_num != list_num) {
    				vip_err(dev, "IRQ occurred for unused list");
    				continue;
    			}
    
    			vpdma_clear_list_stat(vpdma, irq_num, list_num);
    
    			vip_process_buffer_complete(stream);
    
    			vip_schedule_next_buffer(stream);
    
    			irqst &= ~((1 << list_num * 2));
    		}
    	}
    
    	/* Acknowledge that we are done with all interrupts */
    	reg_write(dev->shared, VIP_INTC_E0I, 1 << irq_num);
    
    	/* Clear handled events from status register */
    	reg_addr = VIP_INT0_STATUS0_CLR +
    		   VIP_INTC_INTX_OFFSET * irq_num;
    	reg_write(dev->shared, reg_addr, irqst_saved);
    
    	return IRQ_HANDLED;
    }
    
    /*
     * video ioctls
     */
    static int vip_querycap(struct file *file, void *priv,
    			struct v4l2_capability *cap)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	u32 vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
    
    	strlcpy(cap->driver, VIP_MODULE_NAME, sizeof(cap->driver));
    	strlcpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card));
    	snprintf(cap->bus_info, sizeof(cap->bus_info),
    		 "platform:vip%1d:vin%1d%c:stream%1d", dev->instance_id, vin_id,
    		 port->port_id == VIP_PORTA ? 'a' : 'b', stream->stream_id);
    	cap->device_caps  = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
    			    V4L2_CAP_READWRITE;
    	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
    	return 0;
    }
    
    static int vip_enuminput(struct file *file, void *priv,
    			 struct v4l2_input *inp)
    {
    	struct vip_stream *stream = file2stream(file);
    
    	if (inp->index)
    		return -EINVAL;
    
    	inp->type = V4L2_INPUT_TYPE_CAMERA;
    	inp->std = stream->vfd->tvnorms;
    	sprintf(inp->name, "camera %u", stream->vfd->num);
    
    	return 0;
    }
    
    static int vip_g_input(struct file *file, void *priv, unsigned int *i)
    {
    	*i = 0;
    	return 0;
    }
    
    static int vip_s_input(struct file *file, void *priv, unsigned int i)
    {
    	if (i != 0)
    		return -EINVAL;
    	return 0;
    }
    
    static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    
    	*std = stream->vfd->tvnorms;
    	v4l2_subdev_call(port->subdev, video, querystd, std);
    	vip_dbg(1, stream, "querystd: 0x%lx\n", (unsigned long)*std);
    	return 0;
    }
    
    static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    
    	*std = stream->vfd->tvnorms;
    	v4l2_subdev_call(port->subdev, video, g_std_output, std);
    	vip_dbg(1, stream, "g_std: 0x%lx\n", (unsigned long)*std);
    
    	return 0;
    }
    
    static int vip_s_std(struct file *file, void *fh, v4l2_std_id std)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    
    	vip_dbg(1, stream, "s_std: 0x%lx\n", (unsigned long)std);
    
    	if (!(std & stream->vfd->tvnorms)) {
    		vip_dbg(1, stream, "s_std after check: 0x%lx\n",
    			(unsigned long)std);
    		return -EINVAL;
    	}
    
    	v4l2_subdev_call(port->subdev, video, s_std_output, std);
    	return 0;
    }
    
    static int vip_enum_fmt_vid_cap(struct file *file, void *priv,
    				struct v4l2_fmtdesc *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct vip_fmt *fmt;
    
    	vip_dbg(3, stream, "enum_fmt index:%d\n", f->index);
    	if (f->index >= port->num_active_fmt)
    		return -EINVAL;
    
    	fmt = port->active_fmt[f->index];
    
    	f->pixelformat = fmt->fourcc;
    	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	vip_dbg(3, stream, "enum_fmt fourcc:%s\n",
    		fourcc_to_str(f->pixelformat));
    
    	return 0;
    }
    
    static int vip_enum_framesizes(struct file *file, void *priv,
    			       struct v4l2_frmsizeenum *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct vip_fmt *fmt;
    	struct v4l2_subdev_frame_size_enum fse;
    	int ret;
    
    	fmt = find_port_format_by_pix(port, f->pixel_format);
    	if (!fmt)
    		return -EINVAL;
    
    	fse.index = f->index;
    	fse.pad = 0;
    	fse.code = fmt->code;
    
    	ret = v4l2_subdev_call(port->subdev, pad, enum_frame_size, NULL, &fse);
    	if (ret)
    		return -EINVAL;
    
    	vip_dbg(1, stream, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
    		__func__, fse.index, fse.code, fse.min_width, fse.max_width,
    		fse.min_height, fse.max_height);
    
    	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
    	f->discrete.width = fse.max_width;
    	f->discrete.height = fse.max_height;
    
    	return 0;
    }
    
    static int vip_enum_frameintervals(struct file *file, void *priv,
    				   struct v4l2_frmivalenum *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct vip_fmt *fmt;
    	struct v4l2_subdev_frame_interval_enum fie = {
    		.index = f->index,
    		.width = f->width,
    		.height = f->height,
    		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
    	};
    	int ret;
    
    	fmt = find_port_format_by_pix(port, f->pixel_format);
    	if (!fmt)
    		return -EINVAL;
    
    	fie.code = fmt->code;
    	ret = v4l2_subdev_call(port->subdev, pad, enum_frame_interval,
    			       NULL, &fie);
    	if (ret)
    		return ret;
    	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
    	f->discrete = fie.interval;
    
    	return 0;
    }
    
    static int vip_g_parm(struct file *file, void *priv,
    		      struct v4l2_streamparm *parm)
    {
    	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    		return -EINVAL;
    
    	parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
    	parm->parm.capture.timeperframe.numerator = 1;
    	parm->parm.capture.timeperframe.denominator = 30;
    	parm->parm.capture.readbuffers  = 4;
    	return 0;
    }
    
    static int vip_s_parm(struct file *file, void *priv,
    		      struct v4l2_streamparm *parm)
    {
    	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    		return -EINVAL;
    
    	parm->parm.capture.timeperframe.numerator = 1;
    	parm->parm.capture.timeperframe.denominator = 30;
    	parm->parm.capture.readbuffers  = 4;
    
    	return 0;
    }
    
    static int vip_calc_format_size(struct vip_port *port,
    				struct vip_fmt *fmt,
    				struct v4l2_format *f)
    {
    	enum v4l2_field *field;
    	unsigned int stride;
    
    	if (!fmt) {
    		vip_dbg(2, port,
    			"no vip_fmt format provided!\n");
    		return -EINVAL;
    	}
    
    	field = &f->fmt.pix.field;
    	if (*field == V4L2_FIELD_ANY)
    		*field = V4L2_FIELD_NONE;
    	else if (V4L2_FIELD_NONE != *field && V4L2_FIELD_ALTERNATE != *field)
    		return -EINVAL;
    
    	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, W_ALIGN,
    			      &f->fmt.pix.height, MIN_H, MAX_H, H_ALIGN,
    			      S_ALIGN);
    
    	stride = f->fmt.pix.width * (fmt->vpdma_fmt[0]->depth >> 3);
    	if (stride > f->fmt.pix.bytesperline)
    		f->fmt.pix.bytesperline = stride;
    	f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
    					VPDMA_STRIDE_ALIGN);
    
    	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
    	if (fmt->coplanar) {
    		f->fmt.pix.sizeimage += f->fmt.pix.height *
    					f->fmt.pix.bytesperline *
    					fmt->vpdma_fmt[VIP_CHROMA]->depth >> 3;
    	}
    
    	f->fmt.pix.colorspace = fmt->colorspace;
    	f->fmt.pix.priv = 0;
    
    	vip_dbg(3, port, "calc_format_size: fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		f->fmt.pix.width, f->fmt.pix.height,
    		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
    
    	return 0;
    }
    
    static inline bool vip_is_size_dma_aligned(u32 bpp, u32 width)
    {
    	return ((width * bpp) == ALIGN(width * bpp, VPDMA_STRIDE_ALIGN));
    }
    
    static int vip_try_fmt_vid_cap(struct file *file, void *priv,
    			       struct v4l2_format *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct v4l2_subdev_frame_size_enum fse;
    	struct vip_fmt *fmt;
    	u32 best_width, best_height, largest_width, largest_height;
    	int ret, found;
    	enum vip_csc_state csc_direction;
    
    	vip_dbg(3, stream, "try_fmt fourcc:%s size: %dx%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		f->fmt.pix.width, f->fmt.pix.height);
    
    	fmt = find_port_format_by_pix(port, f->fmt.pix.pixelformat);
    	if (!fmt) {
    		vip_dbg(2, stream,
    			"Fourcc format (0x%08x) not found.\n",
    			f->fmt.pix.pixelformat);
    
    		/* Just get the first one enumerated */
    		fmt = port->active_fmt[0];
    		f->fmt.pix.pixelformat = fmt->fourcc;
    	}
    
    	csc_direction =  vip_csc_direction(fmt->code, fmt->fourcc);
    	if (csc_direction != VIP_CSC_NA) {
    		if (!is_csc_available(port)) {
    			vip_dbg(2, stream,
    				"CSC not available for Fourcc format (0x%08x).\n",
    				f->fmt.pix.pixelformat);
    
    			/* Just get the first one enumerated */
    			fmt = port->active_fmt[0];
    			f->fmt.pix.pixelformat = fmt->fourcc;
    			/* re-evaluate the csc_direction here */
    			csc_direction =  vip_csc_direction(fmt->code,
    							   fmt->fourcc);
    		} else {
    			vip_dbg(3, stream, "CSC active on Port %c: going %s\n",
    				port->port_id == VIP_PORTA ? 'A' : 'B',
    				(csc_direction == VIP_CSC_Y2R) ? "Y2R" : "R2Y");
    		}
    	}
    
    	/*
    	 * Given that sensors might support multiple mbus code we need
    	 * to use the one that matches the requested pixel format
    	 */
    	port->try_mbus_framefmt = port->mbus_framefmt;
    	port->try_mbus_framefmt.code = fmt->code;
    
    	/* check for/find a valid width/height */
    	ret = 0;
    	found = false;
    	best_width = 0;
    	best_height = 0;
    	largest_width = 0;
    	largest_height = 0;
    	fse.pad = 0;
    	fse.code = fmt->code;
    	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    	for (fse.index = 0; ; fse.index++) {
    		u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
    
    		ret = v4l2_subdev_call(port->subdev, pad,
    				       enum_frame_size, NULL, &fse);
    		if (ret)
    			break;
    
    		vip_dbg(3, stream, "try_fmt loop:%d fourcc:%s size: %dx%d\n",
    			fse.index, fourcc_to_str(f->fmt.pix.pixelformat),
    			fse.max_width, fse.max_height);
    
    		if (!vip_is_size_dma_aligned(bpp, fse.max_width))
    			continue;
    
    		if ((fse.max_width >= largest_width) &&
    		    (fse.max_height >= largest_height)) {
    			vip_dbg(3, stream, "try_fmt loop:%d found new larger: %dx%d\n",
    				fse.index, fse.max_width, fse.max_height);
    			largest_width = fse.max_width;
    			largest_height = fse.max_height;
    		}
    
    		if ((fse.max_width >= f->fmt.pix.width) &&
    		    (fse.max_height >= f->fmt.pix.height)) {
    			vip_dbg(3, stream, "try_fmt loop:%d found at least larger: %dx%d\n",
    				fse.index, fse.max_width, fse.max_height);
    
    			if (!best_width ||
    			    ((abs(best_width - f->fmt.pix.width) >=
    			      abs(fse.max_width - f->fmt.pix.width)) &&
    			     (abs(best_height - f->fmt.pix.height) >=
    			      abs(fse.max_height - f->fmt.pix.height)))) {
    				best_width = fse.max_width;
    				best_height = fse.max_height;
    				vip_dbg(3, stream, "try_fmt loop:%d found new best: %dx%d\n",
    					fse.index, fse.max_width,
    					fse.max_height);
    			}
    		}
    
    		if ((f->fmt.pix.width == fse.max_width) &&
    		    (f->fmt.pix.height == fse.max_height)) {
    			found = true;
    			vip_dbg(3, stream, "try_fmt loop:%d found direct match: %dx%d\n",
    				fse.index, fse.max_width,
    				fse.max_height);
    			break;
    		}
    
    		if ((f->fmt.pix.width >= fse.min_width) &&
    		    (f->fmt.pix.width <= fse.max_width) &&
    		    (f->fmt.pix.height >= fse.min_height) &&
    		    (f->fmt.pix.height <= fse.max_height)) {
    			found = true;
    			vip_dbg(3, stream, "try_fmt loop:%d found direct range match: %dx%d\n",
    				fse.index, fse.max_width,
    				fse.max_height);
    			break;
    		}
    	}
    
    	if (found) {
    		port->try_mbus_framefmt.width = f->fmt.pix.width;
    		port->try_mbus_framefmt.height = f->fmt.pix.height;
    		/* No need to check for scaling */
    		goto calc_size;
    	} else if (f->fmt.pix.width > largest_width) {
    		port->try_mbus_framefmt.width = largest_width;
    		port->try_mbus_framefmt.height = largest_height;
    	} else if (best_width) {
    		port->try_mbus_framefmt.width = best_width;
    		port->try_mbus_framefmt.height = best_height;
    	} else {
    		/* use existing values as default */
    	}
    
    	vip_dbg(3, stream, "try_fmt best subdev size: %dx%d\n",
    		port->try_mbus_framefmt.width,
    		port->try_mbus_framefmt.height);
    
    	if (is_scaler_available(port) &&
    	    csc_direction != VIP_CSC_Y2R &&
    	    !vip_is_mbuscode_raw(fmt->code) &&
    	    f->fmt.pix.height <= port->try_mbus_framefmt.height &&
    	    port->try_mbus_framefmt.height <= SC_MAX_PIXEL_HEIGHT &&
    	    port->try_mbus_framefmt.width <= SC_MAX_PIXEL_WIDTH) {
    		/*
    		 * Scaler is only accessible if the dst colorspace is YUV.
    		 * As the input to the scaler must be in YUV mode only.
    		 *
    		 * Scaling up is allowed only horizontally.
    		 */
    		unsigned int hratio, vratio, width_align, height_align;
    		u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
    
    		vip_dbg(3, stream, "Scaler active on Port %c: requesting %dx%d\n",
    			port->port_id == VIP_PORTA ? 'A' : 'B',
    			f->fmt.pix.width, f->fmt.pix.height);
    
    		/* Just make sure everything is properly aligned */
    		width_align = ALIGN(f->fmt.pix.width * bpp, VPDMA_STRIDE_ALIGN);
    		width_align /= bpp;
    		height_align = ALIGN(f->fmt.pix.height, 2);
    
    		f->fmt.pix.width = width_align;
    		f->fmt.pix.height = height_align;
    
    		hratio = f->fmt.pix.width * 1000 /
    			 port->try_mbus_framefmt.width;
    		vratio = f->fmt.pix.height * 1000 /
    			 port->try_mbus_framefmt.height;
    		if (hratio < 125) {
    			f->fmt.pix.width = port->try_mbus_framefmt.width / 8;
    			vip_dbg(3, stream, "Horizontal scaling ratio out of range adjusting -> %d\n",
    				f->fmt.pix.width);
    		}
    
    		if (vratio < 188) {
    			f->fmt.pix.height = port->try_mbus_framefmt.height / 4;
    			vip_dbg(3, stream, "Vertical scaling ratio out of range adjusting -> %d\n",
    				f->fmt.pix.height);
    		}
    		vip_dbg(3, stream, "Scaler: got %dx%d\n",
    			f->fmt.pix.width, f->fmt.pix.height);
    	} else {
    		/* use existing values as default */
    		f->fmt.pix.width = port->try_mbus_framefmt.width;
    		f->fmt.pix.height = port->try_mbus_framefmt.height;
    	}
    
    calc_size:
    	/* That we have a fmt calculate imagesize and bytesperline */
    	return vip_calc_format_size(port, fmt, f);
    }
    
    static int vip_g_fmt_vid_cap(struct file *file, void *priv,
    			     struct v4l2_format *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct vip_fmt *fmt = port->fmt;
    
    	/* Use last known values or defaults */
    	f->fmt.pix.width	= stream->width;
    	f->fmt.pix.height	= stream->height;
    	f->fmt.pix.pixelformat	= port->fmt->fourcc;
    	f->fmt.pix.field	= stream->sup_field;
    	f->fmt.pix.colorspace	= port->fmt->colorspace;
    	f->fmt.pix.bytesperline	= stream->bytesperline;
    	f->fmt.pix.sizeimage	= stream->sizeimage;
    
    	vip_dbg(3, stream,
    		"g_fmt fourcc:%s code: %04x size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		fmt->code,
    		f->fmt.pix.width, f->fmt.pix.height,
    		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
    	vip_dbg(3, stream, "g_fmt vpdma data type: 0x%02X\n",
    		port->fmt->vpdma_fmt[0]->data_type);
    
    	return 0;
    }
    
    static int vip_s_fmt_vid_cap(struct file *file, void *priv,
    			     struct v4l2_format *f)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct vip_port *port = stream->port;
    	struct v4l2_subdev_format sfmt;
    	struct v4l2_mbus_framefmt *mf;
    	enum vip_csc_state csc_direction;
    	int ret;
    
    	vip_dbg(3, stream, "s_fmt input fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		f->fmt.pix.width, f->fmt.pix.height,
    		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
    
    	ret = vip_try_fmt_vid_cap(file, priv, f);
    	if (ret)
    		return ret;
    
    	vip_dbg(3, stream, "s_fmt try_fmt fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		f->fmt.pix.width, f->fmt.pix.height,
    		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
    
    	if (vb2_is_busy(&stream->vb_vidq)) {
    		vip_err(stream, "%s queue busy\n", __func__);
    		return -EBUSY;
    	}
    
    	/*
    	 * Check if we need the scaler or not
    	 *
    	 * Since on previous S_FMT call the scaler might have been
    	 * allocated if it is not needed in this instance we will
    	 * attempt to free it just in case.
    	 *
    	 * free_scaler() is harmless unless the current port
    	 * allocated it.
    	 */
    	if (f->fmt.pix.width == port->try_mbus_framefmt.width &&
    	    f->fmt.pix.height == port->try_mbus_framefmt.height)
    		free_scaler(port);
    	else
    		allocate_scaler(port);
    
    	port->fmt = find_port_format_by_pix(port,
    					    f->fmt.pix.pixelformat);
    	stream->width		= f->fmt.pix.width;
    	stream->height		= f->fmt.pix.height;
    	stream->bytesperline	= f->fmt.pix.bytesperline;
    	stream->sizeimage	= f->fmt.pix.sizeimage;
    	stream->sup_field	= f->fmt.pix.field;
    
    	port->c_rect.left	= 0;
    	port->c_rect.top	= 0;
    	port->c_rect.width	= stream->width;
    	port->c_rect.height	= stream->height;
    
    	/*
    	 * Check if we need the csc unit or not
    	 *
    	 * Since on previous S_FMT call, the csc might have been
    	 * allocated if it is not needed in this instance we will
    	 * attempt to free it just in case.
    	 *
    	 * free_csc() is harmless unless the current port
    	 * allocated it.
    	 */
    	csc_direction =  vip_csc_direction(port->fmt->code, port->fmt->fourcc);
    	if (csc_direction == VIP_CSC_NA)
    		free_csc(port);
    	else
    		allocate_csc(port, csc_direction);
    
    	if (stream->sup_field == V4L2_FIELD_ALTERNATE)
    		port->flags |= FLAG_INTERLACED;
    	else
    		port->flags &= ~FLAG_INTERLACED;
    
    	vip_dbg(3, stream, "s_fmt fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f->fmt.pix.pixelformat),
    		f->fmt.pix.width, f->fmt.pix.height,
    		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
    
    	mf = &sfmt.format;
    	v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code);
    	/* Make sure to use the subdev size found in the try_fmt */
    	mf->width = port->try_mbus_framefmt.width;
    	mf->height = port->try_mbus_framefmt.height;
    
    	vip_dbg(3, stream, "s_fmt pix_to_mbus mbus_code: %04X size: %dx%d\n",
    		mf->code,
    		mf->width, mf->height);
    
    	sfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    	sfmt.pad = 0;
    	ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt);
    	if (ret) {
    		vip_dbg(1, stream, "set_fmt failed in subdev\n");
    		return ret;
    	}
    
    	/* Save it */
    	port->mbus_framefmt = *mf;
    
    	vip_dbg(3, stream, "s_fmt subdev fmt mbus_code: %04X size: %dx%d\n",
    		port->mbus_framefmt.code,
    		port->mbus_framefmt.width, port->mbus_framefmt.height);
    	vip_dbg(3, stream, "s_fmt vpdma data type: 0x%02X\n",
    		port->fmt->vpdma_fmt[0]->data_type);
    
    	return 0;
    }
    
    /*
     * Does the exact opposite of set_fmt_params
     * It makes sure the DataPath register is sane after tear down
     */
    static void unset_fmt_params(struct vip_stream *stream)
    {
    	struct vip_dev *dev = stream->port->dev;
    	struct vip_port *port = stream->port;
    
    	stream->sequence = 0;
    	stream->field = V4L2_FIELD_TOP;
    
    	if (port->csc == VIP_CSC_Y2R) {
    		if (port->port_id == VIP_PORTA) {
    			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 0);
    		} else {
    			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    		}
    		/* We are done */
    		return;
    	} else if (port->csc == VIP_CSC_R2Y) {
    		if (port->scaler && port->fmt->coplanar) {
    			if (port->port_id == VIP_PORTA) {
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_SC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			}
    		} else if (port->scaler) {
    			if (port->port_id == VIP_PORTA) {
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_SC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			}
    		} else if (port->fmt->coplanar) {
    			if (port->port_id == VIP_PORTA) {
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			}
    		} else {
    			if (port->port_id == VIP_PORTA) {
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   0);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			}
    		}
    		/* We are done */
    		return;
    	} else if (vip_is_fmt_rgb(port->fmt->fourcc)) {
    		if (port->port_id == VIP_PORTA) {
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    		}
    		/* We are done */
    		return;
    	}
    
    	if (port->scaler && port->fmt->coplanar) {
    		if (port->port_id == VIP_PORTA) {
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    		}
    	} else if (port->scaler) {
    		if (port->port_id == VIP_PORTA) {
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		}
    	} else if (port->fmt->coplanar) {
    		if (port->port_id == VIP_PORTA) {
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    		}
    	} else {
    		/*
    		 * We undo all data path setting except for the multi
    		 * stream case.
    		 * Because we cannot disrupt other on-going capture if only
    		 * one stream is terminated the other might still be going
    		 */
    		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
    		vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    	}
    }
    
    /*
     * Set the registers that are modified when the video format changes.
     */
    static void set_fmt_params(struct vip_stream *stream)
    {
    	struct vip_dev *dev = stream->port->dev;
    	struct vip_port *port = stream->port;
    
    	stream->sequence = 0;
    	stream->field = V4L2_FIELD_TOP;
    
    	if (port->csc == VIP_CSC_Y2R) {
    		port->flags &= ~FLAG_MULT_PORT;
    		/* Set alpha component in background color */
    		vpdma_set_bg_color(dev->shared->vpdma,
    				   (struct vpdma_data_format *)
    				   port->fmt->vpdma_fmt[0],
    				   0xff);
    		if (port->port_id == VIP_PORTA) {
    			/*
    			 * Input A: YUV422
    			 * Output: Y_UP/UV_UP: RGB
    			 * CSC_SRC_SELECT       = 1
    			 * RGB_OUT_HI_SELECT    = 1
    			 * RGB_SRC_SELECT       = 1
    			 * MULTI_CHANNEL_SELECT = 0
    			 */
    			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 1);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 1);
    		} else {
    			/*
    			 * Input B: YUV422
    			 * Output: Y_UP/UV_UP: RGB
    			 * CSC_SRC_SELECT       = 2
    			 * RGB_OUT_LO_SELECT    = 1
    			 * MULTI_CHANNEL_SELECT = 0
    			 */
    			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 2);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
    		}
    		/* We are done */
    		return;
    	} else if (port->csc == VIP_CSC_R2Y) {
    		port->flags &= ~FLAG_MULT_PORT;
    		if (port->scaler && port->fmt->coplanar) {
    			if (port->port_id == VIP_PORTA) {
    				/*
    				 * Input A: RGB
    				 * Output: Y_UP/UV_UP: Scaled YUV420
    				 * CSC_SRC_SELECT       = 4
    				 * SC_SRC_SELECT        = 1
    				 * CHR_DS_1_SRC_SELECT  = 1
    				 * CHR_DS_1_BYPASS      = 0
    				 * RGB_OUT_HI_SELECT    = 0
    				 */
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 4);
    				vip_set_slice_path(dev,
    						   VIP_SC_SRC_DATA_SELECT, 1);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   1);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			} else {
    				vip_err(stream, "RGB sensor can only be on Port A\n");
    			}
    		} else if (port->scaler) {
    			if (port->port_id == VIP_PORTA) {
    				/*
    				 * Input A: RGB
    				 * Output: Y_UP: Scaled YUV422
    				 * CSC_SRC_SELECT       = 4
    				 * SC_SRC_SELECT        = 1
    				 * CHR_DS_1_SRC_SELECT  = 1
    				 * CHR_DS_1_BYPASS      = 1
    				 * RGB_OUT_HI_SELECT    = 0
    				 */
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 4);
    				vip_set_slice_path(dev,
    						   VIP_SC_SRC_DATA_SELECT, 1);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   1);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 1);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			} else {
    				vip_err(stream, "RGB sensor can only be on Port A\n");
    			}
    		} else if (port->fmt->coplanar) {
    			if (port->port_id == VIP_PORTA) {
    				/*
    				 * Input A: RGB
    				 * Output: Y_UP/UV_UP: YUV420
    				 * CSC_SRC_SELECT       = 4
    				 * CHR_DS_1_SRC_SELECT  = 2
    				 * CHR_DS_1_BYPASS      = 0
    				 * RGB_OUT_HI_SELECT    = 0
    				 */
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 4);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   2);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 0);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			} else {
    				vip_err(stream, "RGB sensor can only be on Port A\n");
    			}
    		} else {
    			if (port->port_id == VIP_PORTA) {
    				/*
    				 * Input A: RGB
    				 * Output: Y_UP/UV_UP: YUV420
    				 * CSC_SRC_SELECT       = 4
    				 * CHR_DS_1_SRC_SELECT  = 2
    				 * CHR_DS_1_BYPASS      = 1
    				 * RGB_OUT_HI_SELECT    = 0
    				 */
    				vip_set_slice_path(dev,
    						   VIP_CSC_SRC_DATA_SELECT, 4);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_SRC_DATA_SELECT,
    						   2);
    				vip_set_slice_path(dev,
    						   VIP_CHR_DS_1_DATA_BYPASS, 1);
    				vip_set_slice_path(dev,
    						   VIP_RGB_OUT_HI_DATA_SELECT,
    						   0);
    			} else {
    				vip_err(stream, "RGB sensor can only be on Port A\n");
    			}
    		}
    		/* We are done */
    		return;
    	} else if (vip_is_fmt_rgb(port->fmt->fourcc)) {
    		port->flags &= ~FLAG_MULT_PORT;
    		/* Set alpha component in background color */
    		vpdma_set_bg_color(dev->shared->vpdma,
    				   (struct vpdma_data_format *)
    				   port->fmt->vpdma_fmt[0],
    				   0xff);
    		if (port->port_id == VIP_PORTA) {
    			/*
    			 * Input A: RGB
    			 * Output: Y_LO/UV_LO: RGB
    			 * RGB_OUT_LO_SELECT    = 1
    			 * MULTI_CHANNEL_SELECT = 1
    			 */
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
    		} else {
    			vip_err(stream, "RGB sensor can only be on Port A\n");
    		}
    		/* We are done */
    		return;
    	}
    
    	if (port->scaler && port->fmt->coplanar) {
    		port->flags &= ~FLAG_MULT_PORT;
    		if (port->port_id == VIP_PORTA) {
    			/*
    			 * Input A: YUV422
    			 * Output: Y_UP/UV_UP: Scaled YUV420
    			 * SC_SRC_SELECT        = 2
    			 * CHR_DS_1_SRC_SELECT  = 1
    			 * CHR_DS_1_BYPASS      = 0
    			 * RGB_OUT_HI_SELECT    = 0
    			 */
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			/*
    			 * Input B: YUV422
    			 * Output: Y_LO/UV_LO: Scaled YUV420
    			 * SC_SRC_SELECT        = 3
    			 * CHR_DS_2_SRC_SELECT  = 1
    			 * RGB_OUT_LO_SELECT    = 0
    			 * MULTI_CHANNEL_SELECT = 0
    			 */
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    		}
    	} else if (port->scaler) {
    		port->flags &= ~FLAG_MULT_PORT;
    		if (port->port_id == VIP_PORTA) {
    			/*
    			 * Input A: YUV422
    			 * Output: Y_UP: Scaled YUV422
    			 * SC_SRC_SELECT        = 2
    			 * CHR_DS_1_SRC_SELECT  = 1
    			 * CHR_DS_1_BYPASS      = 1
    			 * RGB_OUT_HI_SELECT    = 0
    			 */
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			/*
    			 * Input B: YUV422
    			 * Output: UV_UP: Scaled YUV422
    			 * SC_SRC_SELECT        = 3
    			 * CHR_DS_2_SRC_SELECT  = 1
    			 * CHR_DS_1_BYPASS      = 1
    			 * CHR_DS_2_BYPASS      = 1
    			 * RGB_OUT_HI_SELECT    = 0
    			 */
    			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
    			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 1);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		}
    	} else if (port->fmt->coplanar) {
    		port->flags &= ~FLAG_MULT_PORT;
    		if (port->port_id == VIP_PORTA) {
    			/*
    			 * Input A: YUV422
    			 * Output: Y_UP/UV_UP: YUV420
    			 * CHR_DS_1_SRC_SELECT  = 3
    			 * CHR_DS_1_BYPASS      = 0
    			 * RGB_OUT_HI_SELECT    = 0
    			 */
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_1_SRC_DATA_SELECT, 3);
    			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
    		} else {
    			/*
    			 * Input B: YUV422
    			 * Output: Y_LO/UV_LO: YUV420
    			 * CHR_DS_2_SRC_SELECT  = 4
    			 * CHR_DS_2_BYPASS      = 0
    			 * RGB_OUT_LO_SELECT    = 0
    			 * MULTI_CHANNEL_SELECT = 0
    			 */
    			vip_set_slice_path(dev,
    					   VIP_CHR_DS_2_SRC_DATA_SELECT, 4);
    			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
    			vip_set_slice_path(dev,
    					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
    			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    		}
    	} else {
    		port->flags |= FLAG_MULT_PORT;
    		/*
    		 * Input A/B: YUV422
    		 * Output: Y_LO: YUV422 - UV_LO: YUV422
    		 * MULTI_CHANNEL_SELECT = 1
    		 * RGB_OUT_LO_SELECT    = 0
    		 */
    		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
    		vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
    	}
    }
    
    static int vip_g_selection(struct file *file, void *fh,
    			   struct v4l2_selection *s)
    {
    	struct vip_stream *stream = file2stream(file);
    
    	switch (s->target) {
    	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
    	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
    	case V4L2_SEL_TGT_CROP_BOUNDS:
    	case V4L2_SEL_TGT_CROP_DEFAULT:
    		s->r.left = 0;
    		s->r.top = 0;
    		s->r.width = stream->width;
    		s->r.height = stream->height;
    		return 0;
    
    	case V4L2_SEL_TGT_COMPOSE:
    	case V4L2_SEL_TGT_CROP:
    		s->r = stream->port->c_rect;
    		return 0;
    	}
    
    	return -EINVAL;
    }
    
    static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
    {
    	if (a->left < b->left || a->top < b->top)
    		return 0;
    	if (a->left + a->width > b->left + b->width)
    		return 0;
    	if (a->top + a->height > b->top + b->height)
    		return 0;
    
    	return 1;
    }
    
    static int vip_s_selection(struct file *file, void *fh,
    			   struct v4l2_selection *s)
    {
    	struct vip_stream *stream = file2stream(file);
    	struct v4l2_rect r = s->r;
    
    	v4l_bound_align_image(&r.width, 0, stream->width, 0,
    			      &r.height, 0, stream->height, 0, 0);
    
    	r.left = clamp_t(unsigned int, r.left, 0, stream->width - r.width);
    	r.top  = clamp_t(unsigned int, r.top, 0, stream->height - r.height);
    
    	if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
    		return -ERANGE;
    
    	if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
    		return -ERANGE;
    
    	s->r = r;
    	stream->port->c_rect = r;
    
    	vip_dbg(1, stream, "cropped (%d,%d)/%dx%d of %dx%d\n",
    		r.left, r.top, r.width, r.height,
    		stream->width, stream->height);
    
    	return 0;
    }
    
    static long vip_ioctl_default(struct file *file, void *fh, bool valid_prio,
    			      unsigned int cmd, void *arg)
    {
    	struct vip_stream *stream = file2stream(file);
    
    	if (!valid_prio) {
    		vip_err(stream, "%s device busy\n", __func__);
    		return -EBUSY;
    	}
    
    	switch (cmd) {
    	default:
    		return -ENOTTY;
    	}
    }
    
    static const struct v4l2_ioctl_ops vip_ioctl_ops = {
    	.vidioc_querycap	= vip_querycap,
    	.vidioc_enum_input	= vip_enuminput,
    	.vidioc_g_input		= vip_g_input,
    	.vidioc_s_input		= vip_s_input,
    
    	.vidioc_querystd	= vip_querystd,
    	.vidioc_g_std		= vip_g_std,
    	.vidioc_s_std		= vip_s_std,
    
    	.vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap,
    	.vidioc_g_fmt_vid_cap	= vip_g_fmt_vid_cap,
    	.vidioc_try_fmt_vid_cap	= vip_try_fmt_vid_cap,
    	.vidioc_s_fmt_vid_cap	= vip_s_fmt_vid_cap,
    
    	.vidioc_enum_frameintervals	= vip_enum_frameintervals,
    	.vidioc_enum_framesizes		= vip_enum_framesizes,
    	.vidioc_s_parm			= vip_s_parm,
    	.vidioc_g_parm			= vip_g_parm,
    	.vidioc_g_selection	= vip_g_selection,
    	.vidioc_s_selection	= vip_s_selection,
    	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
    	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
    	.vidioc_prepare_buf	= vb2_ioctl_prepare_buf,
    	.vidioc_querybuf	= vb2_ioctl_querybuf,
    	.vidioc_qbuf		= vb2_ioctl_qbuf,
    	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
    	.vidioc_expbuf		= vb2_ioctl_expbuf,
    
    	.vidioc_streamon	= vb2_ioctl_streamon,
    	.vidioc_streamoff	= vb2_ioctl_streamoff,
    	.vidioc_log_status	= v4l2_ctrl_log_status,
    	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
    	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
    	.vidioc_default		= vip_ioctl_default,
    };
    
    /*
     * Videobuf operations
     */
    static int vip_queue_setup(struct vb2_queue *vq,
    			   unsigned int *nbuffers, unsigned int *nplanes,
    			   unsigned int sizes[], struct device *alloc_devs[])
    {
    	struct vip_stream *stream = vb2_get_drv_priv(vq);
    	unsigned int size = stream->sizeimage;
    
    	if (vq->num_buffers + *nbuffers < 3)
    		*nbuffers = 3 - vq->num_buffers;
    
    	if (*nplanes) {
    		if (sizes[0] < size)
    			return -EINVAL;
    		size = sizes[0];
    	}
    
    	*nplanes = 1;
    	sizes[0] = size;
    
    	vip_dbg(1, stream, "get %d buffer(s) of size %d each.\n",
    		*nbuffers, sizes[0]);
    
    	return 0;
    }
    
    static int vip_buf_prepare(struct vb2_buffer *vb)
    {
    	struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
    
    	if (vb2_plane_size(vb, 0) < stream->sizeimage) {
    		vip_dbg(1, stream,
    			"%s data will not fit into plane (%lu < %lu)\n",
    			__func__, vb2_plane_size(vb, 0),
    			(long)stream->sizeimage);
    		return -EINVAL;
    	}
    
    	vb2_set_plane_payload(vb, 0, stream->sizeimage);
    
    	return 0;
    }
    
    static void vip_buf_queue(struct vb2_buffer *vb)
    {
    	struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
    	struct vip_dev *dev = stream->port->dev;
    	struct vip_buffer *buf = container_of(vb, struct vip_buffer,
    					      vb.vb2_buf);
    	unsigned long flags;
    
    	spin_lock_irqsave(&dev->slock, flags);
    	list_add_tail(&buf->list, &stream->vidq);
    	spin_unlock_irqrestore(&dev->slock, flags);
    }
    
    static int vip_setup_scaler(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	struct sc_data *sc = dev->sc;
    	struct csc_data *csc = dev->csc;
    	struct vpdma_data *vpdma = dev->shared->vpdma;
    	struct vip_mmr_adb *mmr_adb = port->mmr_adb.addr;
    	int list_num = stream->list_num;
    	int timeout = 500;
    
    	/* if scaler not associated with this port then skip */
    	if (port->scaler) {
    		sc_set_hs_coeffs(sc, port->sc_coeff_h.addr,
    				 port->mbus_framefmt.width,
    				 port->c_rect.width);
    		sc_set_vs_coeffs(sc, port->sc_coeff_v.addr,
    				 port->mbus_framefmt.height,
    				 port->c_rect.height);
    		sc_config_scaler(sc, &mmr_adb->sc_regs0[0],
    				 &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0],
    				 port->mbus_framefmt.width,
    				 port->mbus_framefmt.height,
    				 port->c_rect.width,
    				 port->c_rect.height);
    		port->load_mmrs = true;
    	}
    
    	/* if csc not associated with this port then skip */
    	if (port->csc) {
    		csc_set_coeff(csc, &mmr_adb->csc_regs[0],
    			      vip_code_to_colorspace(port->fmt->code),
    			      vip_fourcc_to_colorspace(port->fmt->fourcc));
    		port->load_mmrs = true;
    	}
    
    	/* If coeff are already loaded then skip */
    	if (!sc->load_coeff_v && !sc->load_coeff_h && !port->load_mmrs)
    		return 0;
    
    	if (vpdma_list_busy(vpdma, list_num)) {
    		vip_dbg(3, stream, "%s: List %d is busy\n",
    			__func__, list_num);
    	}
    
    	/* Make sure we start with a clean list */
    	vpdma_reset_desc_list(&stream->desc_list);
    
    	/* config descriptors */
    	if (port->load_mmrs) {
    		vpdma_map_desc_buf(vpdma, &port->mmr_adb);
    		vpdma_add_cfd_adb(&stream->desc_list, CFD_MMR_CLIENT,
    				  &port->mmr_adb);
    
    		port->load_mmrs = false;
    		vip_dbg(3, stream, "Added mmr_adb config desc\n");
    	}
    
    	if (sc->loaded_coeff_h != port->sc_coeff_h.dma_addr ||
    	    sc->load_coeff_h) {
    		vpdma_map_desc_buf(vpdma, &port->sc_coeff_h);
    		vpdma_add_cfd_block(&stream->desc_list,
    				    VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
    				    &port->sc_coeff_h, 0);
    
    		sc->loaded_coeff_h = port->sc_coeff_h.dma_addr;
    		sc->load_coeff_h = false;
    		vip_dbg(3, stream, "Added sc_coeff_h config desc\n");
    	}
    
    	if (sc->loaded_coeff_v != port->sc_coeff_v.dma_addr ||
    	    sc->load_coeff_v) {
    		vpdma_map_desc_buf(vpdma, &port->sc_coeff_v);
    		vpdma_add_cfd_block(&stream->desc_list,
    				    VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
    				    &port->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4);
    
    		sc->loaded_coeff_v = port->sc_coeff_v.dma_addr;
    		sc->load_coeff_v = false;
    		vip_dbg(3, stream, "Added sc_coeff_v config desc\n");
    	}
    	vip_dbg(3, stream, "CFD_SC_CLIENT %d slice_id: %d\n",
    		VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, dev->slice_id);
    
    	vpdma_map_desc_buf(vpdma, &stream->desc_list.buf);
    	vip_dbg(3, stream, "Submitting desc on list# %d\n", list_num);
    	vpdma_submit_descs(vpdma, &stream->desc_list, list_num);
    
    	while (vpdma_list_busy(vpdma, list_num) && timeout--)
    		usleep_range(1000, 1100);
    
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->mmr_adb);
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_h);
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_v);
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    
    	vpdma_reset_desc_list(&stream->desc_list);
    
    	if (timeout <= 0) {
    		vip_err(stream, "Timed out setting up scaler through VPDMA list\n");
    		return -EBUSY;
    	}
    
    	return 0;
    }
    
    static int vip_load_vpdma_list_fifo(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	struct vpdma_data *vpdma = dev->shared->vpdma;
    	int list_num = stream->list_num;
    	struct vip_buffer *buf;
    	unsigned long flags;
    	int timeout, i;
    
    	if (vpdma_list_busy(dev->shared->vpdma, stream->list_num))
    		return -EBUSY;
    
    	for (i = 0; i < VIP_VPDMA_FIFO_SIZE; i++) {
    		spin_lock_irqsave(&dev->slock, flags);
    		if (list_empty(&stream->vidq)) {
    			vip_err(stream, "No buffer left!");
    			spin_unlock_irqrestore(&dev->slock, flags);
    			return -EINVAL;
    		}
    
    		buf = list_entry(stream->vidq.next,
    				 struct vip_buffer, list);
    		buf->drop = false;
    
    		list_move_tail(&buf->list, &stream->post_bufs);
    		spin_unlock_irqrestore(&dev->slock, flags);
    
    		vip_dbg(2, stream, "%s: start_dma vb2 buf idx:%d\n",
    			__func__, buf->vb.vb2_buf.index);
    		start_dma(stream, buf);
    
    		timeout = 500;
    		while (vpdma_list_busy(vpdma, list_num) && timeout--)
    			usleep_range(1000, 1100);
    
    		if (timeout <= 0) {
    			vip_err(stream, "Timed out loading VPDMA list fifo\n");
    			return -EBUSY;
    		}
    	}
    	return 0;
    }
    
    static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
    {
    	struct vip_stream *stream = vb2_get_drv_priv(vq);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	int ret;
    
    	vip_setup_scaler(stream);
    
    	/*
    	 * Make sure the scaler is configured before the datapath is
    	 * enabled. The scaler can only load the coefficient
    	 * parameters when it is idle. If the scaler path is enabled
    	 * and video data is being received then the VPDMA transfer will
    	 * stall indefinetely.
    	 */
    	set_fmt_params(stream);
    	vip_setup_parser(port);
    
    	if (port->subdev) {
    		ret = v4l2_subdev_call(port->subdev, video, s_stream, 1);
    		if (ret) {
    			vip_dbg(1, stream, "stream on failed in subdev\n");
    			return ret;
    		}
    	}
    
    	stream->sequence = 0;
    	stream->field = V4L2_FIELD_TOP;
    	populate_desc_list(stream);
    
    	ret = vip_load_vpdma_list_fifo(stream);
    	if (ret)
    		return ret;
    
    	stream->num_recovery = 0;
    
    	clear_irqs(dev, dev->slice_id, stream->list_num);
    	enable_irqs(dev, dev->slice_id, stream->list_num);
    	vip_schedule_next_buffer(stream);
    	vip_parser_stop_imm(port, false);
    	vip_enable_parser(port, true);
    
    	return 0;
    }
    
    /*
     * Abort streaming and wait for last buffer
     */
    static void vip_stop_streaming(struct vb2_queue *vq)
    {
    	struct vip_stream *stream = vb2_get_drv_priv(vq);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	struct vip_buffer *buf;
    	int ret;
    
    	vip_dbg(2, stream, "%s:\n", __func__);
    
    	vip_parser_stop_imm(port, true);
    	vip_enable_parser(port, false);
    	unset_fmt_params(stream);
    
    	disable_irqs(dev, dev->slice_id, stream->list_num);
    	clear_irqs(dev, dev->slice_id, stream->list_num);
    
    	if (port->subdev) {
    		ret = v4l2_subdev_call(port->subdev, video, s_stream, 0);
    		if (ret)
    			vip_dbg(1, stream, "stream on failed in subdev\n");
    	}
    
    	stop_dma(stream, true);
    
    	/* release all active buffers */
    	while (!list_empty(&stream->post_bufs)) {
    		buf = list_entry(stream->post_bufs.next,
    				 struct vip_buffer, list);
    		list_del(&buf->list);
    		if (buf->drop == 1)
    			list_add_tail(&buf->list, &stream->dropq);
    		else
    			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
    	}
    	while (!list_empty(&stream->vidq)) {
    		buf = list_entry(stream->vidq.next, struct vip_buffer, list);
    		list_del(&buf->list);
    		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
    	}
    
    	if (!vb2_is_streaming(vq))
    		return;
    
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    	vpdma_reset_desc_list(&stream->desc_list);
    }
    
    static const struct vb2_ops vip_video_qops = {
    	.queue_setup		= vip_queue_setup,
    	.buf_prepare		= vip_buf_prepare,
    	.buf_queue		= vip_buf_queue,
    	.start_streaming	= vip_start_streaming,
    	.stop_streaming		= vip_stop_streaming,
    	.wait_prepare		= vb2_ops_wait_prepare,
    	.wait_finish		= vb2_ops_wait_finish,
    };
    
    /*
     * File operations
     */
    
    static int vip_init_dev(struct vip_dev *dev)
    {
    	if (dev->num_ports != 0)
    		goto done;
    
    	vip_set_clock_enable(dev, 1);
    	vip_module_reset(dev, VIP_SC_RST, false);
    	vip_module_reset(dev, VIP_CSC_RST, false);
    done:
    	dev->num_ports++;
    
    	return 0;
    }
    
    static inline bool is_scaler_available(struct vip_port *port)
    {
    	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL)
    		if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
    		    port->dev->sc_assigned == port->port_id)
    			return true;
    	return false;
    }
    
    static inline bool allocate_scaler(struct vip_port *port)
    {
    	if (is_scaler_available(port)) {
    		if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
    		    port->dev->sc_assigned == port->port_id) {
    			port->dev->sc_assigned = port->port_id;
    			port->scaler = true;
    			return true;
    		}
    	}
    	return false;
    }
    
    static inline void free_scaler(struct vip_port *port)
    {
    	if (port->dev->sc_assigned == port->port_id) {
    		port->dev->sc_assigned = VIP_NOT_ASSIGNED;
    		port->scaler = false;
    	}
    }
    
    static bool is_csc_available(struct vip_port *port)
    {
    	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL)
    		if (port->dev->csc_assigned == VIP_NOT_ASSIGNED ||
    		    port->dev->csc_assigned == port->port_id)
    			return true;
    	return false;
    }
    
    static bool allocate_csc(struct vip_port *port,
    				enum vip_csc_state csc_direction)
    {
    	/* Is CSC needed? */
    	if (csc_direction != VIP_CSC_NA) {
    		if (is_csc_available(port)) {
    			port->dev->csc_assigned = port->port_id;
    			port->csc = csc_direction;
    			vip_dbg(1, port, "%s: csc allocated: dir: %d\n",
    				__func__, csc_direction);
    			return true;
    		}
    	}
    	return false;
    }
    
    static void free_csc(struct vip_port *port)
    {
    	if (port->dev->csc_assigned == port->port_id) {
    		port->dev->csc_assigned = VIP_NOT_ASSIGNED;
    		port->csc = VIP_CSC_NA;
    		vip_dbg(1, port, "%s: csc freed\n",
    			__func__);
    	}
    }
    
    static int vip_init_port(struct vip_port *port)
    {
    	int ret;
    	struct vip_fmt *fmt;
    	struct v4l2_subdev_format sd_fmt;
    	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
    
    	if (port->num_streams != 0)
    		goto done;
    
    	ret = vip_init_dev(port->dev);
    	if (ret)
    		goto done;
    
    	/* Get subdevice current frame format */
    	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    	sd_fmt.pad = 0;
    	ret = v4l2_subdev_call(port->subdev, pad, get_fmt, NULL, &sd_fmt);
    	if (ret)
    		vip_dbg(1, port, "init_port get_fmt failed in subdev: (%d)\n",
    			ret);
    
    	/* try to find one that matches */
    	fmt = find_port_format_by_code(port, mbus_fmt->code);
    	if (!fmt) {
    		vip_dbg(1, port, "subdev default mbus_fmt %04x is not matched.\n",
    			mbus_fmt->code);
    		/* if all else fails just pick the first one */
    		fmt = port->active_fmt[0];
    
    		mbus_fmt->code = fmt->code;
    		sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    		sd_fmt.pad = 0;
    		ret = v4l2_subdev_call(port->subdev, pad, set_fmt,
    				       NULL, &sd_fmt);
    		if (ret)
    			vip_dbg(1, port, "init_port set_fmt failed in subdev: (%d)\n",
    				ret);
    	}
    
    	/* Assign current format */
    	port->fmt = fmt;
    	port->mbus_framefmt = *mbus_fmt;
    
    	vip_dbg(3, port, "%s: g_mbus_fmt subdev mbus_code: %04X fourcc:%s size: %dx%d\n",
    		__func__, fmt->code,
    		fourcc_to_str(fmt->fourcc),
    		mbus_fmt->width, mbus_fmt->height);
    
    	if (mbus_fmt->field == V4L2_FIELD_ALTERNATE)
    		port->flags |= FLAG_INTERLACED;
    	else
    		port->flags &= ~FLAG_INTERLACED;
    
    	port->c_rect.left	= 0;
    	port->c_rect.top	= 0;
    	port->c_rect.width	= mbus_fmt->width;
    	port->c_rect.height	= mbus_fmt->height;
    
    	ret = vpdma_alloc_desc_buf(&port->sc_coeff_h, SC_COEF_SRAM_SIZE);
    	if (ret != 0)
    		return ret;
    
    	ret = vpdma_alloc_desc_buf(&port->sc_coeff_v, SC_COEF_SRAM_SIZE);
    	if (ret != 0)
    		goto free_sc_h;
    
    	ret = vpdma_alloc_desc_buf(&port->mmr_adb, sizeof(struct vip_mmr_adb));
    	if (ret != 0)
    		goto free_sc_v;
    
    	init_adb_hdrs(port);
    
    	vip_enable_parser(port, false);
    done:
    	port->num_streams++;
    	return 0;
    
    free_sc_v:
    	vpdma_free_desc_buf(&port->sc_coeff_v);
    free_sc_h:
    	vpdma_free_desc_buf(&port->sc_coeff_h);
    	return ret;
    }
    
    static int vip_init_stream(struct vip_stream *stream)
    {
    	struct vip_port *port = stream->port;
    	struct vip_fmt *fmt;
    	struct v4l2_mbus_framefmt *mbus_fmt;
    	struct v4l2_format f;
    	int ret;
    
    	ret = vip_init_port(port);
    	if (ret != 0)
    		return ret;
    
    	fmt = port->fmt;
    	mbus_fmt = &port->mbus_framefmt;
    
    	memset(&f, 0, sizeof(f));
    
    	/* Properly calculate the sizeimage and bytesperline values. */
    	v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt);
    	f.fmt.pix.pixelformat = fmt->fourcc;
    	ret = vip_calc_format_size(port, fmt, &f);
    	if (ret)
    		return ret;
    
    	stream->width = f.fmt.pix.width;
    	stream->height = f.fmt.pix.height;
    	stream->sup_field = f.fmt.pix.field;
    	stream->bytesperline = f.fmt.pix.bytesperline;
    	stream->sizeimage = f.fmt.pix.sizeimage;
    
    	vip_dbg(3, stream, "init_stream fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
    		fourcc_to_str(f.fmt.pix.pixelformat),
    		f.fmt.pix.width, f.fmt.pix.height,
    		f.fmt.pix.bytesperline, f.fmt.pix.sizeimage);
    	vip_dbg(3, stream, "init_stream vpdma data type: 0x%02X\n",
    		port->fmt->vpdma_fmt[0]->data_type);
    
    	ret = vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE,
    				     VPDMA_LIST_TYPE_NORMAL);
    
    	if (ret != 0)
    		return ret;
    
    	stream->write_desc = (struct vpdma_dtd *)stream->desc_list.buf.addr
    				+ 15;
    
    	vip_dbg(1, stream, "%s: stream instance %pa\n",
    		__func__, &stream);
    
    	return 0;
    }
    
    static void vip_release_dev(struct vip_dev *dev)
    {
    	/*
    	 * On last close, disable clocks to conserve power
    	 */
    
    	if (--dev->num_ports == 0) {
    		/* reset the scaler module */
    		vip_module_reset(dev, VIP_SC_RST, true);
    		vip_module_reset(dev, VIP_CSC_RST, true);
    		vip_set_clock_enable(dev, 0);
    	}
    }
    
    static int vip_set_crop_parser(struct vip_port *port)
    {
    	struct vip_dev *dev = port->dev;
    	struct vip_parser_data *parser = dev->parser;
    	u32 hcrop = 0, vcrop = 0;
    	u32 width = port->mbus_framefmt.width;
    
    	if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
    		/*
    		 * Special case since we are faking a YUV422 16bit format
    		 * to have the vpdma perform the needed byte swap
    		 * we need to adjust the pixel width accordingly
    		 * otherwise the parser will attempt to collect more pixels
    		 * then available and the vpdma transfer will exceed the
    		 * allocated frame buffer.
    		 */
    		width >>= 1;
    		vip_dbg(1, port, "%s: 8 bit raw detected, adjusting width to %d\n",
    			__func__, width);
    	}
    
    	/*
    	 * Set Parser Crop parameters to source size otherwise
    	 * scaler and colorspace converter will yield garbage.
    	 */
    	hcrop = VIP_ACT_BYPASS;
    	insert_field(&hcrop, 0, VIP_ACT_SKIP_NUMPIX_MASK,
    		     VIP_ACT_SKIP_NUMPIX_SHFT);
    	insert_field(&hcrop, width,
    		     VIP_ACT_USE_NUMPIX_MASK, VIP_ACT_USE_NUMPIX_SHFT);
    	reg_write(parser, VIP_PARSER_CROP_H_PORT(port->port_id), hcrop);
    
    	insert_field(&vcrop, 0, VIP_ACT_SKIP_NUMLINES_MASK,
    		     VIP_ACT_SKIP_NUMLINES_SHFT);
    	insert_field(&vcrop, port->mbus_framefmt.height,
    		     VIP_ACT_USE_NUMLINES_MASK, VIP_ACT_USE_NUMLINES_SHFT);
    	reg_write(parser, VIP_PARSER_CROP_V_PORT(port->port_id), vcrop);
    
    	return 0;
    }
    
    static int vip_setup_parser(struct vip_port *port)
    {
    	struct vip_dev *dev = port->dev;
    	struct vip_parser_data *parser = dev->parser;
    	struct v4l2_fwnode_endpoint *endpoint = port->endpoint;
    	int iface, sync_type;
    	u32 flags = 0, config0;
    
    	/* Reset the port */
    	vip_reset_parser(port, true);
    	usleep_range(200, 250);
    	vip_reset_parser(port, false);
    
    	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
    
    	if (endpoint->bus_type == V4L2_MBUS_BT656) {
    		flags = endpoint->bus.parallel.flags;
    		iface = DUAL_8B_INTERFACE;
    
    		/*
    		 * Ideally, this should come from subdev
    		 * port->fmt can be anything once CSC is enabled
    		 */
    		if (vip_is_mbuscode_rgb(port->fmt->code)) {
    			sync_type = EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444;
    		} else {
    			switch (endpoint->bus.parallel.num_channels) {
    			case 4:
    				sync_type = EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422;
    				break;
    			case 2:
    				sync_type = EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422;
    				break;
    			case 1:
    				sync_type = EMBEDDED_SYNC_SINGLE_YUV422;
    				break;
    			default:
    				sync_type =
    				EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
    			}
    			if (endpoint->bus.parallel.pixmux == 0)
    				sync_type =
    				EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
    		}
    
    	} else if (endpoint->bus_type == V4L2_MBUS_PARALLEL) {
    		flags = endpoint->bus.parallel.flags;
    
    		switch (endpoint->bus.parallel.bus_width) {
    		case 24:
    			iface = SINGLE_24B_INTERFACE;
    		break;
    		case 16:
    			iface = SINGLE_16B_INTERFACE;
    		break;
    		case 8:
    		default:
    			iface = DUAL_8B_INTERFACE;
    		}
    
    		if (vip_is_mbuscode_rgb(port->fmt->code))
    			sync_type = DISCRETE_SYNC_SINGLE_RGB_24B;
    		else
    			sync_type = DISCRETE_SYNC_SINGLE_YUV422;
    
    		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
    			config0 |= VIP_HSYNC_POLARITY;
    		else if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
    			config0 &= ~VIP_HSYNC_POLARITY;
    
    		if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
    			config0 |= VIP_VSYNC_POLARITY;
    		else if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
    			config0 &= ~VIP_VSYNC_POLARITY;
    
    		config0 &= ~VIP_USE_ACTVID_HSYNC_ONLY;
    		config0 |= VIP_ACTVID_POLARITY;
    		config0 |= VIP_DISCRETE_BASIC_MODE;
    
    	} else {
    		vip_err(port, "Device doesn't support CSI2");
    		return -EINVAL;
    	}
    
    	if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) {
    		vip_set_pclk_invert(port);
    		config0 |= VIP_PIXCLK_EDGE_POLARITY;
    	} else {
    		config0 &= ~VIP_PIXCLK_EDGE_POLARITY;
    	}
    
    	config0 |= ((sync_type & VIP_SYNC_TYPE_MASK) << VIP_SYNC_TYPE_SHFT);
    
    	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
    
    	vip_set_data_interface(port, iface);
    	vip_set_crop_parser(port);
    
    	return 0;
    }
    
    static void vip_enable_parser(struct vip_port *port, bool on)
    {
    	u32 config0;
    	struct vip_dev *dev = port->dev;
    	struct vip_parser_data *parser = dev->parser;
    
    	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
    
    	if (on) {
    		config0 |= VIP_PORT_ENABLE;
    		config0 &= ~(VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
    	} else {
    		config0 &= ~VIP_PORT_ENABLE;
    		config0 |= (VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
    	}
    	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
    }
    
    static void vip_reset_parser(struct vip_port *port, bool on)
    {
    	u32 config0;
    	struct vip_dev *dev = port->dev;
    	struct vip_parser_data *parser = dev->parser;
    
    	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
    
    	if (on)
    		config0 |= VIP_SW_RESET;
    	else
    		config0 &= ~VIP_SW_RESET;
    
    	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
    }
    
    static void vip_parser_stop_imm(struct vip_port *port, bool on)
    {
    	u32 config0;
    	struct vip_dev *dev = port->dev;
    	struct vip_parser_data *parser = dev->parser;
    
    	config0 = reg_read(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id));
    
    	if (on)
    		config0 = 0xffffffff;
    	else
    		config0 = 0;
    
    	reg_write(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id), config0);
    }
    
    static void vip_release_stream(struct vip_stream *stream)
    {
    	struct vip_dev *dev = stream->port->dev;
    
    	vip_dbg(1, stream, "%s: stream instance %pa\n",
    		__func__, &stream);
    
    	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
    	vpdma_free_desc_buf(&stream->desc_list.buf);
    	vpdma_free_desc_list(&stream->desc_list);
    }
    
    static void vip_release_port(struct vip_port *port)
    {
    	vip_dbg(1, port, "%s: port instance %pa\n",
    		__func__, &port);
    
    	vpdma_free_desc_buf(&port->mmr_adb);
    	vpdma_free_desc_buf(&port->sc_coeff_h);
    	vpdma_free_desc_buf(&port->sc_coeff_v);
    }
    
    static void stop_dma(struct vip_stream *stream, bool clear_list)
    {
    	struct vip_dev *dev = stream->port->dev;
    	int ch, size = 0;
    
    	/* Create a list of channels to be cleared */
    	for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) {
    		if (stream->vpdma_channels[ch] == 1) {
    			stream->vpdma_channels_to_abort[size++] = ch;
    			vip_dbg(2, stream, "Clear channel no: %d\n", ch);
    		}
    	}
    
    	/* Clear all the used channels for the list */
    	vpdma_list_cleanup(dev->shared->vpdma, stream->list_num,
    			   stream->vpdma_channels_to_abort, size);
    
    	if (clear_list)
    		for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++)
    			stream->vpdma_channels[ch] = 0;
    }
    
    static int vip_open(struct file *file)
    {
    	struct vip_stream *stream = video_drvdata(file);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	int ret = 0;
    
    	vip_dbg(2, stream, "%s\n", __func__);
    
    	mutex_lock(&dev->mutex);
    
    	ret = v4l2_fh_open(file);
    	if (ret) {
    		vip_err(stream, "v4l2_fh_open failed\n");
    		goto unlock;
    	}
    
    	/*
    	 * If this is the first open file.
    	 * Then initialize hw module.
    	 */
    	if (!v4l2_fh_is_singular_file(file))
    		goto unlock;
    
    	if (vip_init_stream(stream))
    		ret = -ENODEV;
    unlock:
    	mutex_unlock(&dev->mutex);
    	return ret;
    }
    
    static int vip_release(struct file *file)
    {
    	struct vip_stream *stream = video_drvdata(file);
    	struct vip_port *port = stream->port;
    	struct vip_dev *dev = port->dev;
    	bool fh_singular;
    	int ret;
    
    	vip_dbg(2, stream, "%s\n", __func__);
    
    	mutex_lock(&dev->mutex);
    
    	/* Save the singular status before we call the clean-up helper */
    	fh_singular = v4l2_fh_is_singular_file(file);
    
    	/* the release helper will cleanup any on-going streaming */
    	ret = _vb2_fop_release(file, NULL);
    
    	free_csc(port);
    	free_scaler(port);
    
    	/*
    	 * If this is the last open file.
    	 * Then de-initialize hw module.
    	 */
    	if (fh_singular) {
    		vip_release_stream(stream);
    
    		if (--port->num_streams == 0) {
    			vip_release_port(port);
    			vip_release_dev(port->dev);
    		}
    	}
    
    	mutex_unlock(&dev->mutex);
    
    	return ret;
    }
    
    static const struct v4l2_file_operations vip_fops = {
    	.owner		= THIS_MODULE,
    	.open		= vip_open,
    	.release	= vip_release,
    	.read		= vb2_fop_read,
    	.poll		= vb2_fop_poll,
    	.unlocked_ioctl	= video_ioctl2,
    	.mmap		= vb2_fop_mmap,
    };
    
    static struct video_device vip_videodev = {
    	.name		= VIP_MODULE_NAME,
    	.fops		= &vip_fops,
    	.ioctl_ops	= &vip_ioctl_ops,
    	.minor		= -1,
    	.release	= video_device_release,
    	.tvnorms	= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
    };
    
    static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
    {
    	struct vip_stream *stream;
    	struct vip_dev *dev = port->dev;
    	struct vb2_queue *q;
    	struct video_device *vfd;
    	struct vip_buffer *buf;
    	struct list_head *pos, *tmp;
    	int ret, i;
    	u32 vin_id;
    
    	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
    	if (!stream)
    		return -ENOMEM;
    
    	stream->port = port;
    	stream->stream_id = stream_id;
    	stream->vfl_type = vfl_type;
    
    	vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
    	snprintf(stream->name, sizeof(stream->name), "vin%d%c-%d",
    		 vin_id, (port->port_id == VIP_PORTA) ? 'a' : 'b', stream_id);
    
    	stream->list_num = vpdma_hwlist_alloc(dev->shared->vpdma, stream);
    	if (stream->list_num < 0) {
    		vip_err(stream, "Could not get VPDMA hwlist");
    		ret = -ENODEV;
    		goto do_free_stream;
    	}
    
    	INIT_LIST_HEAD(&stream->post_bufs);
    
    	if (vfl_type == VFL_TYPE_GRABBER)
    		port->cap_streams[stream_id] = stream;
    	else
    		port->vbi_streams[stream_id] = stream;
    
    	/*
    	 * Initialize queue
    	 */
    	q = &stream->vb_vidq;
    	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
    	q->drv_priv = stream;
    	q->buf_struct_size = sizeof(struct vip_buffer);
    	q->ops = &vip_video_qops;
    	q->mem_ops = &vb2_dma_contig_memops;
    	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
    	q->lock = &dev->mutex;
    	q->min_buffers_needed = 3;
    	q->dev = dev->v4l2_dev->dev;
    
    	ret = vb2_queue_init(q);
    	if (ret)
    		goto do_free_hwlist;
    
    	INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work);
    
    	INIT_LIST_HEAD(&stream->vidq);
    
    	/* Allocate/populate Drop queue entries */
    	INIT_LIST_HEAD(&stream->dropq);
    	for (i = 0; i < VIP_DROPQ_SIZE; i++) {
    		buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
    		if (!buf) {
    			ret = -ENOMEM;
    			goto do_free_dropq;
    		}
    		buf->drop = true;
    		list_add(&buf->list, &stream->dropq);
    	}
    
    	vfd = video_device_alloc();
    	if (!vfd)
    		goto do_free_dropq;
    	*vfd = vip_videodev;
    	vfd->v4l2_dev = dev->v4l2_dev;
    	vfd->queue = q;
    
    	vfd->lock = &dev->mutex;
    	video_set_drvdata(vfd, stream);
    
    	ret = video_register_device(vfd, vfl_type, -1);
    	if (ret) {
    		vip_err(stream, "Failed to register video device\n");
    		goto do_free_vfd;
    	}
    
    	stream->vfd = vfd;
    
    	vip_info(stream, "device registered as %s\n",
    		 video_device_node_name(vfd));
    	return 0;
    
    do_free_vfd:
    	video_device_release(vfd);
    do_free_dropq:
    	list_for_each_safe(pos, tmp, &stream->dropq) {
    		buf = list_entry(pos,
    				 struct vip_buffer, list);
    		vip_dbg(1, dev, "dropq buffer\n");
    		list_del(pos);
    		kfree(buf);
    	}
    do_free_hwlist:
    	vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
    do_free_stream:
    	kfree(stream);
    	return ret;
    }
    
    static void free_stream(struct vip_stream *stream)
    {
    	struct vip_dev *dev;
    	struct vip_buffer *buf;
    	struct list_head *pos, *q;
    
    	if (!stream)
    		return;
    
    	dev = stream->port->dev;
    	/* Free up the Drop queue */
    	list_for_each_safe(pos, q, &stream->dropq) {
    		buf = list_entry(pos,
    				 struct vip_buffer, list);
    		vip_dbg(1, stream, "dropq buffer\n");
    		list_del(pos);
    		kfree(buf);
    	}
    
    	video_unregister_device(stream->vfd);
    	video_device_release(stream->vfd);
    	vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
    	stream->port->cap_streams[stream->stream_id] = NULL;
    	kfree(stream);
    }
    
    static int get_subdev_active_format(struct vip_port *port,
    				    struct v4l2_subdev *subdev)
    {
    	struct vip_fmt *fmt;
    	struct v4l2_subdev_mbus_code_enum mbus_code;
    	int ret = 0;
    	unsigned int k, i, j;
    	enum vip_csc_state csc;
    
    	/* Enumerate sub device formats and enable all matching local formats */
    	port->num_active_fmt = 0;
    	for (k = 0, i = 0; (ret != -EINVAL); k++) {
    		memset(&mbus_code, 0, sizeof(mbus_code));
    		mbus_code.index = k;
    		ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
    				       NULL, &mbus_code);
    		if (ret)
    			continue;
    
    		vip_dbg(2, port,
    			"subdev %s: code: %04x idx: %d\n",
    			subdev->name, mbus_code.code, k);
    
    		for (j = 0; j < ARRAY_SIZE(vip_formats); j++) {
    			fmt = &vip_formats[j];
    			if (mbus_code.code != fmt->code)
    				continue;
    
    			/*
    			 * When the port is configured for BT656
    			 * then none of the downstream unit can be used.
    			 * So here we need to skip all format requiring
    			 * either CSC or CHR_DS
    			 */
    			csc = vip_csc_direction(fmt->code, fmt->fourcc);
    			if (port->endpoint->bus_type == V4L2_MBUS_BT656 &&
    			    (csc != VIP_CSC_NA || fmt->coplanar))
    				continue;
    
    			port->active_fmt[i] = fmt;
    			vip_dbg(2, port,
    				"matched fourcc: %s: code: %04x idx: %d\n",
    				fourcc_to_str(fmt->fourcc), fmt->code, i);
    			port->num_active_fmt = ++i;
    		}
    	}
    
    	if (i == 0) {
    		vip_err(port, "No suitable format reported by subdev %s\n",
    			subdev->name);
    		return -EINVAL;
    	}
    	return 0;
    }
    
    static int alloc_port(struct vip_dev *dev, int id)
    {
    	struct vip_port *port;
    	u32 vin_id;
    
    	if (dev->ports[id])
    		return -EINVAL;
    
    	port = devm_kzalloc(&dev->pdev->dev, sizeof(*port), GFP_KERNEL);
    	if (!port)
    		return -ENOMEM;
    
    	dev->ports[id] = port;
    	port->dev = dev;
    	port->port_id = id;
    	vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
    	snprintf(port->name, sizeof(port->name),
    		 "vin%d%c", vin_id, (id == VIP_PORTA) ? 'a' : 'b');
    	port->num_streams = 0;
    	return 0;
    }
    
    static void free_port(struct vip_port *port)
    {
    	if (!port)
    		return;
    
    	v4l2_async_notifier_unregister(&port->notifier);
    	free_stream(port->cap_streams[0]);
    }
    
    static int get_field(u32 value, u32 mask, int shift)
    {
    	return (value & (mask << shift)) >> shift;
    }
    
    static int vip_of_probe(struct platform_device *pdev);
    static void vip_vpdma_fw_cb(struct platform_device *pdev)
    {
    	dev_info(&pdev->dev, "VPDMA firmware loaded\n");
    
    	if (pdev->dev.of_node)
    		vip_of_probe(pdev);
    }
    
    static int vip_create_streams(struct vip_port *port,
    			      struct v4l2_subdev *subdev)
    {
    	struct v4l2_fwnode_bus_parallel *bus;
    	int i;
    
    	for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++)
    		free_stream(port->cap_streams[i]);
    
    	if (get_subdev_active_format(port, subdev))
    		return -ENODEV;
    
    	port->subdev = subdev;
    
    	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL) {
    		port->flags |= FLAG_MULT_PORT;
    		port->num_streams_configured = 1;
    		alloc_stream(port, 0, VFL_TYPE_GRABBER);
    	} else if (port->endpoint->bus_type == V4L2_MBUS_BT656) {
    		port->flags |= FLAG_MULT_PORT;
    		bus = &port->endpoint->bus.parallel;
    		port->num_streams_configured = bus->num_channels;
    		for (i = 0; i < bus->num_channels; i++) {
    			if (bus->channels[i] >= 16)
    				continue;
    			alloc_stream(port, bus->channels[i], VFL_TYPE_GRABBER);
    		}
    	}
    	return 0;
    }
    
    static int vip_async_bound(struct v4l2_async_notifier *notifier,
    			   struct v4l2_subdev *subdev,
    			   struct v4l2_async_subdev *asd)
    {
    	struct vip_port *port = notifier_to_vip_port(notifier);
    	struct vip_async_config *config = &port->config;
    	unsigned int idx = asd - &config->asd[0];
    	int ret;
    
    	vip_dbg(1, port, "%s\n", __func__);
    	if (idx > config->asd_sizes)
    		return -EINVAL;
    
    	if (port->subdev) {
    		if (asd < port->subdev->asd)
    			/* Notified of a subdev earlier in the array */
    			vip_info(port, "Switching to subdev %s (High priority)",
    				 subdev->name);
    
    		else {
    			vip_info(port, "Rejecting subdev %s (Low priority)",
    				 subdev->name);
    			return 0;
    		}
    	}
    
    	port->endpoint = &config->endpoints[idx];
    	vip_info(port, "Port %c: Using subdev %s for capture\n",
    		 port->port_id == VIP_PORTA ? 'A' : 'B', subdev->name);
    
    	ret = vip_create_streams(port, subdev);
    	if (ret)
    		return ret;
    
    	return 0;
    }
    
    static int vip_async_complete(struct v4l2_async_notifier *notifier)
    {
    	struct vip_port *port = notifier_to_vip_port(notifier);
    
    	vip_dbg(1, port, "%s\n", __func__);
    	return 0;
    }
    
    static const struct v4l2_async_notifier_operations vip_async_ops = {
    	.bound = vip_async_bound,
    	.complete = vip_async_complete,
    };
    
    static struct fwnode_handle *
    fwnode_graph_get_next_endpoint_by_regs(const struct fwnode_handle *fwnode,
    				       int port_reg, int reg)
    {
    	return of_fwnode_handle(of_graph_get_endpoint_by_regs(to_of_node(fwnode),
    							      port_reg, reg));
    }
    
    static int vip_register_subdev_notif(struct vip_port *port,
    				     struct fwnode_handle *ep)
    {
    	struct vip_async_config *config = &port->config;
    	struct v4l2_async_notifier *notifier = &port->notifier;
    	struct vip_dev *dev = port->dev;
    	struct fwnode_handle *subdev, *remote_ep;
    	int ret;
    
    	subdev = fwnode_graph_get_remote_port_parent(ep);
    	if (!subdev) {
    		vip_dbg(3, port, "can't get remote parent\n");
    		return -EINVAL;
    	}
    
    	remote_ep = fwnode_graph_get_remote_endpoint(ep);
    	if (!remote_ep) {
    		vip_dbg(3, port, "can't get remote-endpoint\n");
    		fwnode_handle_put(subdev);
    		return -EINVAL;
    	}
    
    	ret = v4l2_fwnode_endpoint_parse(remote_ep, &config->endpoints[0]);
    	if (ret) {
    		vip_dbg(3, port, "Failed to parse endpoint:\n");
    		fwnode_handle_put(subdev);
    		fwnode_handle_put(remote_ep);
    		return -EINVAL;
    	}
    
    	fwnode_handle_put(subdev);
    	fwnode_handle_put(remote_ep);
    
    	config->asd[0].match_type = V4L2_ASYNC_MATCH_FWNODE;
    	config->asd[0].match.fwnode = subdev;
    	config->asd_list[0] = &config->asd[0];
    	config->asd_sizes = 1;
    	notifier->ops = &vip_async_ops;
    	notifier->subdevs = config->asd_list;
    	notifier->num_subdevs = config->asd_sizes;
    
    	ret = v4l2_async_notifier_register(dev->v4l2_dev, notifier);
    	if (ret) {
    		vip_dbg(1, port, "Error registering async notifier\n");
    		ret = -EINVAL;
    	}
    
    	return ret;
    }
    
    static int vip_of_probe(struct platform_device *pdev)
    {
    	struct vip_shared *shared = platform_get_drvdata(pdev);
    	struct regmap *syscon_pol = NULL;
    	u32 syscon_pol_offset = 0;
    	struct vip_port *port;
    	struct vip_dev *dev;
    	struct device_node *parent = pdev->dev.of_node;
    	struct fwnode_handle *ep = NULL;
    	int ret, slice_id, port_id, p;
    
    	if (parent && of_property_read_bool(parent, "syscon-pol")) {
    		syscon_pol = syscon_regmap_lookup_by_phandle(parent,
    							     "syscon-pol");
    		if (IS_ERR(syscon_pol)) {
    			dev_err(&pdev->dev, "failed to get syscon-pol regmap\n");
    			return PTR_ERR(syscon_pol);
    		}
    
    		if (of_property_read_u32_index(parent, "syscon-pol", 1,
    					       &syscon_pol_offset)) {
    			dev_err(&pdev->dev, "failed to get syscon-pol offset\n");
    			return -EINVAL;
    		}
    	}
    
    	for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) {
    		ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent),
    							    p, 0);
    		if (!ep)
    			continue;
    
    		switch (p) {
    		case 0:
    			slice_id = VIP_SLICE1;	port_id = VIP_PORTA;
    			break;
    		case 1:
    			slice_id = VIP_SLICE2;	port_id = VIP_PORTA;
    			break;
    		case 2:
    			slice_id = VIP_SLICE1;	port_id = VIP_PORTB;
    			break;
    		case 3:
    			slice_id = VIP_SLICE2;	port_id = VIP_PORTB;
    			break;
    		default:
    			dev_err(&pdev->dev, "Unknown port reg=<%d>\n", p);
    			continue;
    		}
    
    		ret = alloc_port(shared->devs[slice_id], port_id);
    		if (ret < 0)
    			continue;
    
    		dev = shared->devs[slice_id];
    		dev->syscon_pol = syscon_pol;
    		dev->syscon_pol_offset = syscon_pol_offset;
    		port = dev->ports[port_id];
    
    		vip_register_subdev_notif(port, ep);
    	}
    	return 0;
    }
    
    static const struct of_device_id vip_of_match[];
    static int vip_probe(struct platform_device *pdev)
    {
    	struct vip_dev *dev;
    	struct vip_shared *shared;
    	struct vip_parser_data *parser;
    	const struct of_device_id *of_dev_id;
    	struct pinctrl *pinctrl;
    	int ret, slice = VIP_SLICE1;
    	u32 tmp, pid;
    	struct v4l2_ctrl_handler *hdl;
    
    	pm_runtime_enable(&pdev->dev);
    
    	ret = pm_runtime_get_sync(&pdev->dev);
    	if (ret)
    		goto err_runtime_get;
    
    	of_dev_id = of_match_device(vip_of_match, &pdev->dev);
    	if (!of_dev_id) {
    		dev_err(&pdev->dev, "%s: Unable to match device\n", __func__);
    		return -ENODEV;
    	}
    
    	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
    	if (ret) {
    		dev_err(&pdev->dev,
    			"32-bit consistent DMA enable failed\n");
    		return ret;
    	}
    
    	shared = devm_kzalloc(&pdev->dev, sizeof(*shared), GFP_KERNEL);
    	if (!shared)
    		return -ENOMEM;
    
    	shared->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vip");
    	shared->base = devm_ioremap_resource(&pdev->dev, shared->res);
    	if (IS_ERR(shared->base)) {
    		dev_err(&pdev->dev, "failed to ioremap\n");
    		ret = PTR_ERR(shared->base);
    		goto err_runtime_get;
    	}
    
    	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
    
    	/* Make sure H/W module has the right functionality */
    	pid = reg_read(shared, VIP_PID);
    	tmp = get_field(pid, VIP_PID_FUNC_MASK, VIP_PID_FUNC_SHIFT);
    
    	if (tmp != VIP_PID_FUNC) {
    		dev_info(&pdev->dev, "vip: unexpected PID function: 0x%x\n",
    			 tmp);
    		ret = -ENODEV;
    		goto err_runtime_get;
    	}
    
    	ret = v4l2_device_register(&pdev->dev, &shared->v4l2_dev);
    	if (ret)
    		goto err_runtime_get;
    
    	/* enable clocks, so the firmware will load properly */
    	vip_shared_set_clock_enable(shared, 1);
    	vip_top_vpdma_reset(shared);
    
    	platform_set_drvdata(pdev, shared);
    
    	for (slice = VIP_SLICE1; slice < VIP_NUM_SLICES; slice++) {
    		u32 vin_id;
    
    		dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
    		if (!dev) {
    			ret = -ENOMEM;
    			goto err_runtime_get;
    		}
    		dev->instance_id = (int)of_dev_id->data;
    		vin_id = 1 + ((dev->instance_id - 1) * 2) + slice;
    		snprintf(dev->name, sizeof(dev->name),
    			 "vin%d", vin_id);
    
    		dev->irq = platform_get_irq(pdev, slice);
    		if (!dev->irq) {
    			dev_err(&pdev->dev, "Could not get IRQ");
    			goto err_runtime_get;
    		}
    
    		if (devm_request_irq(&pdev->dev, dev->irq, vip_irq,
    				     0, dev->name, dev) < 0) {
    			ret = -ENOMEM;
    			goto dev_unreg;
    		}
    
    		spin_lock_init(&dev->slock);
    		spin_lock_init(&dev->lock);
    
    		mutex_init(&dev->mutex);
    
    		hdl = &dev->ctrl_handler;
    		v4l2_ctrl_handler_init(hdl, 11);
    		shared->v4l2_dev.ctrl_handler = hdl;
    
    		dev->slice_id = slice;
    		dev->pdev = pdev;
    		dev->res = shared->res;
    		dev->base = shared->base;
    		dev->v4l2_dev = &shared->v4l2_dev;
    
    		dev->shared = shared;
    		shared->devs[slice] = dev;
    
    		vip_top_reset(dev);
    		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
    
    		parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser),
    				      GFP_KERNEL);
    		if (!parser)
    			return PTR_ERR(parser);
    
    		parser->res = platform_get_resource_byname(pdev,
    							   IORESOURCE_MEM,
    							   (slice == 0) ?
    							   "parser0" :
    							   "parser1");
    		parser->base = devm_ioremap_resource(&pdev->dev, parser->res);
    		if (IS_ERR(parser->base)) {
    			ret = PTR_ERR(parser->base);
    			goto dev_unreg;
    		}
    		parser->pdev = pdev;
    		dev->parser = parser;
    
    		dev->sc_assigned = VIP_NOT_ASSIGNED;
    		dev->sc = sc_create(pdev, (slice == 0) ? "sc0" : "sc1");
    		if (IS_ERR(dev->sc)) {
    			ret = PTR_ERR(dev->sc);
    			goto dev_unreg;
    		}
    
    		dev->csc_assigned = VIP_NOT_ASSIGNED;
    		dev->csc = csc_create(pdev, (slice == 0) ? "csc0" : "csc1");
    		if (IS_ERR(dev->sc)) {
    			ret = PTR_ERR(dev->sc);
    			goto dev_unreg;
    		}
    	}
    
    	shared->vpdma = &shared->vpdma_data;
    	ret = vpdma_create(pdev, shared->vpdma, vip_vpdma_fw_cb);
    	if (ret) {
    		dev_err(&pdev->dev, "Creating VPDMA failed");
    		goto dev_unreg;
    	}
    
    	return 0;
    dev_unreg:
    	v4l2_device_unregister(&shared->v4l2_dev);
    err_runtime_get:
    	if (slice == VIP_SLICE1) {
    		pm_runtime_disable(&pdev->dev);
    		return ret;
    	} else {
    		return 0;
    	}
    }
    
    static int vip_remove(struct platform_device *pdev)
    {
    	struct vip_shared *shared = platform_get_drvdata(pdev);
    	struct vip_dev *dev;
    	int slice;
    
    	for (slice = 0; slice < VIP_NUM_SLICES; slice++) {
    		dev = shared->devs[slice];
    		if (!dev)
    			continue;
    
    		free_port(dev->ports[VIP_PORTA]);
    		free_port(dev->ports[VIP_PORTB]);
    	}
    	pm_runtime_put_sync(&pdev->dev);
    	pm_runtime_disable(&pdev->dev);
    
    	return 0;
    }
    
    #if defined(CONFIG_OF)
    static const struct of_device_id vip_of_match[] = {
    	{
    		.compatible = "ti,vip1", .data = (void *)VIP_INSTANCE1,
    	},
    
    	{
    		.compatible = "ti,vip2", .data = (void *)VIP_INSTANCE2,
    	},
    
    	{
    		.compatible = "ti,vip3", .data = (void *)VIP_INSTANCE3,
    	},
    	{},
    };
    MODULE_DEVICE_TABLE(of, vip_of_match);
    #endif
    
    static struct platform_driver vip_pdrv = {
    	.probe		= vip_probe,
    	.remove		= vip_remove,
    	.driver		= {
    		.name	= VIP_MODULE_NAME,
    		.of_match_table = of_match_ptr(vip_of_match),
    	},
    };
    
    module_platform_driver(vip_pdrv);
    
    MODULE_DESCRIPTION("TI VIP driver");
    MODULE_AUTHOR("Texas Instruments");
    MODULE_LICENSE("GPL v2");

    Could you give me some suggestions or any sample code for set_fmt and v4l2 settings for 1920x1080.

    Thanking you,

    Regards,

    Ramachandra

  • Hi Ramachandra,

    It looks like to_ov5640_dev API is not returning correct sensor pointer, can you please the implementation of this API, put some printk and see why it is not returning pointer to sensor object correctly? Is the pointer to v4l2_subdev object is correct?

    Regards,

    Brijesh

  • hi sir,

    Some changes we have done but same problem occurs


    am57xx-evm login: root
    root@am57xx-evm:~# cd ..
    root@am57xx-evm:/home# cd ..
    root@am57xx-evm:/# sudo dmesg | grep ov5640
    [ 7.466291] ov5640 2-003c: GPIO lookup for consumer powerdown
    [ 7.466298] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466311] of_get_named_gpiod_flags: can't parse 'powerdown-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466323] of_get_named_gpiod_flags: can't parse 'powerdown-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466330] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466337] ov5640 2-003c: No GPIO consumer powerdown found
    [ 7.466343] ov5640 2-003c: GPIO lookup for consumer reset
    [ 7.466349] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466360] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466369] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466375] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466380] ov5640 2-003c: No GPIO consumer reset found
    [ 7.466414] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
    [ 7.490765] ov5640 2-003c: Linked as a consumer to regulator.0
    [ 7.502895] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
    [ 7.512570] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
    [ 7.524857] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
    [ 8.763535] vin3a: Port A: Using subdev ov5640 2-003c for capture
    root@am57xx-evm:/# sudo dmesg | grep vip
    [ 8.715227] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [ 8.758480] vip 48990000.vip: VPDMA firmware loaded
    root@am57xx-evm:/# sudo dmesg | grep vpe
    [ 8.211160] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
    [ 8.324845] vpe 489d0000.vpe: Device registered as /dev/video0
    root@am57xx-evm:/# sudo dmesg | grep video*
    [ 0.598673] videodev: Linux video capture interface: v2.00
    [ 8.324845] vpe 489d0000.vpe: Device registered as /dev/video0
    [ 8.774865] vin3a-0: device registered as video1
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/
    common/ i2c/ platform/ usb/ v4l2-core/
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/common/v4l2-tpg/v4l2-tpg.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/common/v
    v4l2-tpg/ videobuf2/
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/common/videobuf2/videobuf2-vmalloc.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/i2c/
    adv7180.ko ml86v7667.ko mt9t11x.ko ov1063x.ko ov2659.ko ov490.ko ov5640.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/i2c/ov5640.ko
    insmod: ERROR: could not insert module /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/i2c/ov5640.ko: File exists
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/platform/
    soc_camera/ ti-vpe/ vivid/
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/platform/ti-vpe/ti-c
    ti-cal.ko ti-csc.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/platform/ti-vpe/ti-c
    ti-cal.ko ti-csc.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/platform/ti-vpe/ti-cal.ko
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/platform/
    soc_camera/ ti-vpe/ vivid/
    root@am57xx-evm:/# insmod /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/v4l2-core/[ 129.426789] NET: Registered protocol family 15
    [ 129.521551] Initializing XFRM netlink socket
    v4l2-fwnode.ko
    insmod: ERROR: could not insert module /lib/modules/4.19.59-g5f8c1c6121/kernel/drivers/media/v4l2-core/v4l2-fwnode.ko: File exists
    root@am57xx-evm:/# sudo dmesg | grep vpe
    [ 8.211160] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
    [ 8.324845] vpe 489d0000.vpe: Device registered as /dev/video0
    root@am57xx-evm:/# sudo dmesg | grep vip
    [ 8.715227] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [ 8.758480] vip 48990000.vip: VPDMA firmware loaded
    root@am57xx-evm:/# sudo dmesg | grep ov5640
    [ 7.466291] ov5640 2-003c: GPIO lookup for consumer powerdown
    [ 7.466298] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466311] of_get_named_gpiod_flags: can't parse 'powerdown-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466323] of_get_named_gpiod_flags: can't parse 'powerdown-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466330] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466337] ov5640 2-003c: No GPIO consumer powerdown found
    [ 7.466343] ov5640 2-003c: GPIO lookup for consumer reset
    [ 7.466349] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466360] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466369] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466375] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466380] ov5640 2-003c: No GPIO consumer reset found
    [ 7.466414] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
    [ 7.490765] ov5640 2-003c: Linked as a consumer to regulator.0
    [ 7.502895] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
    [ 7.512570] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
    [ 7.524857] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
    [ 8.763535] vin3a: Port A: Using subdev ov5640 2-003c for capture
    root@am57xx-evm:/# sudo dmesg | grep ov5640
    [ 7.466291] ov5640 2-003c: GPIO lookup for consumer powerdown
    [ 7.466298] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466311] of_get_named_gpiod_flags: can't parse 'powerdown-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466323] of_get_named_gpiod_flags: can't parse 'powerdown-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466330] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466337] ov5640 2-003c: No GPIO consumer powerdown found
    [ 7.466343] ov5640 2-003c: GPIO lookup for consumer reset
    [ 7.466349] ov5640 2-003c: using device tree for GPIO lookup
    [ 7.466360] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466369] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/ocp/i2c@48060000/ov5640@3C[0]'
    [ 7.466375] ov5640 2-003c: using lookup tables for GPIO lookup
    [ 7.466380] ov5640 2-003c: No GPIO consumer reset found
    [ 7.466414] ov5640 2-003c: 2-003c supply DOVDD not found, using dummy regulator
    [ 7.490765] ov5640 2-003c: Linked as a consumer to regulator.0
    [ 7.502895] ov5640 2-003c: 2-003c supply DVDD not found, using dummy regulator
    [ 7.512570] ov5640 2-003c: 2-003c supply AVDD not found, using dummy regulator
    [ 7.524857] ov5640 2-003c: ov5640_init_slave_id: client->addr= 3c
    [ 8.763535] vin3a: Port A: Using subdev ov5640 2-003c for capture
    root@am57xx-evm:/# sudo dmesg | grep vip
    [ 8.715227] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [ 8.758480] vip 48990000.vip: VPDMA firmware loaded
    root@am57xx-evm:/# sudo dmesg | grep vpe
    [ 8.211160] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
    [ 8.324845] vpe 489d0000.vpe: Device registered as /dev/video0
    root@am57xx-evm:/# lsmod
    Module Size Used by
    xfrm_user 32768 2
    xfrm4_tunnel 16384 0
    ipcomp 16384 0
    xfrm_ipcomp 16384 1 ipcomp
    esp4 20480 0
    ah4 16384 0
    af_key 36864 0
    xfrm_algo 16384 5 xfrm_user,esp4,ah4,af_key,xfrm_ipcomp
    ti_cal 36864 0
    videobuf2_vmalloc 16384 0
    v4l2_tpg 61440 0
    usbhid 24576 0
    pru_rproc 20480 0
    irq_pruss_intc 16384 1 pru_rproc
    xhci_plat_hcd 16384 0
    xhci_hcd 143360 1 xhci_plat_hcd
    pruss 16384 1 pru_rproc
    rpmsg_proto 16384 0
    rpmsg_rpc 32768 0
    dwc3 73728 0
    udc_core 28672 1 dwc3
    snd_soc_simple_card 16384 0
    snd_soc_simple_card_utils 16384 1 snd_soc_simple_card
    ahci_platform 16384 0
    ti_vip 49152 0
    libahci_platform 20480 1 ahci_platform
    pvrsrvkm 401408 4
    snd_soc_omap_hdmi 16384 0
    libahci 36864 2 ahci_platform,libahci_platform
    libata 208896 3 ahci_platform,libahci_platform,libahci
    ti_vpe 28672 0
    omap_aes_driver 24576 0
    omap_wdt 16384 0
    pruss_soc_bus 16384 0
    phy_omap_usb2 16384 3
    omap_sham 28672 0
    ti_sc 36864 2 ti_vpe,ti_vip
    ti_csc 16384 2 ti_vpe,ti_vip
    ti_vpdma 24576 2 ti_vpe,ti_vip
    omap_des 20480 0
    ov5640 32768 1
    snd_soc_tlv320aic3x 57344 1
    des_generic 28672 1 omap_des
    dwc3_omap 16384 0
    omap_hdq 16384 0
    at24 20480 0
    rtc_omap 20480 0
    v4l2_fwnode 20480 3 ov5640,ti_cal,ti_vip
    crypto_engine 16384 2 omap_des,omap_aes_driver
    wire 32768 1 omap_hdq
    rtc_palmas 16384 1
    extcon_palmas 16384 0
    omap_crypto 16384 2 omap_des,omap_aes_driver
    omap_remoteproc 20480 0
    virtio_rpmsg_bus 20480 0
    remoteproc 49152 4 pru_rproc,rpmsg_rpc,omap_remoteproc,rpmsg_proto
    sch_fq_codel 20480 3
    uio_module_drv 16384 0
    uio 20480 1 uio_module_drv
    ftdi_sio 40960 0
    usbserial 36864 1 ftdi_sio
    usbcore 217088 5 usbhid,ftdi_sio,usbserial,xhci_plat_hcd,xhci_hcd
    usb_common 16384 3 udc_core,usbcore,dwc3
    jailhouse 32768 0
    gdbserverproxy 16384 0
    cryptodev 49152 0
    cmemk 45056 2
    root@am57xx-evm:/# v4l2-ctl -d /dev/video1 --all
    Driver Info:
    Driver name : vip
    Card type : vip
    Bus info : platform:vip2:vin3a:stream0
    Driver version : 4.19.59
    Capabilities : 0x85200001
    Video Capture
    Read/Write
    Streaming
    Extended Pix Format
    Device Capabilities
    Device Caps : 0x05200001
    Video Capture
    Read/Write
    Streaming
    Extended Pix Format
    Priority: 2
    Video input : 0 (camera 1: ok)
    Video Standard = 0x00ffb0ff
    PAL-B/B1/G/H/I/D/D1/K
    NTSC-M/M-JP/M-KR
    SECAM-B/D/G/H/K/K1/L/Lc
    Format Video Capture:
    Width/Height : 1920/1080
    Pixel Format : 'NV12' (Y/CbCr 4:2:0)
    Field : None
    Bytes per Line : 1920
    Size Image : 3110400
    Colorspace : SMPTE 170M
    Transfer Function : Default (maps to Rec. 709)
    YCbCr/HSV Encoding: Default (maps to ITU-R 601)
    Quantization : Default (maps to Limited Range)
    Flags :
    Crop Capability Video Capture:
    Bounds : Left 0, Top 0, Width 1920, Height 1080
    Default : Left 0, Top 0, Width 1920, Height 1080
    Pixel Aspect: 1/1
    Crop Capability Video Output:
    Bounds : Left 0, Top 0, Width 1920, Height 1080
    Default : Left 0, Top 0, Width 1920, Height 1080
    Pixel Aspect: 1/1
    Selection Video Capture: crop, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Capture: compose_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Selection Video Output: compose_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
    Streaming Parameters Video Capture:
    Capabilities : timeperframe
    Frames per second: 30.000 (30/1)
    Read buffers : 4

    User Controls

    contrast 0x00980901 (int) : min=0 max=255 step=1 default=0 value=0 flags=slider
    saturation 0x00980902 (int) : min=0 max=255 step=1 default=64 value=64 flags=slider
    hue 0x00980903 (int) : min=0 max=359 step=1 default=0 value=0 flags=slider
    white_balance_automatic 0x0098090c (bool) : default=1 value=1 flags=update
    red_balance 0x0098090e (int) : min=0 max=4095 step=1 default=0 value=0 flags=inactive, slider
    blue_balance 0x0098090f (int) : min=0 max=4095 step=1 default=0 value=0 flags=inactive, slider
    [ 185.051097] omap_i2c 48060000.i2c: controller timed out
    [ 185.081092] ov5640 2-003c: ov5640_read_reg: error: reg=3500
    error 110 getting ctrl Exposure
    gain_automatic 0x00980912 (bool) : default=1 value=1 flags=update
    [ 186.091140] omap_i2c 48060000.i2c: timeout waiting for bus ready
    [ 186.097213] ov5640 2-003c: ov5640_read_reg: error: reg=350a
    error 16 getting ctrl Gain
    horizontal_flip 0x00980914 (bool) : default=0 value=0
    vertical_flip 0x00980915 (bool) : default=0 value=0
    power_line_frequency 0x00980918 (menu) : min=0 max=3 default=1 value=1
    0: Disabled
    1: 50 Hz
    2: 60 Hz
    3: Auto

    Camera Controls

    [ 187.111103] omap_i2c 48060000.i2c: timeout waiting for bus ready
    [ 187.117145] ov5640 2-003c: ov5640_read_reg: error: reg=3500
    error 16 getting ext_ctrl Auto Exposure

    Image Processing Controls

    pixel_rate 0x009f0902 (int64) : min=0 max=2147483647 step=1 default=55969920 value=84000000 flags=read-only
    test_pattern 0x009f0903 (menu) : min=0 max=1 default=0 value=0
    0: Disabled
    1: Color bars
    root@am57xx-evm:/#
    root@am57xx-evm:/# /etc/init.d/weston stop
    Stopping Weston
    root@am57xx-evm:/# sudo dmesg | grep vip
    [ 8.715227] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [ 8.758480] vip 48990000.vip: VPDMA firmware loaded
    root@am57xx-evm:/# cat /proc/interrupts | grep vip
    root@am57xx-evm:/# gst-launch-1.0 v4l2src device=/dev/video1 num-buffers=1000 io-mode=4 ! 'video/x-raw, format=(string)YUY2, width=(int)1920, height=(int)1080, framerate=30/1' ! queue ! vpe ! queue ! kmssink
    Setting pipeline to PAUSED ...
    Pipeline is live and does not need PREROLL ...
    Setting pipeline to PLAYING ...
    New clock: GstSystemClock
    [ 257.281092] omap_i2c 48060000.i2c: timeout waiting for bus ready
    [ 257.287139] ov5640 2-003c: ov5640_read_reg: error: reg=3503
    [ 257.293362] ------------[ cut here ]------------
    [ 257.298015] WARNING: CPU: 1 PID: 1281 at drivers/media/common/videobuf2/videobuf2-core.c:1363 vb2_start_streaming+0xec/0x154
    [ 257.309332] Modules linked in: xfrm_user xfrm4_tunnel ipcomp xfrm_ipcomp esp4 ah4 af_key xfrm_algo ti_cal videobuf2_vmalloc v4l2_tpg usbhid pru_rproc irq_pruss_intc xhci_plat_hcd xhci_hcd pruss rpmsg_proto rpmsg_rpc dwc3 udc_core snd_soc_simple_card snd_soc_simple_card_utils ahci_platform ti_vip libahci_platform pvrsrvkm(O) snd_soc_omap_hdmi libahci libata ti_vpe omap_aes_driver omap_wdt pruss_soc_bus phy_omap_usb2 omap_sham ti_sc ti_csc ti_vpdma omap_des ov5640 snd_soc_tlv320aic3x des_generic dwc3_omap omap_hdq at24 rtc_omap v4l2_fwnode crypto_engine wire rtc_palmas extcon_palmas omap_crypto omap_remoteproc virtio_rpmsg_bus remoteproc sch_fq_codel uio_module_drv(O) uio ftdi_sio usbserial usbcore usb_common jailhouse(O) gdbserverproxy(O) cryptodev(O) cmemk(O)
    [ 257.377227] CPU: 1 PID: 1281 Comm: v4l2src0:src Tainted: G O 4.19.59-g5f8c1c6121 #11
    [ 257.386310] Hardware name: Generic DRA74X (Flattened Device Tree)
    [ 257.392425] Backtrace:
    [ 257.394887] [<c020ca34>] (dump_backtrace) from [<c020cd6c>] (show_stack+0x18/0x1c)
    [ 257.402488] r7:c0e3e294 r6:60010013 r5:00000000 r4:c125005c
    [ 257.408171] [<c020cd54>] (show_stack) from [<c09f8c80>] (dump_stack+0x90/0xa4)
    [ 257.415425] [<c09f8bf0>] (dump_stack) from [<c022deb0>] (__warn+0xdc/0xf8)
    [ 257.422327] r7:c0e3e294 r6:00000009 r5:00000000 r4:00000000
    [ 257.428010] [<c022ddd4>] (__warn) from [<c022df14>] (warn_slowpath_null+0x48/0x50)
    [ 257.435610] r9:ec1b83a4 r8:00000001 r7:c07b4584 r6:c07d0310 r5:00000553 r4:c0e3e294
    [ 257.443385] [<c022decc>] (warn_slowpath_null) from [<c07d0310>] (vb2_start_streaming+0xec/0x154)
    [ 257.452205] r6:ec09d898 r5:ec09db14 r4:fffffff0
    [ 257.456841] [<c07d0224>] (vb2_start_streaming) from [<c07d24d8>] (vb2_core_streamon+0x88/0x164)
    [ 257.465574] r7:c07b4584 r6:c1204c48 r5:d2c31600 r4:ec09d898
    [ 257.471257] [<c07d2450>] (vb2_core_streamon) from [<c07d4f9c>] (vb2_streamon+0x1c/0x58)
    [ 257.479292] r5:d2c31600 r4:00000001
    [ 257.482881] [<c07d4f80>] (vb2_streamon) from [<c07d501c>] (vb2_ioctl_streamon+0x44/0x48)
    [ 257.491007] [<c07d4fd8>] (vb2_ioctl_streamon) from [<c07b45a8>] (v4l_streamon+0x24/0x28)
    [ 257.499129] r5:40045612 r4:c07d4fd8
    [ 257.502721] [<c07b4584>] (v4l_streamon) from [<c07b5a38>] (__video_do_ioctl+0x1f8/0x4e4)
    [ 257.510842] r5:40045612 r4:ec1e2c00
    [ 257.514433] [<c07b5840>] (__video_do_ioctl) from [<c07b9034>] (video_usercopy+0x260/0x4d0)
    [ 257.522730] r10:00000000 r9:d5051e04 r8:00000004 r7:00000001 r6:00000000 r5:c1204c48
    [ 257.530588] r4:40045612
    [ 257.533133] [<c07b8dd4>] (video_usercopy) from [<c07b92bc>] (video_ioctl2+0x18/0x1c)
    [ 257.540907] r10:ec9bd620 r9:d5050000 r8:00045cd8 r7:0000000d r6:d2c31600 r5:00045cd8
    [ 257.548765] r4:c07b92a4
    [ 257.551307] [<c07b92a4>] (video_ioctl2) from [<c07b2b20>] (v4l2_ioctl+0x44/0x58)
    [ 257.558734] [<c07b2adc>] (v4l2_ioctl) from [<c0368074>] (do_vfs_ioctl+0xb8/0x7c0)
    [ 257.566245] r5:00045cd8 r4:c1204c48
    [ 257.569833] [<c0367fbc>] (do_vfs_ioctl) from [<c03687b8>] (ksys_ioctl+0x3c/0x68)
    [ 257.577260] r10:00000036 r9:d5050000 r8:00045cd8 r7:40045612 r6:0000000d r5:d2c31600
    [ 257.585118] r4:d2c31601
    [ 257.587659] [<c036877c>] (ksys_ioctl) from [<c03687f4>] (sys_ioctl+0x10/0x14)
    [ 257.594823] r9:d5050000 r8:c0201204 r7:00000036 r6:b6a2b000 r5:b40120e0 r4:00000000
    [ 257.602599] [<c03687e4>] (sys_ioctl) from [<c0201000>] (ret_fast_syscall+0x0/0x4c)
    [ 257.610198] Exception stack(0xd5051fa8 to 0xd5051ff0)
    [ 257.615269] 1fa0: 00000000 b40120e0 0000000d 40045612 00045cd8 b6c3a150
    [ 257.623479] 1fc0: 00000000 b40120e0 b6a2b000 00000036 b400e210 b40116a8 00000002 00000001
    [ 257.631687] 1fe0: b6e02050 b49fec14 b69fd41f b6c3a15c
    [ 257.636816] ---[ end trace 7f2d8fc560f09821 ]---
    ERROR: from element /GstPipeline:pipeline0/GstV4l2Src:v4l2src0: Failed to allocate required memory.
    Additional debug info:
    ../../../gst-plugins-good-1.14.4/sys/v4l2/gstv4l2src.c(658): gst_v4l2src_decide_allocation (): /GstPipeline:pipeline0/GstV4l2Src:v4l2src0:
    Buffer pool activation failed
    Execution ended after 0:00:01.393910020
    Setting pipeline to PAUSED ...
    Setting pipeline to READY ...

    (gst-launch-1.0:1277): GStreamer-CRITICAL **: 16:11:53.632: gst_mini_object_unref: assertion 'GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) > 0' failed
    Setting pipeline to NULL ...
    Freeing pipeline ...
    root@am57xx-evm:/#

    It is some failure occurrs,

    1.Interrupt not generating (cat /proc/interrupts | grep vip)?

    2.In vip list not showing video1 i want to add any thing in dts or config file?

    My code:

    root@am57xx-evm:/# sudo dmesg | grep vip
    [ 8.715227] vip 48990000.vip: loading firmware vpdma-1b8.bin
    [ 8.758480] vip 48990000.vip: VPDMA firmware loaded

    Example code: (~# dmesg | grep vip
    [   11.760572] vip 48970000.vip: loading firmware vpdma-1b8.bin
    [   11.791094] vip 48970000.vip: VPDMA firmware loaded
    [   11.801269] vip1-s1: Port A: Using subdev ov1063x 4-0021 for capture
    [   11.825669] vip1-s1: device registered as video1)

    Could you give me some suggestions sir,

    Thanking you,


    Regards,
    Ramachandra
  • Hi sir,

    I am able capture the video now,

    Because of FPGA not sending proper data,

    Thanking you,

    Regards,

    Ramachandra