/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019 Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
 * Copyright (C) 2021 PHYTEC Messtechnik Gmbh
 * Author: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
 * Author: Stefan Riedmueller <s.riedmueller@phytec.de>
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>

#include <linux/of_graph.h>

#include <linux/v4l2-mediabus.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-ctrls.h>

#define DRIVER_VERSION "v0.1"

#define	AR052X_DATA_FORMAT_BITS				0x0112
#define		AR052X_FLD_DATA_FMT_IN_MASK		0xf00
#define		AR052X_FLD_DATA_FMT_IN_SHIFT		8
#define		AR052X_FLD_DATA_FMT_OUT_MASK		0xf
#define		AR052X_FLD_DATA_FMT_OUT_SHIFT		0
#define	AR052X_VT_PIX_CLK_DIV				0x0300
#define	AR052X_VT_SYS_CLK_DIV				0x0302
#define AR052X_PRE_PLL_CLK_DIV				0x0304
#define		AR052X_FLD_PLL_DIV2_MASK		0x3f00
#define		AR052X_FLD_PLL_DIV2_SHIFT		8
#define		AR052X_FLD_PLL_DIV1_MASK		0x3f
#define		AR052X_FLD_PLL_DIV1_SHIFT		0
#define AR052X_PLL_MUL					0x0306
#define		AR052X_FLD_PLL_MUL2_MASK		0xff00
#define		AR052X_FLD_PLL_MUL2_SHIFT		8
#define		AR052X_FLD_PLL_MUL1_MASK		0xff
#define		AR052X_FLD_PLL_MUL1_SHIFT		0
#define	AR052X_OP_PIX_CLK_DIV				0x0308
#define	AR052X_OP_SYS_CLK_DIV				0x030a
#define AR052X_X_ADDR_START				0x0344
#define AR052X_Y_ADDR_START				0x0346
#define AR052X_X_ADRR_END				0x0348
#define AR052X_Y_ADRR_END				0x034a
#define AR052X_X_OUTPUT_SIZE				0x034c
#define AR052X_Y_OUTPUT_SIZE				0x034e

#define AR052X_MODEL_ID					0x3000
#define AR052X_FRAME_LENGTH_LINES			0x300a
#define AR052X_LINE_LENGTH_PCK				0x300c
#define AR052X_REVISION					0x300e
#define	AR052X_COARSE_INT_TIME				0x3012
#define	AR052X_FINE_INT_TIME				0x3014
#define	AR052X_EXTRA_DELAY				0x3018
#define AR052X_RESET_REGISTER				0x301a
#define		AR052X_FLD_GROUPED_PARAM_HOLD		BIT(15)
#define		AR052X_FLD_SMIA_SER_DIS			BIT(12)
#define		AR052X_FLD_FORCED_PLL_ON		BIT(11)
#define		AR052X_FLD_RESTART_BAD			BIT(10)
#define		AR052X_FLD_MASK_BAD			BIT(9)
#define		AR052X_FLD_GPI_EN			BIT(8)
#define		AR052X_FLD_LOCK_REG			BIT(3)
#define		AR052X_FLD_STREAM			BIT(2)
#define		AR052X_FLD_RESTART			BIT(1)
#define		AR052X_FLD_RESET			BIT(0)
#define	AR052X_DATA_PEDESTAL				0x301e
#define AR052X_FRAME_COUNT				0x303a
#define AR052X_FRAME_STATUS				0x303c
#define		AR052X_FLD_PLL_LOCKED			BIT(3)
#define		AR052X_FLD_FRAME_START_DURING_GPH	BIT(2)
#define		AR052X_FLD_STANDBY_STATUS		BIT(1)
#define		AR052X_FLD_FRAMESYNC			BIT(0)
#define	AR052X_READ_MODE				0x3040
#define		AR052X_FLD_VERT_FLIP			BIT(15)
#define		AR052X_FLD_HORIZ_MIRR			BIT(14)
#define		AR052X_FLD_X_BIN_EN			BIT(11)
#define	AR052X_FLASH					0x3046
#define		AR052X_FLD_XENON_FLASH			BIT(13)
#define		AR052X_FLD_LED_FLASH			BIT(8)
#define		AR052X_FLD_INVERT_FLASH			BIT(7)
#define	AR052X_FLASH_COUNT				0x3048
#define	AR052X_GREENR_GAIN				0x3056
#define	AR052X_BLUE_GAIN				0x3058
#define	AR052X_RED_GAIN					0x305a
#define	AR052X_GREENB_GAIN				0x305c
#define	AR052X_GLOBAL_GAIN				0x305e
#define		AR052X_FLD_DIGITAL_GAIN_MASK		0xff80
#define		AR052X_FLD_DIGITAL_GAIN_SHIFT		7
#define		AR052X_FLD_ANA_COARSE_GAIN_MASK		0x70
#define		AR052X_FLD_ANA_COARSE_GAIN_SHIFT	4
#define		AR052X_FLD_ANA_FINE_GAIN_MASK		0xf
#define		AR052X_FLD_ANA_FINE_GAIN_SHIFT		0
#define	AR052X_TEST_PATTERN				0x3070
#define		AR052X_FLD_NO_TESTPATTERN		0
#define		AR052X_FLD_SOLIDCOLOR			1
#define		AR052X_FLD_FULL_COLOR_BAR		2
#define		AR052X_FLD_FADE_TO_GRAY			3
#define		AR052X_FLD_PN9_LINK_INT			4
#define		AR052X_FLD_WALKING_ONES_10BIT		256
#define		AR052X_FLD_WALKING_ONES_8BIT		257
#define	AR052X_TEST_DATA_RED				0x3072
#define	AR052X_TEST_DATA_GREENR				0x3074
#define	AR052X_TEST_DATA_BLUE				0x3076
#define	AR052X_TEST_DATA_GREENB				0x3078
#define	AR052X_X_ODD_INC				0x30a2
#define	AR052X_Y_ODD_INC				0x30a6
#define	AR052X_DIGITAL_TEST				0x30b0
#define		AR052X_FLD_PLL_BYPASS			BIT(14)
#define		AR052X_FLD_PIXCLK_ON			BIT(8)
#define		AR052X_FLD_MONOCHROME_OP		BIT(7)
#define AR052X_TEMPSENS_DATA				0x30b2
#define AR052X_TEMPSENS_CTRL				0x30b4
#define		AR052X_FLD_RETRIG_THRESHOLD_MASK	0xfc
#define		AR052X_FLD_RETRIG_THRESHOLD_SHIFT	6
#define		AR052X_FLD_TEMP_CLEAR			BIT(5)
#define		AR052X_FLD_TEMP_START_CONV		BIT(4)
#define		AR052X_FLD_TEMPSENS_PWRON		BIT(0)
#define AR052X_TEMPSENS_CALIB1				0x30c6
#define AR052X_TEMPSENS_CALIB2				0x30c8

#define AR052X_AECTRL					0x3100
#define		AR052X_FLD_MIN_ANA_GAIN_MASK		0x60
#define		AR052X_FLD_MIN_ANA_GAIN_SHIFT		5
#define		AR052X_FLD_AUTO_DG_EN			BIT(4)
#define		AR052X_FLD_AUTO_AG_EN			BIT(1)
#define		AR052X_FLD_AE_EN			BIT(0)
#define	AR052X_AE_LUMA_TGT				0x3102
#define AR052X_AE_MAX_EXPOSURE				0x311c
#define	AR052X_AE_MIN_EXPOSURE				0x311e
#define	AR052X_AE_COARSE_INT_TIME			0x3164
#define	AR052X_DELTA_DK_CTRL				0x3180
#define		AR052X_FLD_DK_SUB_EN			BIT(15)
#define		AR052X_FLD_DK_EVERY_FRAME		BIT(14)
#define		AR052X_FLD_DK_RECALC			BIT(13)
#define	AR052X_SERIAL_FORMAT				0x31ae
#define		AR052X_FLD_TYPE_MASK			0x300
#define		AR052X_FLD_TYPE_SHIFT			8
#define		AR052X_FLD_TYPE_MIPI			2
#define		AR052X_FLD_LANES_MASK			0xf
#define		AR052X_FLD_LANES_SHIFT			0
#define AR052X_MIPI_TIMING_0				0x31b4
#define		AR052X_FLD_HS_PREP_MASK			0xf000
#define		AR052X_FLD_HS_PREP_SHIFT		12
#define		AR052X_FLD_HS_ZERO_MASK			0xfc0
#define		AR052X_FLD_HS_ZERO_SHIFT		6
#define		AR052X_FLD_HS_TRAIL_MASK		0x3e
#define		AR052X_FLD_HS_TRAIL_SHIFT		1
#define AR052X_MIPI_TIMING_1				0x31b6
#define		AR052X_FLD_CLK_PREP_MASK		0xf000
#define		AR052X_FLD_CLK_PREP_SHIFT		12
#define		AR052X_FLD_CLK_ZERO_MASK		0xfe0
#define		AR052X_FLD_CLK_ZERO_SHIFT		5
#define		AR052X_FLD_CLK_TRAIL_MASK		0x1f
#define		AR052X_FLD_CLK_TRAIL_SHIFT		0
#define AR052X_MIPI_TIMING_2				0x31b8
#define		AR052X_FLD_BGAP_MASK			0xfc00
#define		AR052X_FLD_BGAP_SHIFT			10
#define		AR052X_FLD_CLK_PRE_MASK			0x3f0
#define		AR052X_FLD_CLK_PRE_SHIFT		4
#define		AR052X_FLD_CLK_POST_MSBS_MASK		0xf
#define		AR052X_FLD_CLK_POST_MSBS_SHIFT		0
#define AR052X_MIPI_TIMING_3				0x31ba
#define		AR052X_FLD_LPX_MASK			0xfc00
#define		AR052X_FLD_LPX_SHIFT			10
#define		AR052X_FLD_WAKE_UP_MASK			0x3f8
#define		AR052X_FLD_WAKE_UP_SHIFT		3
#define		AR052X_FLD_CLK_POST_MASK		0x3
#define		AR052X_FLD_CLK_POST_SHIFT		0
#define AR052X_MIPI_TIMING_4				0x31bc
#define		AR052X_FLD_CONT_TX_CLK			BIT(15)
#define		AR052X_FLD_VREG_MODE			BIT(13)
#define		AR052X_FLD_HS_EXIT_MASK			0x1f80
#define		AR052X_FLD_HS_EXIT_SHIFT		7
#define		AR052X_FLD_INIT_MASK			0x7f
#define		AR052X_FLD_INIT_SHIFT			0
#define AR052X_SER_CTRL_STAT				0x31c6
#define		AR052X_FLD_FRAMER_TEST_MODE		BIT(7)
#define AR052X_SERIAL_TEST				0x3066
#define		AR052X_FLD_TEST_LANE_EN_MASK		0x3c0
#define		AR052X_FLD_TEST_LANE_EN_SHIFT		6
#define		AR052X_FLD_TEST_LANE_0			0x1
#define		AR052X_FLD_TEST_LANE_1			0x2
#define		AR052X_FLD_TEST_LANE_2			0x4
#define		AR052X_FLD_TEST_LANE_3			0x8
#define		AR052X_FLD_TEST_MODE_MASK		0x3c
#define		AR052X_FLD_TEST_MODE_SHIFT		2
#define		AR052X_FLD_TEST_MODE_LP11		0x1
#define	AR052X_PIX_DEF_ID				0x31e0
#define		AR052X_FLD_PIX_DEF_2D_COUPLE_EN		BIT(10)
#define		AR052X_FLD_PIX_DEF_2D_SINGLE_EN		BIT(9)
#define		AR052X_FLD_PIX_DEF_2D_FUSE_EN		BIT(8)
#define		AR052X_FLD_PIX_DEF_ID_LOC_CORR_EN	BIT(7)
#define		AR052X_FLD_PIX_DEF_ID_EN		BIT(0)
#define AR052X_CUSTOMER_REV				0x31fe

#define AR052X_MIPI_CNTRL				0x3354
#define		AR052X_FLD_CHAN_NUM_MASK		0xc0
#define		AR052X_FLD_CHAN_NUM_SHIFT		6
#define		AR052X_FLD_DATA_TYPE_MASK		0x3f
#define		AR052X_FLD_DATA_TYPE_SHIFT		0
#define		AR052X_FLD_CSI2_DT_RAW8			0x2a
#define		AR052X_FLD_CSI2_DT_RAW10		0x2b
#define		AR052X_FLD_CSI2_DT_RAW12		0x2c

#define AR052X_CHIP_ID			0x0457
#define AR052X_DEF_WIDTH		2592
#define AR052X_DEF_HEIGHT		1944


enum {
	V4L2_CID_USER_BASE_AR052X		= V4L2_CID_USER_BASE + 0x2500,

	V4L2_CID_X_BINNING_COL,
	V4L2_CID_X_EXTRA_BLANKING,

	V4L2_CID_X_DIGITAL_GAIN_RED,
	V4L2_CID_X_DIGITAL_GAIN_GREENR,
	V4L2_CID_X_DIGITAL_GAIN_BLUE,
	V4L2_CID_X_DIGITAL_GAIN_GREENB,

	V4L2_CID_X_DYNAMIC_PIXEL_CORRECTION,

	V4L2_CID_X_FLASH_INVERT,
	V4L2_CID_X_FLASH_XENON_WIDTH,
};

enum ar052x_model {
	AR052X_MODEL_UNKNOWN,
	AR052X_MODEL_COLOR,
	AR052X_MODEL_MONOCHROME,
};

struct ar052x_format {
	unsigned int code;
	unsigned int bpp;
	unsigned int x_offset;
	unsigned int y_offset;
};

static const struct ar052x_format ar052x_mono_formats[] = {
	{
		.code	= MEDIA_BUS_FMT_Y8_1X8,
		.bpp	= 8,
	}, {
		.code	= MEDIA_BUS_FMT_Y10_1X10,
		.bpp	= 10,
	}, {
		.code	= MEDIA_BUS_FMT_Y12_1X12,
		.bpp	= 12,
	},
};

static const struct ar052x_format ar052x_col_formats[] = {
	{
		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
		.bpp		= 8,
		.x_offset	= 1,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
		.bpp		= 8,
		.x_offset	= 0,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
		.bpp		= 8,
		.x_offset	= 1,
		.y_offset	= 0,
	}, {
		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
		.bpp		= 8,
		.x_offset	= 0,
		.y_offset	= 0,
	}, {
		.code		= MEDIA_BUS_FMT_SGBRG10_1X10,
		.bpp		= 10,
		.x_offset	= 1,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
		.bpp		= 10,
		.x_offset	= 0,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SRGGB10_1X10,
		.bpp		= 10,
		.x_offset	= 1,
		.y_offset	= 0,
	}, {
		.code		= MEDIA_BUS_FMT_SGRBG10_1X10,
		.bpp		= 10,
		.x_offset	= 0,
		.y_offset	= 0,
	}, {
		.code		= MEDIA_BUS_FMT_SGBRG12_1X12,
		.bpp		= 12,
		.x_offset	= 1,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SBGGR12_1X12,
		.bpp		= 12,
		.x_offset	= 0,
		.y_offset	= 1,
	}, {
		.code		= MEDIA_BUS_FMT_SRGGB12_1X12,
		.bpp		= 12,
		.x_offset	= 1,
		.y_offset	= 0,
	}, {
		.code		= MEDIA_BUS_FMT_SGRBG12_1X12,
		.bpp		= 12,
		.x_offset	= 0,
		.y_offset	= 0,
	},
};

struct limit_range {
	unsigned long min;
	unsigned long max;
};

struct ar052x_sensor_limits {
	struct limit_range x;
	struct limit_range y;

	struct limit_range hlen;
	struct limit_range vlen;

	struct limit_range hblank;
	struct limit_range vblank;

	struct limit_range pll_div;
	struct limit_range pll_mul;

	struct limit_range pll_vt_sys_clk_div;
	struct limit_range pll_vt_pix_clk_div;
	struct limit_range pll_op_sys_clk_div;
	struct limit_range pll_op_pix_clk_div;

	struct limit_range ext_clk;
	struct limit_range pix_clk;
	struct limit_range op_clk;
	struct limit_range vco;
};

struct ar052x_businfo {
	unsigned int num_lanes;

	u16 t_hs_prep;
	u16 t_hs_zero;
	u16 t_hs_trail;
	u16 t_clk_prep;
	u16 t_clk_zero;
	u16 t_clk_trail;
	u16 t_bgap;
	u16 t_clk_pre;
	u16 t_clk_post_msbs;
	u16 t_lpx;
	u16 t_wakeup;
	u16 t_clk_post;
	u16 t_hs_exit;
	u16 t_init;
	bool cont_tx_clk;
	bool vreg_mode;
};

struct ar052x_pll_config {
	unsigned int pll2_div;
	unsigned int pll2_mul;
	unsigned int pll_div;
	unsigned int pll_mul;
	unsigned int vt_sys_div;
	unsigned int vt_pix_div;
	unsigned int op_sys_div;
	unsigned int op_pix_div;
	unsigned long vco_freq;
	unsigned long pix_freq;
	unsigned long ser_freq;
	bool update;
	bool manual_pll;
};

struct ar052x_gains {
	struct v4l2_ctrl *red_ctrl;
	struct v4l2_ctrl *greenb_ctrl;
	struct v4l2_ctrl *greenr_ctrl;
	struct v4l2_ctrl *blue_ctrl;
	unsigned int red_clip;
	unsigned int greenb_clip;
	unsigned int greenr_clip;
	unsigned int blue_clip;
};

struct ar052x {
	struct v4l2_subdev subdev;
	struct v4l2_ctrl_handler ctrls;
	struct media_pad pad;

	struct v4l2_mbus_framefmt fmt;
	struct v4l2_rect crop;
	struct v4l2_fract interval;
	unsigned int bpp;
	unsigned int w_scale;
	unsigned int h_scale;
	unsigned int vblank;
	unsigned int hblank;

	struct ar052x_businfo info;
	struct ar052x_pll_config pll;
	struct ar052x_sensor_limits limits;
	enum ar052x_model model;

	const struct ar052x_format *formats;
	unsigned int num_fmts;

	struct v4l2_ctrl *link_freq_ctrl;
	struct ar052x_gains gains;

	struct dentry *debugfs_root;

	struct clk *extclk;
	struct gpio_desc *reset_gpio;

	struct mutex lock;

	int power_user;
	bool is_streaming;
};

static inline struct ar052x *to_ar052x(struct v4l2_subdev *sd)
{
	return container_of(sd, struct ar052x, subdev);
}

static int ar052x_read(struct ar052x *sensor, u16 reg, u16 *val)
{
	struct i2c_client *i2c = v4l2_get_subdevdata(&sensor->subdev);
	unsigned char reg_buf[2], read_buf[2];
	int ret;
	u16 result;
	struct i2c_msg xfer[] = {
		[0] = {
			.addr = i2c->addr,
			.flags = 0,
			.len = 2,
			.buf = reg_buf,
		},
		[1] = {
			.addr = i2c->addr,
			.flags = I2C_M_RD,
			.len = 2,
			.buf = read_buf,
		},
	};

	reg_buf[0] = (reg >> 8) & 0xff;
	reg_buf[1] = reg & 0xff;

	ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer));
	if (ret == ARRAY_SIZE(xfer))
		ret = 0;
	else if (ret >= 0)
		ret = -EIO;

	if (ret < 0)
		return ret;

	result = read_buf[0] << 8;
	result |= read_buf[1];

	*val = result;

	if (ret)
		dev_err(&i2c->dev, "Failed to read i2c message (%d)\n", ret);

	return ret;
}

static int ar052x_write(struct ar052x *sensor, u16 reg, u16 val)
{
	struct i2c_client *i2c = v4l2_get_subdevdata(&sensor->subdev);
	unsigned char write_buf[4];
	int ret;
	struct i2c_msg xfer[] = {
		[0] = {
			.addr = i2c->addr,
			.flags = 0,
			.len = 4,
			.buf = write_buf,
		},
	};

	write_buf[0] = (reg >> 8) & 0xff;
	write_buf[1] = reg & 0xff;

	write_buf[2] = (val >> 8) & 0xff;
	write_buf[3] = val & 0xff;

	ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer));
	if (ret == ARRAY_SIZE(xfer))
		ret = 0;
	else if (ret >= 0)
		ret = -EIO;

	dev_dbg(&i2c->dev, "Wrote i2c message 0x%02x at 0x%02x (%d) \n",
		val, reg, ret);

	if (ret)
		dev_err(&i2c->dev, "Failed to write i2c message (%d)\n", ret);

	return ret;
}

static int ar052x_update_bits(struct ar052x *sensor, u16 reg,
			      u16 mask, u16 val)
{
	u16 orig, tmp;
	int ret;

	ret = ar052x_read(sensor, reg, &orig);
	if (ret < 0)
		return ret;

	tmp = orig & ~mask;
	tmp |= val & mask;

	if (tmp != orig)
		ret = ar052x_write(sensor, reg, tmp);

	return ret;
}

static int ar052x_debugfs_init(struct ar052x *sensor)
{
	struct dentry *d;

	if (!debugfs_initialized())
		return -ENODEV;

	sensor->debugfs_root = debugfs_create_dir(dev_name(sensor->subdev.dev),
						  NULL);
	if (!sensor->debugfs_root)
		return -ENOMEM;

	d = debugfs_create_bool("manual_pll", 0600, sensor->debugfs_root,
				&sensor->pll.manual_pll);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("pll2_div", 0600, sensor->debugfs_root,
				&sensor->pll.pll2_div);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("pll_div", 0600, sensor->debugfs_root,
				&sensor->pll.pll_div);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("pll2_mul", 0600, sensor->debugfs_root,
				&sensor->pll.pll2_mul);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("pll_mul", 0600, sensor->debugfs_root,
				&sensor->pll.pll_mul);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("vt_sys_div", 0600, sensor->debugfs_root,
				&sensor->pll.vt_sys_div);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("vt_pix_div", 0600, sensor->debugfs_root,
				&sensor->pll.vt_pix_div);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("op_sys_div", 0600, sensor->debugfs_root,
				&sensor->pll.op_sys_div);
	if (!d)
		goto remove_debugfs;

	d = debugfs_create_u32("op_pix_div", 0600, sensor->debugfs_root,
				&sensor->pll.op_pix_div);
	if (!d)
		goto remove_debugfs;

	return 0;

remove_debugfs:
	debugfs_remove_recursive(sensor->debugfs_root);
	return -ENOMEM;

}

static void ar052x_debugfs_remove(struct ar052x *sensor)
{
	debugfs_remove_recursive(sensor->debugfs_root);
}

static const struct ar052x_format *ar052x_find_format(struct ar052x *sensor,
						      u32 code)
{
	int i;

	for (i = 0; i < sensor->num_fmts; i++)
		if (sensor->formats[i].code == code)
			return &sensor->formats[i];

	return &sensor->formats[sensor->num_fmts - 1];
}

static const unsigned int ar052x_match_col_format(struct ar052x *sensor,
						  struct v4l2_rect *crop)
{
	unsigned int x_offset = crop->left % 2;
	unsigned int y_offset = crop->top % 2;
	int i;

	for (i = 0; i < sensor->num_fmts; i++) {
		if (sensor->bpp != sensor->formats[i].bpp)
			continue;

		if (sensor->formats[i].x_offset == x_offset &&
		    sensor->formats[i].y_offset == y_offset)
			return sensor->formats[i].code;
	}

	return sensor->formats[sensor->num_fmts - 1].code;
}

static int ar052x_enter_standby(struct ar052x *sensor)
{
	unsigned int timeout = 1000;
	int ret;
	u16 val;

	ret = ar052x_update_bits(sensor, AR052X_RESET_REGISTER,
				 AR052X_FLD_STREAM | AR052X_FLD_GPI_EN, 0);
	if (ret)
		return ret;

	while (timeout) {
		ar052x_read(sensor, AR052X_FRAME_STATUS, &val);

		if (val & AR052X_FLD_STANDBY_STATUS) {
			break;
		}

		timeout--;

		if (timeout == 0) {
			v4l2_warn(&sensor->subdev,
				  "timeout while trying to enter standby\n");
			break;
		}

		usleep_range(2000, 3000);
	}

	msleep(100);
	ret = ar052x_update_bits(sensor, AR052X_RESET_REGISTER,
				 AR052X_FLD_SMIA_SER_DIS,
				 AR052X_FLD_SMIA_SER_DIS);
	if (ret)
		return ret;

	ret = ar052x_update_bits(sensor, AR052X_SER_CTRL_STAT,
				 AR052X_FLD_FRAMER_TEST_MODE, 0);

	return ret;
}

static int ar052x_mipi_enter_lp11(struct ar052x *sensor)
{
	int ret = 0;
	u16 val;

	ret = ar052x_read(sensor, AR052X_SER_CTRL_STAT, &val);
	if (ret)
		return ret;

	val = AR052X_FLD_TEST_MODE_LP11 << AR052X_FLD_TEST_MODE_SHIFT |
	      AR052X_FLD_TEST_LANE_0 << AR052X_FLD_TEST_LANE_EN_SHIFT |
	      AR052X_FLD_TEST_LANE_1 << AR052X_FLD_TEST_LANE_EN_SHIFT |
	      AR052X_FLD_TEST_LANE_2 << AR052X_FLD_TEST_LANE_EN_SHIFT |
	      AR052X_FLD_TEST_LANE_3 << AR052X_FLD_TEST_LANE_EN_SHIFT;

	/* TODO: Do we need to differentiate between different num of lanes? */

	ret = ar052x_write(sensor, AR052X_SERIAL_TEST, val);
	if (ret)
		return ret;

	ret = ar052x_update_bits(sensor,
				 AR052X_SER_CTRL_STAT,
				 AR052X_FLD_FRAMER_TEST_MODE,
				 AR052X_FLD_FRAMER_TEST_MODE);
	if (ret)
		return ret;

	ret = ar052x_update_bits(sensor,
				 AR052X_RESET_REGISTER,
				 AR052X_FLD_STREAM |
				 AR052X_FLD_SMIA_SER_DIS,
				 AR052X_FLD_STREAM);
	return ret;
}

static void ar052x_reset(struct ar052x *sensor)
{
	unsigned long ext_freq = clk_get_rate(sensor->extclk);
	unsigned long ext_freq_mhz = ext_freq / 1000000;
	unsigned long wait_usecs;

	if (sensor->reset_gpio) {
		gpiod_set_value_cansleep(sensor->reset_gpio, 1);
		usleep_range(1000, 1100);
		gpiod_set_value_cansleep(sensor->reset_gpio, 0);
	} else {
		ar052x_update_bits(sensor, AR052X_RESET_REGISTER,
				   AR052X_FLD_RESET, AR052X_FLD_RESET);
	}

	wait_usecs = 160000 / ext_freq_mhz;
	usleep_range(wait_usecs, wait_usecs + 1000);
}

static int ar052x_power_on(struct ar052x *sensor)
{
	/* TODO: Enable power, clocks, etc... */
	return 0;
}

static void ar052x_power_off(struct ar052x *sensor)
{
	/* TODO: Disable power, clocks, etc... */
}

/* V4L2 subdev core ops */
static int ar052x_s_power(struct v4l2_subdev *sd, int on)
{
	struct ar052x *sensor = to_ar052x(sd);
	int ret = 0;
	int link_freq;

	dev_dbg(sd->dev, "%s on: %d\n", __func__, on);

	mutex_lock(&sensor->lock);

	if (on) {
		if (sensor->power_user > 0) {
			sensor->power_user++;
			goto out;
		}

		ret = ar052x_power_on(sensor);
		if (ret)
			goto out;

		/* Enable MIPI LP-11 test mode as required by e.g. i.MX 6 */
		if (!sensor->is_streaming) {
			ret = ar052x_mipi_enter_lp11(sensor);
			if (ret) {
				ar052x_power_off(sensor);
				goto out;
			}

			/* Select the correct link frequency depending on
			 * bits per pixel
			 */
			/* TODO: Can we remove these magic values here? */
			link_freq = (sensor->bpp - 8) / 2;

			ret = v4l2_ctrl_s_ctrl(sensor->link_freq_ctrl,
					       link_freq);
			if (ret) {
				v4l2_err(&sensor->subdev,
					 "Failed to set link freq ctrl\n");
				ar052x_power_off(sensor);
				goto out;
			}
		}

		sensor->power_user++;

	} else {
		sensor->power_user--;
		if (sensor->power_user < 0) {
			dev_err(sd->dev, "More s_power OFF than ON\n");
			ret = -EINVAL;
			goto out;
		}

		if (sensor->power_user == 0) {
			ar052x_enter_standby(sensor);
			ar052x_power_off(sensor);
		}
	}

out:
	mutex_unlock(&sensor->lock);
	return ret;
}

#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ar052x_s_register(struct v4l2_subdev *sd,
			     const struct v4l2_dbg_register *reg)
{
	struct ar052x *sensor = to_ar052x(sd);
	int ret;

	dev_dbg(sd->dev, "%s\n", __func__);

	ret = ar052x_write(sensor, reg->reg, reg->val);
	return ret;
}

static int ar052x_g_register(struct v4l2_subdev *sd,
			     struct v4l2_dbg_register *reg)
{
	struct ar052x *sensor = to_ar052x(sd);
	int ret;

	dev_dbg(sd->dev, "%s\n", __func__);

	ret = ar052x_read(sensor, reg->reg, (u16 *)&reg->val);
	return ret;
}
#endif

static unsigned long ar052x_clk_div_mul(unsigned long freq,
					unsigned int div,
					unsigned int mul)
{
	uint64_t result;

	if (WARN_ON(div == 0))
		return 0;

	result = freq;
	result *= mul;
	result = div_u64(result, div);

	return result;
}

static int ar052x_calculate_pll(struct ar052x *sensor)
{
	struct ar052x_sensor_limits *limits = &sensor->limits;
	struct v4l2_subdev *sd = &sensor->subdev;
	unsigned long ext_freq = clk_get_rate(sensor->extclk);
	unsigned long op_clk;
	unsigned long vco;
	unsigned long pix_clk;
	unsigned long pix_clk_target;
	long diff, diff_old;
	unsigned int pll_div, pll_div_min, pll_div_max;
	unsigned int pll_mul, pll_mul_min, pll_mul_max;
	unsigned int bpp = sensor->bpp;

	if (sensor->pll.manual_pll) {
		pll_div = sensor->pll.pll2_div;
		pll_mul = sensor->pll.pll2_mul;
		vco = ar052x_clk_div_mul(ext_freq, pll_div, pll_mul);
		pix_clk = ar052x_clk_div_mul(vco, sensor->pll.vt_pix_div, 1);

		sensor->pll.vco_freq = vco;
		sensor->pll.pix_freq = pix_clk;
		sensor->pll.ser_freq = ar052x_clk_div_mul(pix_clk, 2, bpp);

		goto out;
	}

	if (!sensor->pll.update)
		return 0;

	pix_clk_target = 207000000;

	diff_old = pix_clk_target;

	pll_div_min = limits->pll_div.min;
	pll_div_max = limits->pll_div.max;
	pll_mul_min = limits->pll_mul.min;
	pll_mul_max = limits->pll_mul.max;
	sensor->pll.pll_div = 3;
	sensor->pll.pll_mul = 89;
	sensor->pll.pll2_div = 1;
	sensor->pll.pll2_mul = 0;
	sensor->pll.op_sys_div = 1;
	sensor->pll.op_pix_div = 2;
	sensor->pll.vt_sys_div = 1;
	sensor->pll.vt_pix_div = bpp / 2;

	pll_div = pll_div_min;
	pll_mul = pll_mul_min - 1;

	while (pll_div <= pll_div_max) {
		if (pll_mul % 2 != 0)
			pll_mul++;

		if (pll_mul > pll_mul_max) {
			pll_mul = pll_mul_min;
			pll_div++;
			if (pll_div > pll_div_max)
				break;
		}

		vco = ar052x_clk_div_mul(ext_freq, pll_div, pll_mul);

		if (vco < limits->vco.min || vco > limits->vco.max) {
			pll_mul++;
			continue;
		}

		pix_clk = ar052x_clk_div_mul(vco, sensor->pll.vt_pix_div, 1);
		op_clk = ar052x_clk_div_mul(pix_clk, 2, 1);

		if (pix_clk < limits->pix_clk.min ||
		    pix_clk > limits->pix_clk.max) {
			pll_mul++;
			continue;
		}

		if (op_clk < limits->op_clk.min ||
		    op_clk > limits->op_clk.max) {
			pll_mul++;
			continue;
		}

		diff = abs(pix_clk_target - pix_clk);
		if (diff >= diff_old) {
			pll_mul++;
			continue;
		}

		dev_dbg(sd->dev, "%s: vco: %lu pix_clk: %lu op_clk: %lu\n",
			__func__, vco, pix_clk, op_clk);
		dev_dbg(sd->dev,
			"%s pll2_div: %d pll2_mul: %d\n",
			__func__, pll_div, pll_mul);

		diff_old = diff;

		sensor->pll.pll2_div = pll_div;
		sensor->pll.pll2_mul = pll_mul;
		sensor->pll.vco_freq = vco;
		sensor->pll.pix_freq = pix_clk;
		sensor->pll.ser_freq = ar052x_clk_div_mul(op_clk, 1, bpp);

		pll_mul++;
	}

	if (sensor->pll.pll2_mul == 0) {
		v4l2_err(sd, "Unable to find matching pll config\n");
		return -EINVAL;
	}

out:
	sensor->pll.update = false;

	dev_dbg(sd->dev, "VCO: %ld PIX: %ld SER: %ld\n",
		sensor->pll.vco_freq, sensor->pll.pix_freq,
		sensor->pll.ser_freq);
	dev_dbg(sd->dev, "PLLDIV: %d PLLMUL: %d\n",
		sensor->pll.pll_div, sensor->pll.pll_mul);
	dev_dbg(sd->dev, "PLL2DIV: %d PLL2MUL: %d\n",
		sensor->pll.pll2_div, sensor->pll.pll2_mul);
	dev_dbg(sd->dev, "VTDIV: %d VTDIVPIX: %d\n",
		sensor->pll.vt_sys_div, sensor->pll.vt_pix_div);
	dev_dbg(sd->dev, "OPDIV: %d OPDIVPIX: %d\n",
		sensor->pll.op_sys_div, sensor->pll.op_pix_div);

	return 0;
}

static int ar052x_config_pll(struct ar052x *sensor)
{
	int ret;
	u16 val;

	ret = ar052x_write(sensor, AR052X_VT_PIX_CLK_DIV,
			   sensor->pll.vt_pix_div);
	if (ret)
		return ret;

	ret = ar052x_write(sensor, AR052X_VT_SYS_CLK_DIV,
			   sensor->pll.vt_sys_div);
	if (ret)
		return ret;

	val = sensor->pll.pll2_div << AR052X_FLD_PLL_DIV2_SHIFT |
	      sensor->pll.pll_div << AR052X_FLD_PLL_DIV1_SHIFT;
	ret = ar052x_write(sensor, AR052X_PRE_PLL_CLK_DIV, val);
	if (ret)
		return ret;

	val = sensor->pll.pll2_mul << AR052X_FLD_PLL_MUL2_SHIFT |
	      sensor->pll.pll_mul << AR052X_FLD_PLL_MUL1_SHIFT;
	ret = ar052x_write(sensor, AR052X_PLL_MUL, val);
	if (ret)
		return ret;

	ret = ar052x_write(sensor, AR052X_OP_PIX_CLK_DIV,
			   sensor->pll.op_pix_div);
	if (ret)
		return ret;

	ret = ar052x_write(sensor, AR052X_OP_SYS_CLK_DIV,
			   sensor->pll.op_sys_div);
	if (ret)
		return ret;

	usleep_range(1000, 1500);

	return 0;
}

static int ar052x_config_frame(struct ar052x *sensor)
{
	unsigned int hlength;
	unsigned int vlength;
	unsigned int height = sensor->fmt.height * sensor->h_scale;
	unsigned int width = sensor->fmt.width * sensor->w_scale;
	int ret;
	u16 x_end, y_end;

	ret = ar052x_write(sensor, AR052X_Y_ADDR_START, sensor->crop.top);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_X_ADDR_START, sensor->crop.left);
	if (ret)
		goto out;

	y_end = sensor->crop.top + height - 1;
	ret = ar052x_write(sensor, AR052X_Y_ADRR_END, y_end);
	if (ret)
		goto out;

	x_end = sensor->crop.left + width - 1;
	ret = ar052x_write(sensor, AR052X_X_ADRR_END, x_end);
	if (ret)
		goto out;

	hlength = sensor->fmt.width;
	hlength += sensor->hblank;
	hlength = clamp_t(unsigned int, hlength,
			  sensor->limits.hlen.min, sensor->limits.hlen.max);

	vlength = sensor->fmt.height;
	vlength += sensor->vblank;
	vlength = clamp_t(unsigned int, vlength,
			  sensor->limits.vlen.min, sensor->limits.vlen.max);

	ret = ar052x_write(sensor, AR052X_FRAME_LENGTH_LINES, vlength);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_LINE_LENGTH_PCK, hlength);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_X_OUTPUT_SIZE, sensor->fmt.width);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_Y_OUTPUT_SIZE, sensor->fmt.height);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_X_ODD_INC,
			   (sensor->w_scale << 1) - 1);
	if (ret)
		goto out;

	ret = ar052x_write(sensor, AR052X_Y_ODD_INC,
			   (sensor->h_scale << 1) - 1);

out:
	return ret;
}

static int ar052x_config_mipi(struct ar052x *sensor)
{
	int ret = 0;
	u16 val;

	switch (sensor->bpp) {
	case 8:
		val = AR052X_FLD_CSI2_DT_RAW8;
		break;
	case 10:
		val = AR052X_FLD_CSI2_DT_RAW10;
		break;
	case 12:
		val = AR052X_FLD_CSI2_DT_RAW12;
		break;
	}

	ret = ar052x_write(sensor, AR052X_MIPI_CNTRL, val);
	if (ret)
		return ret;

	val = (sensor->info.t_hs_prep << AR052X_FLD_HS_PREP_SHIFT) |
	      (sensor->info.t_hs_zero << AR052X_FLD_HS_ZERO_SHIFT) |
	      (sensor->info.t_hs_trail << AR052X_FLD_HS_TRAIL_SHIFT);

	ret = ar052x_write(sensor, AR052X_MIPI_TIMING_0, val);
	if (ret)
		return ret;

	val = (sensor->info.t_clk_prep << AR052X_FLD_CLK_PREP_SHIFT) |
	      (sensor->info.t_clk_zero << AR052X_FLD_CLK_ZERO_SHIFT) |
	      (sensor->info.t_clk_trail << AR052X_FLD_CLK_TRAIL_SHIFT);

	ret = ar052x_write(sensor, AR052X_MIPI_TIMING_1, val);
	if (ret)
		return ret;

	val = (sensor->info.t_bgap << AR052X_FLD_BGAP_SHIFT) |
	      (sensor->info.t_clk_pre << AR052X_FLD_CLK_PRE_SHIFT) |
	      (sensor->info.t_clk_post_msbs << AR052X_FLD_CLK_POST_MSBS_SHIFT);

	ret = ar052x_write(sensor, AR052X_MIPI_TIMING_2, val);
	if (ret)
		return ret;

	val = (sensor->info.t_lpx << AR052X_FLD_LPX_SHIFT) |
	      (sensor->info.t_wakeup << AR052X_FLD_WAKE_UP_SHIFT) |
	      (sensor->info.t_clk_post << AR052X_FLD_CLK_POST_SHIFT);

	ret = ar052x_write(sensor, AR052X_MIPI_TIMING_3, val);
	if (ret)
		return ret;

	val = (sensor->info.cont_tx_clk ? (u16) AR052X_FLD_CONT_TX_CLK : 0) |
	      (sensor->info.vreg_mode ? (u16) AR052X_FLD_VREG_MODE : 0) |
	      (sensor->info.t_hs_exit << AR052X_FLD_HS_EXIT_SHIFT) |
	      (sensor->info.t_init << AR052X_FLD_INIT_SHIFT);

	ret = ar052x_write(sensor, AR052X_MIPI_TIMING_4, val);
	if (ret)
		return ret;

	ret = ar052x_write(sensor, AR052X_DATA_FORMAT_BITS,
			   sensor->bpp << AR052X_FLD_DATA_FMT_IN_SHIFT |
			   sensor->bpp << AR052X_FLD_DATA_FMT_OUT_SHIFT);
	if (ret)
		return ret;

	val = AR052X_FLD_TYPE_MIPI << AR052X_FLD_TYPE_SHIFT;
	val |= sensor->info.num_lanes;

	ret = ar052x_write(sensor, AR052X_SERIAL_FORMAT, val);
	if (ret)
		return ret;

	ret = ar052x_update_bits(sensor, AR052X_RESET_REGISTER,
				 AR052X_FLD_STREAM | AR052X_FLD_MASK_BAD,
				 AR052X_FLD_STREAM | AR052X_FLD_MASK_BAD);
	if (ret)
		return ret;

	ret = ar052x_update_bits(sensor, AR052X_RESET_REGISTER,
				 AR052X_FLD_SMIA_SER_DIS, 0);

	ret = ar052x_update_bits(sensor, 0x3f20, BIT(3), 0);
	if (ret)
		return ret;

	return ret;
}

static int ar052x_stream_on(struct ar052x *sensor)
{
	int ret;

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

	/* TODO: Implement Trigger Functionality */
	ret = ar052x_calculate_pll(sensor);
	if (ret)
		return ret;

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

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

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

	sensor->is_streaming = true;
	return 0;
}

static int ar052x_stream_off(struct ar052x *sensor)
{
	int ret = 0;

	ret = ar052x_enter_standby(sensor);

	sensor->is_streaming = false;
	return ret;
}

/* V4L2 subdev video ops */
static int ar052x_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct ar052x *sensor = to_ar052x(sd);
	int ret = 0;

	dev_dbg(sd->dev, "%s enable: %d\n", __func__, enable);

	mutex_lock(&sensor->lock);

	if (enable && sensor->is_streaming) {
		ret = -EBUSY;
		goto out;
	}

	if (!enable && !sensor->is_streaming)
		goto out;

	if (enable) {
		ret = ar052x_stream_on(sensor);
	} else {
		ret = ar052x_stream_off(sensor);
	}

out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static int ar052x_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *interval)
{
	struct ar052x *sensor = to_ar052x(sd);

	mutex_lock(&sensor->lock);

	/* TODO: Calculate current frame interval here or in set_selection to
	 * always have the correct frame interval.
	 */
	interval->interval = sensor->interval;

	mutex_unlock(&sensor->lock);
	return 0;
}

static struct v4l2_rect *ar052x_get_pad_crop(struct ar052x *sensor,
					     struct v4l2_subdev_pad_config *cfg,
					     unsigned int pad, u32 which)
{
	switch (which) {
	case V4L2_SUBDEV_FORMAT_TRY:
		return v4l2_subdev_get_try_crop(&sensor->subdev, cfg, pad);
	case V4L2_SUBDEV_FORMAT_ACTIVE:
		return &sensor->crop;
	default:
		return NULL;
	}
}

static struct v4l2_mbus_framefmt *ar052x_get_pad_fmt(struct ar052x *sensor,
					    struct v4l2_subdev_pad_config *cfg,
					    unsigned int pad, u32 which)
{
	switch (which) {
	case V4L2_SUBDEV_FORMAT_TRY:
		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad);
	case V4L2_SUBDEV_FORMAT_ACTIVE:
		return &sensor->fmt;
	default:
		return NULL;
	}
}

static unsigned int ar052x_find_skipfactor(unsigned int reduced,
					   unsigned int orig,
					   int limit)
{
	unsigned int factor;
	unsigned int lower, upper, half;
	int i;

	/* TODO: Explain in a comment what is going on here */
	factor = DIV_ROUND_CLOSEST(orig, reduced);

	for (i = 0; i <= limit; i++) {
		if ((1 << i) > factor)
			break;
	}

	lower = (1 << (i - 1));
	upper = (1 << i);
	half = lower + ((upper - lower) / 2);

	if (factor <= half)
		return lower;
	else
		return upper;

}

static unsigned int ar052x_find_w_skipfactor(unsigned int reduced_res,
					     unsigned int orig_res)
{
	return ar052x_find_skipfactor(reduced_res, orig_res, 4);
}

static unsigned int ar052x_find_h_skipfactor(unsigned int reduced_res,
					     unsigned int orig_res)
{
	return ar052x_find_skipfactor(reduced_res, orig_res, 4);
}

/* V4L2 subdev pad ops */
static int ar052x_enum_mbus_code(struct v4l2_subdev *sd,
				 struct v4l2_subdev_pad_config *cfg,
				 struct v4l2_subdev_mbus_code_enum *code)
{
	struct ar052x *sensor = to_ar052x(sd);

	if (code->index >= 0 && code->index < sensor->num_fmts) {
		code->code = sensor->formats[code->index].code;
		return 0;
	} else {
		return -EINVAL;
	}
}

static int ar052x_enum_frame_size(struct v4l2_subdev *sd,
				  struct v4l2_subdev_pad_config *cfg,
				  struct v4l2_subdev_frame_size_enum *fse)
{
	struct ar052x *sensor = to_ar052x(sd);
	struct v4l2_mbus_framefmt *fmt;
	struct v4l2_rect *crop;
	int ret = 0;

	mutex_lock(&sensor->lock);

	fmt = ar052x_get_pad_fmt(sensor, cfg, fse->pad, fse->which);
	crop = ar052x_get_pad_crop(sensor, cfg, fse->pad, fse->which);

	if (fse->index >= 4 || fse->code != fmt->code) {
		ret = -EINVAL;
		goto out;
	}

	fse->min_width = crop->width / (1u << fse->index);
	fse->max_width = fse->min_width;
	fse->min_height = crop->height / (1u << fse->index);
	fse->max_height = fse->min_height;

	if (fse->min_width > 1 && fse->min_height > 1)
		ret = 0;
	else
		ret = -EINVAL;

out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static int ar052x_set_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *format)
{
	struct ar052x *sensor = to_ar052x(sd);
	const struct ar052x_format *sensor_format;
	struct v4l2_mbus_framefmt *fmt;
	struct v4l2_rect *crop;
	unsigned int width, height;
	unsigned int w_scale, h_scale;
	int ret = 0;

	dev_dbg(sd->dev, "%s\n", __func__);

	if (sensor->is_streaming && format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
		return -EBUSY;

	mutex_lock(&sensor->lock);

	fmt = ar052x_get_pad_fmt(sensor, cfg, format->pad, format->which);
	crop = ar052x_get_pad_crop(sensor, cfg, format->pad, format->which);

	fmt->field = V4L2_FIELD_NONE;
	fmt->colorspace = V4L2_COLORSPACE_RAW;
	fmt->xfer_func = V4L2_XFER_FUNC_NONE;

	sensor_format = ar052x_find_format(sensor, format->format.code);
	fmt->code = sensor_format->code;

	if (sensor_format->bpp != 8 &&
	    sensor_format->bpp != 10 &&
	    sensor_format->bpp != 12) {
		v4l2_err(sd,
			 "Something went wrong bpp is neither 8, 10 nor 12\n");
		ret = -EINVAL;
		goto out;
	}

	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
		if (sensor->bpp != sensor_format->bpp) {
			sensor->bpp = sensor_format->bpp;
			sensor->pll.update = true;
		}
	}

	if (sensor->model == AR052X_MODEL_COLOR)
		fmt->code = ar052x_match_col_format(sensor, crop);

	width = clamp_t(unsigned int, format->format.width,
			1, crop->width);
	height = clamp_t(unsigned int, format->format.height,
			 1, crop->height);

	w_scale = ar052x_find_w_skipfactor(width, crop->width);
	h_scale = ar052x_find_h_skipfactor(height, crop->height);

	fmt->width = crop->width / w_scale;
	fmt->height = crop->height / h_scale;

	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
		sensor->w_scale = w_scale;
		sensor->h_scale = h_scale;
	}

	format->format = *fmt;

out:
	mutex_unlock(&sensor->lock);
	return ret;
}

static int ar052x_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *format)
{
	struct ar052x *sensor = to_ar052x(sd);
	struct v4l2_mbus_framefmt *fmt;

	dev_dbg(sd->dev, "%s\n", __func__);

	mutex_lock(&sensor->lock);

	fmt = ar052x_get_pad_fmt(sensor, cfg, format->pad, format->which);
	format->format = *fmt;

	mutex_unlock(&sensor->lock);

	return 0;
}

static int ar052x_set_selection(struct v4l2_subdev *sd,
				struct v4l2_subdev_pad_config *cfg,
				struct v4l2_subdev_selection *sel)
{
	struct ar052x *sensor = to_ar052x(sd);
	struct v4l2_rect *_crop;
	unsigned int max_w, max_h;

	dev_dbg(sd->dev, "%s\n", __func__);

	if (sel->target != V4L2_SEL_TGT_CROP)
		return -EINVAL;

	if (sensor->is_streaming &&
	    (sel->r.width != sensor->crop.width ||
	     sel->r.height != sensor->crop.height))
		return -EBUSY;

	mutex_lock(&sensor->lock);

	_crop = ar052x_get_pad_crop(sensor, cfg, sel->pad, sel->which);

	max_w = sensor->limits.x.max - sensor->limits.x.min + 1;
	max_h = sensor->limits.y.max - sensor->limits.y.min + 1;

	_crop->top = min_t(unsigned int, sel->r.top, max_h);
	_crop->left = min_t(unsigned int, sel->r.left, max_w);
	_crop->width = min_t(unsigned int, sel->r.width, max_w);
	_crop->height = min_t(unsigned int, sel->r.height, max_h);

	if (sensor->is_streaming) {
		/* TODO: Add on the fly cropping change */
		mutex_unlock(&sensor->lock);
		return -EBUSY;
	} else {
		sel->r = *_crop;
	}

	mutex_unlock(&sensor->lock);

	return 0;
}

static int ar052x_get_selection(struct v4l2_subdev *sd,
				struct v4l2_subdev_pad_config *cfg,
				struct v4l2_subdev_selection *sel)
{
	struct ar052x *sensor = to_ar052x(sd);
	struct v4l2_rect *_crop;
	unsigned int x_min = sensor->limits.x.min;
	unsigned int y_min = sensor->limits.y.min;
	unsigned int x_max = sensor->limits.x.max;
	unsigned int y_max = sensor->limits.y.max;

	dev_dbg(sd->dev, "%s\n", __func__);

	switch (sel->target) {
	case V4L2_SEL_TGT_CROP:
		mutex_lock(&sensor->lock);

		_crop = ar052x_get_pad_crop(sensor, cfg, sel->pad, sel->which);
		sel->r = *_crop;

		mutex_unlock(&sensor->lock);
		break;

	case V4L2_SEL_TGT_CROP_BOUNDS:
		sel->r.left = x_min;
		sel->r.top = y_min;
		sel->r.width = (x_max - x_min + 1);
		sel->r.height = (y_max - y_min + 1);
		break;

	default:
		return -EINVAL;
	}

	return 0;
}

static const struct v4l2_subdev_core_ops ar052x_subdev_core_ops = {
	.s_power		= ar052x_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
	.s_register		= ar052x_s_register,
	.g_register		= ar052x_g_register,
#endif
};

static const struct v4l2_subdev_video_ops ar052x_subdev_video_ops = {
	.s_stream		= ar052x_s_stream,
	.g_frame_interval	= ar052x_g_frame_interval,
};

static const struct v4l2_subdev_pad_ops ar052x_subdev_pad_ops = {
	.enum_mbus_code		= ar052x_enum_mbus_code,
	.enum_frame_size	= ar052x_enum_frame_size,
	.set_fmt		= ar052x_set_fmt,
	.get_fmt		= ar052x_get_fmt,
	.set_selection		= ar052x_set_selection,
	.get_selection		= ar052x_get_selection,
};

static const struct v4l2_subdev_ops ar052x_subdev_ops = {
	.core			= &ar052x_subdev_core_ops,
	.video			= &ar052x_subdev_video_ops,
	.pad			= &ar052x_subdev_pad_ops,
};

static void ar052x_set_defaults(struct ar052x *sensor)
{
	sensor->limits = (struct ar052x_sensor_limits) {
					/* min		max	 */
		.x 			= {0        ,	2603      },
		.y 			= {0        ,	1955      },
		.hlen 			= {3080     ,	65532     },
		.vlen 			= {48       ,	65535     },
		.hblank 		= {240      ,	65535     },
		.vblank 		= {28       ,	65535     },
		.pll_div 		= {1        ,	63        },
		.pll_mul 		= {32       ,	254       },
		.pll_vt_sys_clk_div 	= {1        ,	16        },
		.pll_vt_pix_clk_div 	= {4        ,	16        },
		.pll_op_sys_clk_div 	= {1        ,	2         },
		.pll_op_pix_clk_div 	= {6        ,	10        },
		.ext_clk 		= {5000000  ,	64000000  },
		.pix_clk		= {84000000 ,	207000000 },
		.op_clk			= {35000000 ,	120000000 },
		.vco 			= {320000000,	1280000000},
	};

	sensor->crop.left = 4;
	sensor->crop.top = 4;
	sensor->crop.width = AR052X_DEF_WIDTH;
	sensor->crop.height = AR052X_DEF_HEIGHT;

	sensor->fmt.width = AR052X_DEF_WIDTH;
	sensor->fmt.height = AR052X_DEF_HEIGHT;
	sensor->fmt.field = V4L2_FIELD_NONE;
	sensor->fmt.colorspace = V4L2_COLORSPACE_RAW;

	if (sensor->model == AR052X_MODEL_MONOCHROME) {
		sensor->formats = ar052x_mono_formats;
		sensor->num_fmts = ARRAY_SIZE(ar052x_mono_formats);
	} else {
		sensor->formats = ar052x_col_formats;
		sensor->num_fmts = ARRAY_SIZE(ar052x_col_formats);
	}

	sensor->fmt.code = sensor->formats[sensor->num_fmts - 1].code;
	sensor->bpp = sensor->formats[sensor->num_fmts - 1].bpp;
	sensor->pll.update = true;

	sensor->w_scale = 1;
	sensor->h_scale = 1;
	sensor->hblank = sensor->limits.hblank.min;
	sensor->vblank = sensor->limits.vblank.min;
	sensor->interval.numerator = 1;
	sensor->interval.denominator = 60;

	/* TODO: I don't think we need this
	 * Or maybe we do if we want to keep manual PLL settings
	 */
	sensor->pll.pll_div = 3;
	sensor->pll.pll2_div = 3;
	sensor->pll.pll_mul = 89;
	sensor->pll.pll2_mul = 92;
	sensor->pll.vt_pix_div = 4;
	sensor->pll.vt_sys_div = 1;
	sensor->pll.op_pix_div = 8;
	sensor->pll.op_sys_div = 1;
	sensor->pll.manual_pll = false;

}

static int ar052x_set_analogue_gain(struct ar052x *sensor, unsigned int val)
{
	unsigned int coarse, fine;

	for (coarse = 1; coarse < 8; coarse++)
		if (val < (1u << coarse) * 1000)
			break;

	val = val / (1u << (coarse - 1));
	fine = ((val * 16) / 1000) - 16;

	if (fine > 15)
		fine = 15;

	ar052x_update_bits(sensor, AR052X_GREENR_GAIN,
			   AR052X_FLD_ANA_COARSE_GAIN_MASK |
			   AR052X_FLD_ANA_FINE_GAIN_MASK,
			   (coarse << AR052X_FLD_ANA_COARSE_GAIN_SHIFT) |
			   (fine << AR052X_FLD_ANA_FINE_GAIN_SHIFT));

	return 1000 * (1u << (coarse - 1)) * (16 + fine) / 16;
}

unsigned int ar052x_get_min_color_gain(struct ar052x *sensor)
{
	unsigned int gains[4];
	int min_idx = 0;
	int i;

	gains[0] = sensor->gains.red_ctrl->cur.val;
	gains[1] = sensor->gains.greenr_ctrl->cur.val;
	gains[2] = sensor->gains.greenb_ctrl->cur.val;
	gains[3] = sensor->gains.blue_ctrl->cur.val;

	for (i = 0; i < 4; i++) {
		if (gains[i] < gains[min_idx])
			min_idx = i;
	}

	return gains[min_idx];
}

static int ar052x_set_digital_gain(struct ar052x *sensor,
				   struct v4l2_ctrl *ctrl)
{
	unsigned int gain;
	unsigned int gain_min;
	unsigned int gain_factor;
	int ret = 0;
	u16 val, mask;

	val = ((ctrl->val * 64) / 1000) << AR052X_FLD_DIGITAL_GAIN_SHIFT;
	mask = AR052X_FLD_DIGITAL_GAIN_MASK;

	switch (ctrl->id) {
	case V4L2_CID_DIGITAL_GAIN:
		if (sensor->model == AR052X_MODEL_MONOCHROME) {
			ret = ar052x_update_bits(sensor, AR052X_GLOBAL_GAIN,
						 mask, val);
			return ret;
		}

		gain_min = ar052x_get_min_color_gain(sensor);
		gain_factor = (ctrl->val * 1000) / gain_min;

		gain = sensor->gains.red_ctrl->cur.val;
		gain += sensor->gains.red_clip;
		gain = (gain * gain_factor) / 1000;

		if (gain > 7999)
			sensor->gains.red_clip = gain - 7999;
		else
			sensor->gains.red_clip = 0;

		gain = clamp_t(unsigned int, gain, 1000, 7999);
		val = (gain * 64) / 1000 << AR052X_FLD_DIGITAL_GAIN_SHIFT;
		ret = ar052x_update_bits(sensor, AR052X_RED_GAIN, mask, val);
		if (ret)
			return ret;

		sensor->gains.red_ctrl->val = gain;
		sensor->gains.red_ctrl->cur.val = gain;

		gain = sensor->gains.greenr_ctrl->cur.val;
		gain += sensor->gains.greenr_clip;
		gain = (gain * gain_factor) / 1000;

		if (gain > 7999)
			sensor->gains.greenr_clip = gain - 7999;
		else
			sensor->gains.greenr_clip = 0;

		gain = clamp_t(unsigned int, gain, 1000, 7999);
		val = (gain * 64) / 1000 << AR052X_FLD_DIGITAL_GAIN_SHIFT;
		ret = ar052x_update_bits(sensor, AR052X_GREENR_GAIN, mask, val);
		if (ret)
			return ret;

		sensor->gains.greenr_ctrl->val = gain;
		sensor->gains.greenr_ctrl->cur.val = gain;

		gain = sensor->gains.greenb_ctrl->cur.val;
		gain += sensor->gains.greenb_clip;
		gain = (gain * gain_factor) / 1000;

		if (gain > 7999)
			sensor->gains.greenb_clip = gain - 7999;
		else
			sensor->gains.greenb_clip = 0;

		gain = clamp_t(unsigned int, gain, 1000, 7999);
		val = (gain * 64) / 1000 << AR052X_FLD_DIGITAL_GAIN_SHIFT;
		ret = ar052x_update_bits(sensor, AR052X_GREENB_GAIN, mask, val);
		if (ret)
			return ret;

		sensor->gains.greenb_ctrl->val = gain;
		sensor->gains.greenb_ctrl->cur.val = gain;

		gain = sensor->gains.blue_ctrl->cur.val;
		gain += sensor->gains.blue_clip;
		gain = (gain * gain_factor) / 1000;

		if (gain > 7999)
			sensor->gains.blue_clip = gain - 7999;
		else
			sensor->gains.blue_clip = 0;

		gain = clamp_t(unsigned int, gain, 1000, 7999);
		val = (gain * 64) / 1000 << AR052X_FLD_DIGITAL_GAIN_SHIFT;
		ret = ar052x_update_bits(sensor, AR052X_BLUE_GAIN, mask, val);
		if (ret)
			return ret;

		sensor->gains.blue_ctrl->val = gain;
		sensor->gains.blue_ctrl->cur.val = gain;

		break;

	case V4L2_CID_X_DIGITAL_GAIN_RED:
		ret = ar052x_update_bits(sensor, AR052X_RED_GAIN, mask, val);
		if (!ret)
			sensor->gains.red_clip = 0;
		break;

	case V4L2_CID_X_DIGITAL_GAIN_GREENR:
		ret = ar052x_update_bits(sensor, AR052X_GREENR_GAIN, mask, val);
		if (!ret)
			sensor->gains.greenr_clip = 0;
		break;

	case V4L2_CID_X_DIGITAL_GAIN_GREENB:
		ret = ar052x_update_bits(sensor, AR052X_GREENB_GAIN, mask, val);
		if (!ret)
			sensor->gains.greenb_clip = 0;
		break;

	case V4L2_CID_X_DIGITAL_GAIN_BLUE:
		ret = ar052x_update_bits(sensor, AR052X_BLUE_GAIN, mask, val);
		if (!ret)
			sensor->gains.blue_clip = 0;
		break;
	}

	return ret;
}

static int ar052x_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct ar052x *sensor = ctrl->priv;
	int ret = 0;
	u16 val;
	u16 mask;

	switch (ctrl->id) {
	case V4L2_CID_VBLANK:
		if (sensor->is_streaming) {
			ret = -EBUSY;
			goto out;
		}
		sensor->vblank = ctrl->val;
		break;

	case V4L2_CID_HBLANK:
		if (sensor->is_streaming) {
			ret = -EBUSY;
			goto out;
		}
		sensor->hblank = ctrl->val;
		break;

	case V4L2_CID_HFLIP:
		ret = ar052x_update_bits(sensor, AR052X_READ_MODE,
					 AR052X_FLD_HORIZ_MIRR,
					 ctrl->val ? AR052X_FLD_HORIZ_MIRR : 0);
		break;

	case V4L2_CID_VFLIP:
		ret = ar052x_update_bits(sensor, AR052X_READ_MODE,
					 AR052X_FLD_VERT_FLIP,
					 ctrl->val ? AR052X_FLD_VERT_FLIP : 0);
		break;

	case V4L2_CID_EXPOSURE:
		ret = ar052x_write(sensor, AR052X_COARSE_INT_TIME, ctrl->val);
		break;

	case V4L2_CID_TEST_PATTERN_RED:
		ret = ar052x_write(sensor, AR052X_TEST_DATA_RED, ctrl->val);
		break;

	case V4L2_CID_TEST_PATTERN_GREENR:
		ret = ar052x_write(sensor, AR052X_TEST_DATA_GREENR, ctrl->val);
		break;

	case V4L2_CID_TEST_PATTERN_BLUE:
		ret = ar052x_write(sensor, AR052X_TEST_DATA_BLUE, ctrl->val);
		break;

	case V4L2_CID_TEST_PATTERN_GREENB:
		ret = ar052x_write(sensor, AR052X_TEST_DATA_GREENB, ctrl->val);
		break;

	case V4L2_CID_TEST_PATTERN:
		/* TODO: This needs fixing */
		switch (ctrl->val) {
		case 5:
			val = AR052X_FLD_WALKING_ONES_10BIT;
			break;
		case 6:
			val = AR052X_FLD_WALKING_ONES_8BIT;
			break;
		default:
			val = ctrl->val;
			break;
		}

		ret = ar052x_write(sensor, AR052X_TEST_PATTERN, val);
		break;

	case V4L2_CID_X_BINNING_COL:
		ret = ar052x_update_bits(sensor, AR052X_READ_MODE,
					 AR052X_FLD_X_BIN_EN,
					 ctrl->val ? AR052X_FLD_X_BIN_EN : 0);
		break;

	case V4L2_CID_X_EXTRA_BLANKING:
		ret = ar052x_write(sensor, AR052X_EXTRA_DELAY, ctrl->val);
		break;

	case V4L2_CID_DIGITAL_GAIN:
	case V4L2_CID_X_DIGITAL_GAIN_RED:
	case V4L2_CID_X_DIGITAL_GAIN_GREENR:
	case V4L2_CID_X_DIGITAL_GAIN_BLUE:
	case V4L2_CID_X_DIGITAL_GAIN_GREENB:
		ret = ar052x_set_digital_gain(sensor, ctrl);
		break;

	case V4L2_CID_ANALOGUE_GAIN:
		ctrl->val = ar052x_set_analogue_gain(sensor, ctrl->val);
		break;

	case V4L2_CID_X_DYNAMIC_PIXEL_CORRECTION:
		mask = AR052X_FLD_PIX_DEF_2D_COUPLE_EN |
		       AR052X_FLD_PIX_DEF_2D_SINGLE_EN |
		       AR052X_FLD_PIX_DEF_2D_FUSE_EN |
		       AR052X_FLD_PIX_DEF_ID_LOC_CORR_EN |
		       AR052X_FLD_PIX_DEF_ID_EN;

		val = ctrl->val ? AR052X_FLD_PIX_DEF_2D_COUPLE_EN |
				  AR052X_FLD_PIX_DEF_2D_SINGLE_EN |
				  AR052X_FLD_PIX_DEF_2D_FUSE_EN : 0;

		val |= AR052X_FLD_PIX_DEF_ID_LOC_CORR_EN |
		       AR052X_FLD_PIX_DEF_ID_EN;

		ret = ar052x_update_bits(sensor, AR052X_PIX_DEF_ID, mask, val);
		break;

	case V4L2_CID_LINK_FREQ:
		break;

	case V4L2_CID_FLASH_LED_MODE:
		switch(ctrl->val) {
		case V4L2_FLASH_LED_MODE_NONE:
			val = 0;
			break;

		case V4L2_FLASH_LED_MODE_FLASH:
			val = AR052X_FLD_XENON_FLASH;
			break;

		case V4L2_FLASH_LED_MODE_TORCH:
			val = AR052X_FLD_LED_FLASH;
			break;
		}

		mask = AR052X_FLD_XENON_FLASH | AR052X_FLD_LED_FLASH;

		ret = ar052x_update_bits(sensor, AR052X_FLASH, mask, val);
		break;

	case V4L2_CID_X_FLASH_INVERT:
		val = ctrl->val ? AR052X_FLD_INVERT_FLASH : 0;
		ret = ar052x_update_bits(sensor, AR052X_FLASH,
					 AR052X_FLD_INVERT_FLASH, val);
		break;

	case V4L2_CID_X_FLASH_XENON_WIDTH:
		ret = ar052x_write(sensor, AR052X_FLASH_COUNT, ctrl->val);
		break;

	default:
		ret = -ENOTTY;
		break;
	}

out:
	return ret;
}

static int ar052x_g_ctrl(struct v4l2_ctrl *ctrl)
{
	struct ar052x *sensor = ctrl->priv;
	int ret = 0;

	switch (ctrl->id) {
	case V4L2_CID_VBLANK:
		ctrl->val = sensor->vblank;
		break;

	case V4L2_CID_HBLANK:
		ctrl->val = sensor->hblank;
		break;

	default:
		ret = -ENOTTY;
		break;
	}

	return ret;
}

static const struct v4l2_ctrl_ops ar052x_ctrl_ops = {
	.s_ctrl			= ar052x_s_ctrl,
	.g_volatile_ctrl	= ar052x_g_ctrl,
};

static const char * const ar052x_test_pattern_menu[] = {
	"disabled",
	"solid color",
	"color bar",
	"fade to gray",
	"pn9 link integrity",
	"walking 1 (10 bit)",
	"walking 1 (8 bit)",
};

static const char * const ar052x_binning_menu[] = {
	"disable",
	"enable",
};

static const s64 ar052x_link_freq[] = {
	414000000,
	517500000,
	549000000,
};

/* TODO: Check controls */
static const struct v4l2_ctrl_config ar052x_ctrls[] = {
	{
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_VBLANK,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.flags		= V4L2_CTRL_FLAG_VOLATILE,
		.min		= 22,
		.max		= 65535,
		.step		= 1,
		.def		= 22,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_HBLANK,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.flags		= V4L2_CTRL_FLAG_VOLATILE,
		.min		= 208,
		.max		= 65535,
		.step		= 1,
		.def		= 208,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_HFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_VFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_EXPOSURE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 0,
		.max		= 65535,
		.step		= 1,
		.def		= 1817,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_TEST_PATTERN_RED,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 0,
		.max		= 4095,
		.step		= 1,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_TEST_PATTERN_GREENR,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 0,
		.max		= 4095,
		.step		= 1,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_TEST_PATTERN_GREENB,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 0,
		.max		= 4095,
		.step		= 1,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_TEST_PATTERN_BLUE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 0,
		.max		= 4095,
		.step		= 1,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_TEST_PATTERN,
		.type		= V4L2_CTRL_TYPE_MENU,
		.min		= 0,
		.max		= ARRAY_SIZE(ar052x_test_pattern_menu) - 1,
		.qmenu		= ar052x_test_pattern_menu,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_BINNING_COL,
		.type		= V4L2_CTRL_TYPE_MENU,
		.name		= "Column Binning",
		.min		= 0,
		.max		= ARRAY_SIZE(ar052x_binning_menu) - 1,
		.qmenu		= ar052x_binning_menu,
		.def		= 0,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_EXTRA_BLANKING,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Extra Vertical Blanking",
		.min		= 0,
		.step		= 1,
		.max		= 65535,
		.def		= 0,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_ANALOGUE_GAIN,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.min		= 1000,
		.step		= 1,
		.max		= 14250,
		.def		= 2000,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_DIGITAL_GAIN,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.flags		= (V4L2_CTRL_FLAG_EXECUTE_ON_WRITE |
				   V4L2_CTRL_FLAG_UPDATE),
		.min		= 1000,
		.step		= 1,
		.max		= 7999,
		.def		= 1000,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_DIGITAL_GAIN_RED,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Digital Gain Red",
		.min		= 1000,
		.step		= 1,
		.max		= 7999,
		.def		= 1300,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_DIGITAL_GAIN_GREENR,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Digital Gain Green (Red)",
		.min		= 1000,
		.step		= 1,
		.max		= 7999,
		.def		= 1000,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_DIGITAL_GAIN_BLUE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Digital Gain Blue",
		.min		= 1000,
		.step		= 1,
		.max		= 7999,
		.def		= 1500,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_DIGITAL_GAIN_GREENB,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Digital Gain Green (Blue)",
		.min		= 1000,
		.step		= 1,
		.max		= 7999,
		.def		= 1000,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_LINK_FREQ,
		.type		= V4L2_CTRL_TYPE_INTEGER_MENU,
		.flags		= V4L2_CTRL_FLAG_READ_ONLY,
		.min		= 0,
		.max		= ARRAY_SIZE(ar052x_link_freq) - 1,
		.def		= 0,
		.qmenu_int	= ar052x_link_freq,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_DYNAMIC_PIXEL_CORRECTION,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Dynamic Defect Pixel Correction",
		.min		= 0,
		.max		= 1,
		.step		= 1,
		.def		= 0,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_FLASH_LED_MODE,
		.type		= V4L2_CTRL_TYPE_MENU,
		.min		= 0,
		.max		= V4L2_FLASH_LED_MODE_TORCH,
		.def		= V4L2_FLASH_LED_MODE_NONE,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_FLASH_INVERT,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Invert Flash",
		.min		= 0,
		.max		= 1,
		.step		= 1,
		.def		= 0,
	}, {
		.ops		= &ar052x_ctrl_ops,
		.id		= V4L2_CID_X_FLASH_XENON_WIDTH,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Flash Xenon Width",
		.min		= 0,
		.step		= 1,
		.max		= 65535,
		.def		= 256,
	},
};

static int ar052x_create_ctrls(struct ar052x *sensor)
{
	struct v4l2_ctrl_config ctrl_cfg;
	struct v4l2_ctrl *ctrl;
	int i;
	int ret;

	for (i = 0; i < ARRAY_SIZE(ar052x_ctrls); i++) {
		ctrl_cfg = ar052x_ctrls[i];

		switch (ctrl_cfg.id) {
		case V4L2_CID_X_DIGITAL_GAIN_RED:
		case V4L2_CID_X_DIGITAL_GAIN_GREENR:
		case V4L2_CID_X_DIGITAL_GAIN_BLUE:
		case V4L2_CID_X_DIGITAL_GAIN_GREENB:
			if (sensor->model == AR052X_MODEL_MONOCHROME)
				continue;

			break;

		case V4L2_CID_HBLANK:
			ctrl_cfg.min = sensor->limits.hblank.min;
			ctrl_cfg.def = ctrl_cfg.min;
			break;

		case V4L2_CID_VBLANK:
			ctrl_cfg.min = sensor->limits.vblank.min;
			ctrl_cfg.def = ctrl_cfg.min;
			break;

		default:
			break;
		}

		ctrl = v4l2_ctrl_new_custom(&sensor->ctrls,
					    &ctrl_cfg, sensor);

		ret = sensor->ctrls.error;
		if (ret) {
			v4l2_warn(&sensor->subdev,
				  "failed to register control '%s' (0x%x): %d\n",
				  ctrl_cfg.name ? ctrl_cfg.name :
				  v4l2_ctrl_get_name(ctrl_cfg.id),
				  ctrl_cfg.id, ret);
			return ret;
		}

		switch (ctrl->id) {
		case V4L2_CID_LINK_FREQ:
			sensor->link_freq_ctrl = ctrl;
			break;

		case V4L2_CID_X_DIGITAL_GAIN_RED:
			if (sensor->model == AR052X_MODEL_COLOR)
				sensor->gains.red_ctrl = ctrl;
			break;

		case V4L2_CID_X_DIGITAL_GAIN_GREENB:
			if (sensor->model == AR052X_MODEL_COLOR)
				sensor->gains.greenb_ctrl = ctrl;
			break;

		case V4L2_CID_X_DIGITAL_GAIN_GREENR:
			if (sensor->model == AR052X_MODEL_COLOR)
				sensor->gains.greenr_ctrl = ctrl;
			break;

		case V4L2_CID_X_DIGITAL_GAIN_BLUE:
			if (sensor->model == AR052X_MODEL_COLOR)
				sensor->gains.blue_ctrl = ctrl;
			break;

		default:
			break;
		}
	}

	return 0;
}

static int ar052x_subdev_registered(struct v4l2_subdev *sd)
{
	struct ar052x *sensor = to_ar052x(sd);
	int ret;
	u16 model_id, customer_rev;

	/* TODO: Enable/Disable Power before accessing i2c */
	ret = ar052x_read(sensor, AR052X_MODEL_ID, &model_id);
	if (ret)
		return ret;

	if (model_id != AR052X_CHIP_ID) {
		dev_err(sd->dev, "Wrong chip version: 0x%04x <-> 0x%04x\n",
			model_id, AR052X_CHIP_ID);
		return -ENOENT;
	}

	ret = ar052x_read(sensor, AR052X_CUSTOMER_REV, &customer_rev);
	if (ret)
		return ret;

	dev_info(sd->dev, "Device ID: 0x%04x customer rev: 0x%04x\n",
		 model_id, customer_rev);

	if (sensor->model == AR052X_MODEL_UNKNOWN) {
		if (customer_rev & BIT(4))
			sensor->model = AR052X_MODEL_COLOR;
		else
			sensor->model = AR052X_MODEL_MONOCHROME;
	}

	ar052x_reset(sensor);

	ar052x_set_defaults(sensor);

	ar052x_debugfs_init(sensor);

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

	v4l2_ctrl_handler_setup(&sensor->ctrls);

	return 0;
}

static const struct v4l2_subdev_internal_ops ar052x_subdev_internal_ops = {
	.registered		= ar052x_subdev_registered,
};

static int ar052x_parse_endpoint(struct device *dev, struct ar052x *sensor,
				 struct device_node *ep)
{
	struct v4l2_fwnode_endpoint endpoint = {
		.bus_type = V4L2_MBUS_CSI2_DPHY
	};
	struct fwnode_handle *fwnode;
	int ret;
	unsigned int tmp;

	fwnode = of_fwnode_handle(ep);
	ret = v4l2_fwnode_endpoint_alloc_parse(fwnode, &endpoint);
	if (ret < 0) {
		dev_err(dev, "Failed to parse MIPI endpoint (%d)\n", ret);
		return ret;
	}

	sensor->info.num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
	if (sensor->info.num_lanes < 2 || sensor->info.num_lanes > 4) {
		dev_err(dev, "Wrong number of lanes configured");
		return -EINVAL;
	}

	tmp = 2;
	of_property_read_u32(ep, "onsemi,t-hs-prep", &tmp);
	sensor->info.t_hs_prep = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 17;
	of_property_read_u32(ep, "onsemi,t-hs-zero", &tmp);
	sensor->info.t_hs_zero = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 9;
	of_property_read_u32(ep, "onsemi,t-hs-trail", &tmp);
	sensor->info.t_hs_trail = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 2;
	of_property_read_u32(ep, "onsemi,t-clk-prep", &tmp);
	sensor->info.t_clk_prep = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 34;
	of_property_read_u32(ep, "onsemi,t-clk-zero", &tmp);
	sensor->info.t_clk_zero = clamp_t(unsigned int, tmp, 0, 0x3f);

	tmp = 10;
	of_property_read_u32(ep, "onsemi,t-clk-trail", &tmp);
	sensor->info.t_clk_trail = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 10;
	of_property_read_u32(ep, "onsemi,t-bgap", &tmp);
	sensor->info.t_bgap = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 1;
	of_property_read_u32(ep, "onsemi,t-clk-pre", &tmp);
	sensor->info.t_clk_pre = clamp_t(unsigned int, tmp, 0, 0x3f);

	tmp = 3;
	of_property_read_u32(ep, "onsemi,t-clk-post-msbs", &tmp);
	sensor->info.t_clk_post_msbs = clamp_t(unsigned int, tmp, 0, 0xf);

	tmp = 7;
	of_property_read_u32(ep, "onsemi,t-lpx", &tmp);
	sensor->info.t_lpx = clamp_t(unsigned int, tmp, 0, 0x3f);

	tmp = 15;
	of_property_read_u32(ep, "onsemi,t-wakeup", &tmp);
	sensor->info.t_wakeup = clamp_t(unsigned int, tmp, 0, 0x7f);

	tmp = 1;
	of_property_read_u32(ep, "onsemi,t-clk-post", &tmp);
	sensor->info.t_clk_post = clamp_t(unsigned int, tmp, 0, 0x3);

	tmp = 1;
	of_property_read_u32(ep, "onsemi,cont-tx-clk", &tmp);
	sensor->info.cont_tx_clk = tmp ? true : false;

	tmp = 0;
	of_property_read_u32(ep, "onsemi,vreg-mode", &tmp);
	sensor->info.vreg_mode = tmp ? true : false;

	tmp = 13;
	of_property_read_u32(ep, "onsemi,t-hs-exit", &tmp);
	sensor->info.t_hs_exit = clamp_t(unsigned int, tmp, 0, 0x3f);

	tmp = 12;
	of_property_read_u32(ep, "onsemi,t-init", &tmp);
	sensor->info.t_init = clamp_t(unsigned int, tmp, 0, 0x7f);

	return 0;
}

static int ar052x_of_probe(struct device *dev, struct ar052x *sensor)
{
	struct device_node *ep;
	struct clk *clk;
	struct gpio_desc *gpio;
	int ret;

	clk = devm_clk_get(dev, "ext");
	ret = PTR_ERR_OR_ZERO(clk);
	if (ret == -EPROBE_DEFER)
		return ret;
	if (ret < 0) {
		dev_err(dev, "Failed to get external clock (%d)\n", ret);
		return ret;
	}

	sensor->extclk = clk;

	gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
	ret = PTR_ERR_OR_ZERO(clk);
	if (ret < 0) {
		dev_err(dev, "Failed to get reset gpio (%d)\n", ret);
		return ret;
	}

	sensor->reset_gpio = gpio;

	ep = of_graph_get_next_endpoint(dev->of_node, NULL);

	if (!ep) {
		dev_err(dev, "Failed to find endpoint node\n");
		return -ENODEV;
	}

	ret = ar052x_parse_endpoint(dev, sensor, ep);

	of_node_put(ep);

	return ret;
}

static int ar052x_probe(struct i2c_client *i2c,
			const struct i2c_device_id *did)
{
	struct ar052x *sensor;
	struct v4l2_subdev *sd;
	int ret;

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

	dev_info(&i2c->dev, "Probing ar052x Driver\n");

	sd = &sensor->subdev;
	sensor->model = did->driver_data;

	ret = ar052x_of_probe(&i2c->dev, sensor);
	if (ret < 0)
		return ret;

	mutex_init(&sensor->lock);

	v4l2_i2c_subdev_init(sd, i2c, &ar052x_subdev_ops);

	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
	sd->internal_ops = &ar052x_subdev_internal_ops;
	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;

	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
	if (ret < 0)
		goto out_media;

	ret = v4l2_ctrl_handler_init(&sensor->ctrls, 10);
	if (ret < 0)
		goto out;

	sensor->subdev.ctrl_handler = &sensor->ctrls;

	ret = v4l2_async_register_subdev(&sensor->subdev);
	if (ret < 0)
		goto out;

	return 0;

out:
	v4l2_ctrl_handler_free(&sensor->ctrls);
out_media:
	media_entity_cleanup(&sd->entity);
	mutex_destroy(&sensor->lock);
	return ret;
}

static int ar052x_remove(struct i2c_client *i2c)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(i2c);
	struct ar052x *sensor = to_ar052x(sd);

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

	return 0;
}

static const struct i2c_device_id ar052x_id_table[] = {
        { "ar052x", AR052X_MODEL_UNKNOWN },
        { "ar052xc", AR052X_MODEL_COLOR },
        { "ar052xm", AR052X_MODEL_MONOCHROME },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ar052x_id_table);

static const struct of_device_id ar052x_of_match[] = {
        { .compatible = "onsemi,ar052x" },
        { .compatible = "onsemi,ar052xc" },
        { .compatible = "onsemi,ar052xm" },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ar052x_of_match);

static struct i2c_driver ar052x_i2c_driver = {
        .driver         = {
                .name   = "ar052x",
                .of_match_table = of_match_ptr(ar052x_of_match),
        },
        .probe          = ar052x_probe,
        .remove         = ar052x_remove,
        .id_table       = ar052x_id_table,
};
module_i2c_driver(ar052x_i2c_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stefan Riedmueller <s.riedmueller@phytec.de>");
MODULE_VERSION(DRIVER_VERSION);
