diff mbox

[5/6,v3] Updated TVP7002 driver for DM365

Message ID 1251845698-18150-1-git-send-email-santiago.nunez@ridgerun.com (mailing list archive)
State Superseded
Headers show

Commit Message

santiago.nunez@ridgerun.com Sept. 1, 2009, 10:54 p.m. UTC
From: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>

This patch provides the implementation of the TVP7002 decoder
driver for DM365. Implemented revisions by Vaibhav Hiremath
and Hans Verkuil. Removed most of controls, cleared up logic,
cleaned up code.

Signed-off-by: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
---
 drivers/media/video/tvp7002.c | 1493 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1493 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/tvp7002.c

Comments

Hans Verkuil Sept. 2, 2009, 6:53 a.m. UTC | #1
On Wednesday 02 September 2009 00:54:58 santiago.nunez@ridgerun.com wrote:
> From: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
> 
> This patch provides the implementation of the TVP7002 decoder
> driver for DM365. Implemented revisions by Vaibhav Hiremath
> and Hans Verkuil. Removed most of controls, cleared up logic,
> cleaned up code.
> 
> Signed-off-by: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
> ---
>  drivers/media/video/tvp7002.c | 1493 +++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 1493 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/tvp7002.c
> 
> diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
> new file mode 100644
> index 0000000..e8cd77e
> --- /dev/null
> +++ b/drivers/media/video/tvp7002.c
> @@ -0,0 +1,1493 @@
> +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
> + * Digitizer with Horizontal PLL registers
> + *
> + * Copyright (C) 2009 Texas Instruments Inc
> + * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
> + *
> + * This code is partially based upon the TVP5150 driver
> + * written by Mauro Carvalho Chehab (mchehab@infradead.org)
> + * and the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/videodev2.h>
> +#include <linux/delay.h>
> +#include <media/v4l2-device.h>
> +#include <media/tvp7002.h>
> +#include <media/v4l2-i2c-drv.h>
> +#include <media/v4l2-chip-ident.h>
> +#include <media/davinci/videohd.h>
> +#include "tvp7002_reg.h"
> +
> +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
> +MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* I2C retry attempts */
> +#define I2C_RETRY_COUNT                 (5)
> +
> +/* Debugging information */
> +
> +static int debug;
> +module_param(debug, int, 0);
> +MODULE_PARM_DESC(debug, "Debug level (0-2)");
> +
> +/* Reference to tvp7002_platform_data */
> +extern struct tvp7002_platform_data tvp7002_pdata;

No, no, don't do this. This will fail if you have multiple tvp7002 devices in
one system. Just pass the platform data in through the board_info. I also
suggest renaming this struct to tvp7002_config or something like that.
platform_data suggests that this device is platform specific, but it can also
be used in PCI or USB boards.

> +
> +/* Struct for handling resolutions and associate register values */
> +struct tvp7002_resol {
> +	v4l2_std_id id;
> +	int hres;
> +	int vres;
> +	int frate;
> +	int lrate;
> +	int prate;
> +	u8 reg01;
> +	u8 reg02;
> +	u8 reg03;
> +	u8 reg04;
> +};
> +
> +/* Struct for handling register values */
> +struct i2c_reg_value {
> +	u8 reg;
> +	u8 value;
> +};
> +
> +/* Register default values (according to tvp7002 datasheet) */
> +static const struct i2c_reg_value tvp7002_init_default[] = {
> +	/* 0x00: read only */
> +	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67 },
> +	{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x20 },
> +	{ TVP7002_HPLL_CRTL, 0xa8 },
> +	{ TVP7002_HPLL_PHASE_SEL, 0x80 },
> +	{ TVP7002_CLAMP_START, 0x32 },
> +	{ TVP7002_CLAMP_W, 0x20 },
> +	{ TVP7002_HSYNC_OUT_W, 0x20 },
> +	{ TVP7002_B_FINE_GAIN, 0x00 },
> +	{ TVP7002_G_FINE_GAIN, 0x00 },
> +	{ TVP7002_R_FINE_GAIN, 0x00 },
> +	{ TVP7002_B_FINE_OFF_MSBS, 0x80 },
> +	{ TVP7002_G_FINE_OFF_MSBS, 0x80 },
> +	{ TVP7002_R_FINE_OFF_MSBS, 0x80 },
> +	{ TVP7002_SYNC_CTL_1, 0x5b },
> +	{ TVP7002_HPLL_AND_CLAMP_CTL, 0x2e },
> +	{ TVP7002_SYNC_ON_G_THRS, 0x5d },
> +	{ TVP7002_SYNC_SEPARATOR_THRS, 0x20 },
> +	{ TVP7002_HPLL_PRE_COAST, 0x00 },
> +	{ TVP7002_HPLL_POST_COAST, 0x00 },
> +	/* 0x14: read only */
> +	{ TVP7002_OUT_FORMATTER, 0x00 },
> +	{ TVP7002_MISC_CTL_1, 0x11 },
> +	{ TVP7002_MISC_CTL_2, 0x03 },
> +	{ TVP7002_MISC_CTL_3, 0x00 },
> +	{ TVP7002_IN_MUX_SEL_1, 0x00 },
> +	{ TVP7002_IN_MUX_SEL_2, 0xc2 },
> +	{ TVP7002_B_AND_G_COARSE_GAIN, 0x77 },
> +	{ TVP7002_R_COARSE_GAIN, 0x07 },
> +	{ TVP7002_COARSE_CLAMP_CTL, 0x00 },
> +	{ TVP7002_FINE_OFF_LSBS, 0x00 },
> +	{ TVP7002_B_COARSE_OFF, 0x10 },
> +	{ TVP7002_G_COARSE_OFF, 0x10 },
> +	{ TVP7002_R_COARSE_OFF, 0x10 },
> +	{ TVP7002_HSOUT_OUT_START, 0x0d	},
> +	{ TVP7002_MISC_CTL_4, 0x0d },
> +	/* 0x23: read only */
> +	/* 0x24: read only */
> +	/* 0x25: read only */
> +	{ TVP7002_AUTO_LVL_CTL_ENABLE, 0x80 },
> +	/* 0x27: read only */
> +	{ TVP7002_AUTO_LVL_CTL_FILTER, 0x53 },
> +	{ TVP7002_FINE_CLAMP_CTL, 0x07 },
> +	{ TVP7002_PWR_CTL, 0x00 },
> +	{ TVP7002_ADC_SETUP, 0x50 },
> +	{ TVP7002_COARSE_CLAMP_CTL, 0x00 },
> +	{ TVP7002_SOG_CLAMP, 0x80 },
> +	{ TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c },
> +	{ TVP7002_SOG_COARSE_CLAMP_CTL, 0x04 },
> +	{ TVP7002_ALC_PLACEMENT, 0x5a },
> +	{ TVP7002_MVIS_STRIPPER_W, 0x03 },
> +	{ TVP7002_VSYNC_ALGN, 0x10 },
> +	{ TVP7002_SYNC_BYPASS, 0x00 },
> +	/* 0x37: read only */
> +	/* 0x38: read only */
> +	/* 0x39: read only */
> +	/* 0x3a: read only */
> +	/* 0x3b: read only */
> +	/* 0x3c: read only */
> +	{ TVP7002_L_LENGTH_TOL, 0x03 },
> +	{ TVP7002_VIDEO_BWTH_CTL, 0x00 },
> +	{ TVP7002_AVID_START_PIXEL_LSBS, 0x01 },
> +	{ TVP7002_AVID_START_PIXEL_MSBS, 0x2c },
> +	{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x06 },
> +	{ TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c },
> +	{ TVP7002_VBLK_F_0_START_L_OFF, 0x05 },
> +	{ TVP7002_VBLK_F_1_START_L_OFF, 0x05 },
> +	{ TVP7002_VBLK_F_0_DURATION, 0x1e },
> +	{ TVP7002_VBLK_F_1_DURATION, 0x1e },
> +	{ TVP7002_FBIT_F_0_START_L_OFF, 0x00 },
> +	{ TVP7002_FBIT_F_1_START_L_OFF, 0x00 },
> +	{ TVP7002_YUV_Y_G_COEF_LSBS, 0xe3 },
> +	{ TVP7002_YUV_Y_G_COEF_MSBS, 0x16 },
> +	{ TVP7002_YUV_Y_B_COEF_LSBS, 0x4f },
> +	{ TVP7002_YUV_Y_B_COEF_MSBS, 0x02 },
> +	{ TVP7002_YUV_Y_R_COEF_LSBS, 0xce },
> +	{ TVP7002_YUV_Y_R_COEF_MSBS, 0x06 },
> +	{ TVP7002_YUV_U_G_COEF_LSBS, 0xab },
> +	{ TVP7002_YUV_U_G_COEF_MSBS, 0xf3 },
> +	{ TVP7002_YUV_U_B_COEF_LSBS, 0x00 },
> +	{ TVP7002_YUV_U_B_COEF_MSBS, 0x10 },
> +	{ TVP7002_YUV_U_R_COEF_LSBS, 0x55 },
> +	{ TVP7002_YUV_U_R_COEF_MSBS, 0xfc },
> +	{ TVP7002_YUV_V_G_COEF_LSBS, 0x78 },
> +	{ TVP7002_YUV_V_G_COEF_MSBS, 0xf1 },
> +	{ TVP7002_YUV_V_B_COEF_LSBS, 0x88 },
> +	{ TVP7002_YUV_V_B_COEF_MSBS, 0xfe },
> +	{ TVP7002_YUV_V_R_COEF_LSBS, 0x00 },
> +	{ TVP7002_YUV_V_R_COEF_MSBS, 0x10 },
> +	{ 0x5c, 0x00 }	/* end of registers */
> +};
> +
> +/* Available resolutions */
> +static struct tvp7002_resol tvp7002_resolutions[] = {
> +	{
> +		.id = V4L2_STD_525I_60,
> +		.hres = 720,
> +		.vres = 480,
> +		.frate = 30,
> +		.lrate = 15,
> +		.prate = 14,
> +		.reg01 = 0x35,
> +		.reg02 = 0xa0,
> +		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_625I_50,
> +		.hres = 720,
> +		.vres = 576,
> +		.frate = 25,
> +		.lrate = 16,
> +		.prate = 14,
> +		.reg01 = 0x36,
> +		.reg02 = 0x00,
> +		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_525P_60,
> +		.hres = 720,
> +		.vres = 480,
> +		.frate = 60,
> +		.lrate = 31,
> +		.prate = 27,
> +		.reg01 = 0x35,
> +		.reg02 = 0xa0,
> +		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_625P_50,
> +		.hres = 720,
> +		.vres = 576,
> +		.frate = 50,
> +		.lrate = 31,
> +		.prate = 27,
> +		.reg01 = 0x36,
> +		.reg02 = 0x00,
> +		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_720P_60,
> +		.hres = 1280,
> +		.vres = 720,
> +		.frate = 60,
> +		.lrate = 45,
> +		.prate = 74,
> +		.reg01 = 0x67,
> +		.reg02 = 0x20,
> +		.reg03 = TVP7002_VCO_RANGE_MED | 0x20,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_720P_50,
> +		.hres = 1280,
> +		.vres = 720,
> +		.frate = 50,
> +		.lrate = 38,
> +		.prate = 74,
> +		.reg01 = 0x7b,
> +		.reg02 = 0xc0,
> +		.reg03 = TVP7002_VCO_RANGE_MED | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_1080I_60,
> +		.hres = 1920,
> +		.vres = 1080,
> +		.frate = 60,
> +		.lrate = 34,
> +		.prate = 74,
> +		.reg01 = 0x89,
> +		.reg02 = 0x80,
> +		.reg03 = TVP7002_VCO_RANGE_MED | 0x18,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_1080I_50,
> +		.hres = 1920,
> +		.vres = 1080,
> +		.frate = 50,
> +		.lrate = 28,
> +		.prate = 74,
> +		.reg01 = 0xa5,
> +		.reg02 = 0x00,
> +		.reg03 = TVP7002_VCO_RANGE_MED | 0x10,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_1080P_60,
> +		.hres = 1920,
> +		.vres = 1080,
> +		.frate = 60,
> +		.lrate = 68,
> +		.prate = 149,
> +		.reg01 = 0x89,
> +		.reg02 = 0x80,
> +		.reg03 = TVP7002_VCO_RANGE_HIGH | 0x20,
> +		.reg04 = 0x80,
> +	},
> +	{
> +		.id = V4L2_STD_1080P_50,
> +		.hres = 1920,
> +		.vres = 1080,
> +		.frate = 50,
> +		.lrate = 56,
> +		.prate = 149,
> +		.reg01 = 0xa5,
> +		.reg02 = 0x00,
> +		.reg03 = TVP7002_VCO_RANGE_HIGH | 0x18,
> +		.reg04 = 0x80,
> +	},
> +};
> +
> +/*
> + * tvp7002_from_std - Map video standard to register information
> + * @std: v4l2_std_id (u64) integer
> + *
> + * Returns index of std or -1 in case of error.
> + */
> +int tvp7002_from_std(v4l2_std_id std)
> +{
> +	int res;
> +
> +	switch (std) {
> +	case V4L2_STD_525P_60:
> +		res = TVP7002_STD_480P;
> +		break;
> +	case V4L2_STD_525I_60:
> +		res = TVP7002_STD_480I;
> +		break;
> +	case V4L2_STD_625P_50:
> +		res = TVP7002_STD_576P;
> +		break;
> +	case V4L2_STD_625I_50:
> +		res = TVP7002_STD_576I;
> +		break;
> +	case V4L2_STD_720P_50:
> +		res = TVP7002_STD_720P_50;
> +		break;
> +	case V4L2_STD_720P_60:
> +		res = TVP7002_STD_720P_60;
> +		break;
> +	case V4L2_STD_1080I_50:
> +		res = TVP7002_STD_1080I_50;
> +		break;
> +	case V4L2_STD_1080I_60:
> +		res = TVP7002_STD_1080I_60;
> +		break;
> +	case V4L2_STD_1080P_50:
> +		res = TVP7002_STD_1080P_50;
> +		break;
> +	case V4L2_STD_1080P_60:
> +		res = TVP7002_STD_1080P_60;
> +		break;
> +	default:
> +		res = -1;
> +		break;
> +	}
> +
> +	return res;
> +}
> +
> +/* Device definition */
> +struct tvp7002 {
> +	struct v4l2_subdev sd;
> +	v4l2_std_id video_mode;
> +	struct v4l2_pix_format pix;
> +	int streaming;
> +};
> +
> +/* Supported controls */
> +static struct v4l2_queryctrl tvp7002_qctrl[] = {
> +	{
> +		/* This gain control uses fine grain in TVP7002 */
> +		.id = V4L2_CID_GAIN,
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.name = "Gain for RGB channels",
> +		.minimum = 0,
> +		.maximum = 255,
> +		.step = 1,
> +		.default_value = 0,
> +		.flags = 0,
> +	},
> +};
> +
> +/*
> + * to_tvp7002 - Obtain device handler TVP7002
> + * @sd: ptr to v4l2_subdev struct
> + *
> + * Returns device handler tvp7002.
> + */
> +static struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct tvp7002, sd);
> +}
> +
> +/*
> + * tvp7002_read - Read a value from a register in an TVP7002
> + * @sd: ptr to v4l2_subdev struct
> + * @reg: TVP7002 register address
> + *
> + * Returns value read if successful, or non-zero (-1) otherwise.
> + */
> +static int tvp7002_read(struct v4l2_subdev *sd, unsigned char addr)
> +{
> +	struct i2c_client *c = v4l2_get_subdevdata(sd);
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
> +		error = i2c_smbus_read_byte_data(c, addr);
> +
> +		if (error >= 0)
> +			return error;
> +		msleep_interruptible(10);
> +	}
> +	v4l2_err(sd, "TVP7002 read error %d\n", error);
> +	return error;
> +}
> +
> +/*
> + * tvp7002_write() - Write a value to a register in TVP7002
> + * @sd: ptr to v4l2_subdev struct
> + * @addr: TVP7002 register address
> + * @value: value to be written to the register
> + *
> + * Write a value to a register in an TVP7002 decoder device.
> + * Returns zero if successful, or non-zero otherwise.
> + */
> +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
> +{
> +	struct i2c_client *c = v4l2_get_subdevdata(sd);
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
> +		error = i2c_smbus_write_byte_data(c, addr, value);
> +
> +		if (error >= 0)
> +			return error;
> +		msleep_interruptible(10);
> +	}
> +	v4l2_err(sd, "TVP7002 write error %d\n", error);
> +	return error;
> +}
> +
> +/*
> + * dump_reg_range() - Dump information from TVP7002 registers
> + * @sd: ptr to v4l2_subdev struct
> + * @init: TVP7002 start address
> + * @end: TVP7002 end address
> + *
> + * Dump values at a specified register range
> + * Returns nothing.
> + */
> +static void dump_reg_range(struct v4l2_subdev *sd, u8 init, const u8 end)
> +{
> +	int i = 0;
> +	int result;
> +
> +	while (init != (u8)(end + 1)) {
> +		result = tvp7002_read(sd, init);
> +
> +		if (result < 0)
> +			v4l2_err(sd, "tvp7002: reg 0x%02x unreadable\n", i);

As I mentioned before: don't add the tvp7002 prefix here, v4l2_err/warn/info
does that for you.

> +		else
> +			v4l2_info(sd, "tvp7002: @0x%02x = %02x\n", i, result);
> +
> +		init++;
> +		i++;
> +	}
> +}
> +
> +/*
> + * Macro for handling reading error conditions in tvp7002_log_status
> + */
> +#define TVP7002_LOG_CHK(reg, message, res) \
> +	do {\
> +		(res) = tvp7002_read(sd, (reg));\
> +		\
> +		if ((res) >= 0)\
> +			v4l2_info(sd, "%s = 0x%02x\n", (message), (res));\
> +	} while (0)

Please make this a static inline function. In general macros like this should
be avoided if there is a decent alternative.

> +/*
> + * tvp7002_log_status() - Print information about register settings
> + * @sd: ptr to v4l2_subdev struct
> + *
> + * Log register values of a TVP7002 decoder device.
> + * Returns zero or -EINVAL if read operation fails.

Nobody cares about the return value. Always returning 0 is perfectly
acceptable here. This code is never involved in anything critical, it's just
used during debugging.

> + */
> +static int tvp7002_log_status(struct v4l2_subdev *sd)
> +{
> +	int rres;
> +
> +	TVP7002_LOG_CHK(TVP7002_CHIP_REV, "Chip revision number", rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_LSBS, "H-PLL feedback div LSB",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "H-PLL feedback div MSB",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "VCO freq range selector",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_PHASE_SEL, "ADC sampling clk phase sel",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_CLAMP_START, "Clamp start", rres);
> +	TVP7002_LOG_CHK(TVP7002_CLAMP_W, "Clamp width", rres);
> +	TVP7002_LOG_CHK(TVP7002_HSYNC_OUT_W, "HSYNC output width", rres);
> +	TVP7002_LOG_CHK(TVP7002_B_FINE_GAIN, "Digital fine grain B ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_G_FINE_GAIN, "Digital fine grain G ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_R_FINE_GAIN, "Digital fine grain R ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_B_FINE_OFF_MSBS, "Digital fine grain off B ch",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_G_FINE_OFF_MSBS, "Digital fine grain off G ch",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_R_FINE_OFF_MSBS, "Digital fine grain off R ch",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_FINE_OFF_LSBS, "Dig fine grain off LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_SYNC_CTL_1, "Sync control 1", rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_AND_CLAMP_CTL, "H-PLL and clamp control",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_SYNC_ON_G_THRS, "Sync-On-Green threshold",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_SYNC_SEPARATOR_THRS, "Sync separator thrshold",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_PRE_COAST, "H-PLL pre-coast", rres);
> +	TVP7002_LOG_CHK(TVP7002_HPLL_POST_COAST, "H-PLL post-coast", rres);
> +	TVP7002_LOG_CHK(TVP7002_SYNC_DETECT_STAT, "Sync detect status", rres);
> +	TVP7002_LOG_CHK(TVP7002_OUT_FORMATTER, "Output formatter", rres);
> +	TVP7002_LOG_CHK(TVP7002_MISC_CTL_1, "Miscelaneous control 1", rres);
> +	TVP7002_LOG_CHK(TVP7002_MISC_CTL_2, "Miscelaneous control 2", rres);
> +	TVP7002_LOG_CHK(TVP7002_MISC_CTL_3, "Miscelaneous control 3", rres);
> +	TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_1, "Input Mux Selector 1", rres);
> +	TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_2, "Input Mux Selector 2", rres);
> +	TVP7002_LOG_CHK(TVP7002_B_AND_G_COARSE_GAIN, "B and G coarse gain",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_R_COARSE_GAIN, "R coarse gain", rres);
> +	TVP7002_LOG_CHK(TVP7002_B_COARSE_OFF, "Coarse offset for B ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_G_COARSE_OFF, "Coarse offset for G ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_R_COARSE_OFF, "Coarse offset for R ch", rres);
> +	TVP7002_LOG_CHK(TVP7002_HSOUT_OUT_START, "HSYNC lead edge out start",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_MISC_CTL_4, "Miscelaneous control 4", rres);
> +	TVP7002_LOG_CHK(TVP7002_B_DGTL_ALC_OUT_LSBS, "Flt ALC out B ch LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_G_DGTL_ALC_OUT_LSBS, "Flt ALC out G ch LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_R_DGTL_ALC_OUT_LSBS, "Flt ALC out R ch LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_ENABLE, "Auto level ctrl enable",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_DGTL_ALC_OUT_MSBS, "Filt ALC out RGB chs MSB",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_FILTER, "Auto level ctrl filter",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_FINE_CLAMP_CTL, "Fine clamp control", rres);
> +	TVP7002_LOG_CHK(TVP7002_PWR_CTL, "Power control", rres);
> +	TVP7002_LOG_CHK(TVP7002_ADC_SETUP, "ADC setup", rres);
> +	TVP7002_LOG_CHK(TVP7002_COARSE_CLAMP_CTL, "Coarse clamp ctrl", rres);
> +	TVP7002_LOG_CHK(TVP7002_SOG_CLAMP, "Sync-On-Green clamp", rres);
> +	TVP7002_LOG_CHK(TVP7002_RGB_COARSE_CLAMP_CTL, "RGB coarse clamp ctrl",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_SOG_COARSE_CLAMP_CTL, "SOG coarse clamp ctrl",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_ALC_PLACEMENT, "ALC placement", rres);
> +	TVP7002_LOG_CHK(TVP7002_MVIS_STRIPPER_W, "Macrovision stripper width",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_SYNC_BYPASS, "Sync bypass", rres);
> +	TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_LSBS, "Lines p Frame status LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_MSBS, "Lines p Frame status MSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_LSBS, "Clks p line status LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_MSBS, "Clks p line status MSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_HSYNC_W, "HSYNC width", rres);
> +	TVP7002_LOG_CHK(TVP7002_VSYNC_W, "VSYNC width", rres);
> +	TVP7002_LOG_CHK(TVP7002_L_LENGTH_TOL, "Line length tolerance", rres);
> +	TVP7002_LOG_CHK(TVP7002_VIDEO_BWTH_CTL, "Video bandwth control", rres);
> +	TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_LSBS, "AVID start pixel LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_MSBS, "AVID start pixel MSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_LSBS, "AVID stop pixel LSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_MSBS, "AVID stop pixel MSBs",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_VBLK_F_0_START_L_OFF, "VBLK start line off 0",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_VBLK_F_1_START_L_OFF, "VBLK start line off 1",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_VBLK_F_0_DURATION, "VBLK duration 0", rres);
> +	TVP7002_LOG_CHK(TVP7002_VBLK_F_1_DURATION, "VBLK duration 1", rres);
> +	TVP7002_LOG_CHK(TVP7002_FBIT_F_0_START_L_OFF, "FBIT start line off 0",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_FBIT_F_1_START_L_OFF, "FBIT start line off 1",
> +									rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_LSBS, "YUV Y G LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_MSBS, "YUV Y G MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_LSBS, "YUV Y B LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_MSBS, "YUV Y B MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_LSBS, "YUV Y R LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_MSBS, "YUV Y R MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_LSBS, "YUV U G LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_MSBS, "YUV U G MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_LSBS, "YUV U B LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_MSBS, "YUV U B MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_LSBS, "YUV U R LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_MSBS, "YUV U R MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_LSBS, "YUV V G LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_MSBS, "YUV V G MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_LSBS, "YUV V B LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_MSBS, "YUV V B MSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_LSBS, "YUV V R LSBs", rres);
> +	TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_MSBS, "YUV V R MSBs", rres);
> +
> +	return rres;
> +}
> +
> +/*
> + * tvp7002_g_chip_ident() - Get chip identification number
> + * @sd: ptr to v4l2_subdev struct
> + * @chip: ptr to v4l2_dbg_chip_ident struct
> + *
> + * Obtains the chip's identification number.
> + * Returns zero or -EINVAL if read operation fails.
> + */
> +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
> +					struct v4l2_dbg_chip_ident *chip)
> +{
> +	int rev;
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	rev = tvp7002_read(sd, TVP7002_CHIP_REV);
> +
> +	if (rev < 0)
> +		return -EINVAL;
> +
> +	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002,
> +									rev);
> +}
> +
> +/*
> + * tvp7002_write_inittab() - Write initialization values
> + * @sd: ptr to v4l2_subdev struct
> + * @regs: ptr to i2c_reg_value struct
> + *
> + * Write initialization values.
> + * Returns zero or -EINVAL if read operation fails.
> + */
> +static int tvp7002_write_inittab(struct v4l2_subdev *sd,
> +					const struct i2c_reg_value *regs)
> +{
> +	int i;
> +	int error;

Please add an empty line here.

> +	/* Initialize the first (defined) registers */
> +	while (regs->reg != 0x5c) {
> +		error = tvp7002_write(sd, regs->reg, regs->value);
> +		if (error < 0)
> +			return -EINVAL;
> +		regs++;
> +	}
> +	/* Initialize the last unnamed registers */
> +	for (i = 0x5c; i <= 0xff; i++) {
> +		error = tvp7002_write(sd, i, 0x00);
> +		if (error < 0)
> +			return -EINVAL;
> +		regs++;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * tvp7002_set_video_mode()
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @sdf: index to structure describing format
> + *
> + * Set video standard according to index
> + *
> + * Returns 0 if operation is successful or -EINVAL otherwise
> + */
> +static int tvp7002_set_video_mode(struct v4l2_subdev *sd, int sdf)
> +{
> +	int error;
> +
> +	if (sdf < TVP7002_STD_480I || sdf > TVP7002_STD_1080P_50) {
> +		v4l2_err(sd, "sf out of range\n");
> +		return -ERANGE;
> +	}
> +
> +	/* Print specific information about current format */
> +	v4l2_info(sd, "Setting standard display format...\n");
> +	v4l2_info(sd, "hres = %d vres=%d fr=%d lr=%d prate=%d\n",
> +			tvp7002_resolutions[sdf].hres,
> +			tvp7002_resolutions[sdf].vres,
> +			tvp7002_resolutions[sdf].frate,
> +			tvp7002_resolutions[sdf].lrate,
> +			tvp7002_resolutions[sdf].prate);
> +	/* Set registers accordingly */
> +	error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_MSBS,
> +					tvp7002_resolutions[sdf].reg01);
> +	if (error < 0)
> +		return error;

Hmm, this is awkward coding. What about this:

static inline void tvp7002_write_err(..., int *err)
{
	if (!*err)
		*err = tvp7002_write(...);
}

Then you can just call tvp7002_write_err and at the end return 'err'.
Once an error occurs any subsequent writes won't do anything.

> +
> +	error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_LSBS,
> +					tvp7002_resolutions[sdf].reg02);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_HPLL_CRTL, 
> +					tvp7002_resolutions[sdf].reg03);
> +	if (error < 0)
> +		return error;
> +
> +	error =  tvp7002_write(sd, TVP7002_HPLL_PHASE_SEL,
> +					tvp7002_resolutions[sdf].reg04);
> +	if (error < 0)
> +		return error;
> +
> +	/* Set SD/HD mode registers */
> +
> +	if (sdf < TVP7002_STD_720P_60) {
> +		error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x06);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x10);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x03);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x03);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0x17);
> +		if (error < 0)
> +			return error;
> +
> +		if (sdf < TVP7002_STD_480P) {
> +			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
> +									0x0c);
> +			if (error < 0)
> +				return error;
> +
> +			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
> +									0x24);
> +			if (error < 0)
> +				return error;
> +
> +		} else {
> +			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
> +									0x0a);
> +			if (error < 0)
> +				return error;
> +
> +			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
> +									0x12);
> +			if (error < 0)
> +				return error;
> +		}
> +		error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x08);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x18);
> +		if (error < 0)
> +			return error;
> +	} else {
> +		error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x32);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x20);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x01);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x00);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0xc7);
> +		if (error < 0)
> +			return error;
> +
> +		if(sdf < TVP7002_STD_1080I_60) {
> +			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
> +									0x35);
> +			if (error < 0)
> +				return error;
> +		} else {
> +			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
> +									0x39);
> +			if (error < 0)
> +				return error;
> +		}
> +		error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x00);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x5a);
> +		if (error < 0)
> +			return error;
> +
> +		if(sdf < TVP7002_STD_1080P_60) {
> +			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
> +									0x07);
> +			if (error < 0)
> +				return error;
> +		} else {
> +			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
> +									0x03);
> +			if (error < 0)
> +				return error;
> +		}
> +	}
> +	if (sdf < TVP7002_STD_1080P_60) {
> +		error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x50);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x0f);
> +		if (error < 0)
> +			return error;
> +	} else {
> +		error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x80);
> +		if (error < 0)
> +			return error;
> +
> +		error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x00);
> +		if (error < 0)
> +			return error;
> +	}
> +	/* Set up registers that hold the same value regardless of the
> +	 * SD mode
> +	 */
> +	error = tvp7002_write(sd, TVP7002_SYNC_ON_G_THRS, 0x5d);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
> +	if (error < 0)
> +		return error;
> +
> +	error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
> +	return error;
> +}
> +
> +/*
> + * tvp7002_get_video_mode() - V4L2 decoder interface handler for querystd
> + * @sd: pointer to standard V4L2 sub-device structure
> + *
> + * Returns the current standard detected by TVP7002. If no active input is
> + * detected, returns -1
> + */
> +static v4l2_std_id tvp7002_get_video_mode(struct v4l2_subdev *sd)
> +{
> +	v4l2_std_id error;
> +	int reg01, reg02, reg03;

Please add an empty line after the declarations of the local variables.

> +	reg01 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_MSBS);
> +	reg02 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_LSBS);
> +	reg03 = tvp7002_read(sd, TVP7002_HPLL_CRTL);
> +
> +	if (reg01 < 0 || reg02 < 0 || reg03 < 0) {
> +		error = V4L2_STD_UNKNOWN;
> +		goto found_error;

Why not just 'return V4L2_STD_UNKNOWN;'? Same in the rest of the function.

> +	}
> +
> +	switch(reg01) {
> +	case 0x35:
> +		if (reg02 == 0xa0)
> +			error = V4L2_STD_525I_60;
> +		else
> +			error = V4L2_STD_625I_50;
> +	case 0x36:
> +		if (reg02 == 0xa0)
> +			error = V4L2_STD_525P_60;
> +		else
> +			error = V4L2_STD_625P_50;
> +		break;
> +	case 0x67:
> +		error = V4L2_STD_720P_60;
> +		break;
> +	case 0x7b:
> +		error = V4L2_STD_720P_50;
> +		break;
> +	case 0x89:
> +		if (reg03 == 0x98)
> +			error = V4L2_STD_1080I_60;
> +		else
> +			error = V4L2_STD_1080P_60;
> +		break;
> +	case 0xa5:
> +		if (reg03 == 0x90)
> +			error = V4L2_STD_1080I_50;
> +		else
> +			error = V4L2_STD_1080P_50;
> +		break;
> +	default:
> +		error = V4L2_STD_UNKNOWN;
> +		break;
> +	}
> +
> +found_error:
> +	return error;
> +}
> +
> +/*
> + * tvp7002_querystd() - V4L2 decoder interface handler for querystd
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @std_id: standard V4L2 std_id ioctl enum
> + *
> + * Returns the current standard detected by TVP7002. If no active input is
> + * detected, returns -EINVAL
> + */
> +static int tvp7002_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
> +{
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +	v4l2_std_id current_std;
> +	u8 sync_lock_status;
> +	int res;
> +
> +	if (std_id == NULL)
> +		return -EINVAL;
> +
> +	/* get the current standard */
> +	res = tvp7002_get_video_mode(sd);
> +	if (res == V4L2_STD_UNKNOWN)
> +		return -EINVAL;
> +	current_std = res;
> +
> +	/* check whether signal is locked */
> +	sync_lock_status = tvp7002_read(sd, TVP7002_SYNC_DETECT_STAT);
> +
> +	if (0x02 != (sync_lock_status & 0xff))
> +		return -EINVAL;	/* No input detected */
> +
> +	decoder->video_mode = current_std;

querystd only returns the detected std, it does NOT change any internal state!

> +	*std_id = current_std;
> +
> +	v4l2_info(sd, "Current STD: %d %d @ %d Hz\n",
> +		tvp7002_resolutions[tvp7002_from_std(current_std)].hres,
> +		tvp7002_resolutions[tvp7002_from_std(current_std)].vres,
> +		tvp7002_resolutions[tvp7002_from_std(current_std)].frate);

This should be v4l2_dbg. You don't want to see logging when calling normal
functions like this.

I suggest that you also look into implementing video_ops.g_input_status. This
should be used much more often than it currently is. It may need additional
support from the dm365 driver to actually call it.

> +	return 0;
> +}
> +
> +/*
> + * tvp7002_scanmode() - Returns whether format is progressive
> + * or interlaced
> + */
> +
> +
> +/*
> + * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
> + *
> + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
> + * ioctl is used to negotiate the image capture size and pixel format
> + * without actually making it take effect.
> + */
> +static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
> +{
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +	struct v4l2_pix_format *pix;
> +	v4l2_std_id current_std;
> +	int res;
> +
> +	if (f == NULL)
> +		return -EINVAL;
> +
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		/* only capture is supported */
> +		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

Huh? It should return an -EINVAL if it isn't this type.

> +
> +	pix = &f->fmt.pix;
> +
> +	/* Calculate height and width based on current standard */
> +	res = tvp7002_get_video_mode(sd);
> +	if (res < 0)
> +		return -EINVAL;
> +	current_std = res;
> +
> +	decoder->video_mode = current_std;

Note: try_fmt_cap should never change the internal state of a driver. That is
why it is called 'try': just to see if it can be done, not actually do it :-)

> +	pix->width = tvp7002_resolutions[tvp7002_from_std(current_std)].hres;
> +	pix->height = tvp7002_resolutions[tvp7002_from_std(current_std)].vres;
> +
> +	pix->pixelformat = V4L2_PIX_FMT_UYVY;
> +
> +	pix->field = V4L2_FIELD_INTERLACED;
> +	pix->bytesperline = pix->width * 2;
> +	pix->sizeimage = pix->bytesperline * pix->height;
> +	pix->colorspace = V4L2_COLORSPACE_REC709;

Does the tvp7002 support SDTV? If so, then I believe those formats have a
slightly different colorspace (REC601 I think). I'm not 100% sure, so please
double-check before changing anything.

> +	pix->priv = 0;
> +
> +	v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
> +			"Width - %d, Height - %d",
> +			"8-bit UYVY 4:2:2 Format", pix->bytesperline,
> +			pix->width, pix->height);
> +	return 0;
> +}
> +
> +/**
> + * tvp7002_s_fmt_cap() - V4L2 decoder interface handler for s_fmt
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
> + *
> + * If the requested format is supported, configures the HW to use that
> + * format, returns error code if format not supported or HW can't be
> + * correctly configured.
> + */
> +static int tvp7002_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
> +{
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +	struct v4l2_pix_format *pix;
> +	int rval;
> +
> +	if (f == NULL)
> +		return -EINVAL;
> +
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		/* only capture is supported */
> +		return -EINVAL;
> +
> +	pix = &f->fmt.pix;
> +	rval = tvp7002_try_fmt_cap(sd, f);
> +	if (rval)
> +		return rval;
> +
> +		decoder->pix = *pix;

Indentation.

> +
> +	return rval;
> +}
> +
> +/**
> + * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @f: pointer to standard V4L2 v4l2_format structure
> + *
> + * Returns the decoder's current pixel format in the v4l2_format
> + * parameter.
> + */
> +static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
> +{
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +
> +	if (f == NULL)
> +		return -EINVAL;
> +
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		/* only capture is supported */
> +		return -EINVAL;
> +
> +	f->fmt.pix = decoder->pix;
> +
> +	v4l2_info(sd, "Current FMT: bytesperline - %d"
> +			"Width - %d, Height - %d",
> +			decoder->pix.bytesperline,
> +			decoder->pix.width, decoder->pix.height);

Use v4l2_dbg.

> +	return 0;
> +}
> +
> +/*
> + * tvp7002_s_ctrl() - Set a control
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to v4l2_control struct
> + *
> + * Set a control for a TVP7002 decoder device.
> + * Returns zero when successful or -EINVAL if register access fails.
> + */
> +static int tvp7002_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
> +{
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +	int vmd = 0;
> +
> +	decoder->video_mode = std;
> +	vmd = tvp7002_from_std(std);
> +
> +	v4l2_info(sd, "Set video std mode to %d.\n", (int)std);

Use v4l2_dbg. Please check this source for similar cases.

And also use %llx when printing v4l2_std_id codes. It's a bitmask, and it's
hard to 'see' the bits when printing decimal.

> +
> +	return tvp7002_set_video_mode(sd, vmd);
> +}
> +
> +/*
> + * tvp7002_g_ctrl() - Get a control
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to v4l2_control struct
> + *
> + * Get a control for a TVP7002 decoder device.
> + * Returns zero when successful or -EINVAL if register access fails.
> + */
> +static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +{
> +	int rval, gval, bval;
> +	int res;
> +
> +	v4l2_info(sd, "g_ctrl called\n");
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_GAIN:
> +		rval = tvp7002_read(sd, TVP7002_R_FINE_GAIN);
> +		gval = tvp7002_read(sd, TVP7002_G_FINE_GAIN);
> +		bval = tvp7002_read(sd, TVP7002_B_FINE_GAIN);
> +
> +		if (rval < 0 || gval < 0 || bval < 0) {
> +			res = -1;
> +		} else if (rval != gval || rval != bval) {
> +			res = -1;
> +		} else {
> +			ctrl->value = rval & 0x0F;
> +			res = ctrl->value;
> +		}
> +		break;
> +	default:
> +		res = -1;
> +		break;
> +	}
> +
> +	return res < 0 ? res : 0;
> +}
> +
> +/*
> + * tvp7002_s_ctrl() - Set a control
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to v4l2_control struct
> + *
> + * Set a control in TVP7002 decoder device.
> + * Returns zero when successful or -EINVAL if register access fails.
> + */
> +static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +{
> +	int rval, gval, bval;
> +	int error;
> +	u8 i, n;
> +	n = ARRAY_SIZE(tvp7002_qctrl);
> +
> +	for (i = 0; i < n; i++)
> +		if (ctrl->id == tvp7002_qctrl[i].id)
> +			break;
> +
> +	if (i == n)
> +		return -EINVAL;
> +
> +	if (ctrl->value < tvp7002_qctrl[i].minimum ||
> +			ctrl->value > tvp7002_qctrl[i].maximum)
> +		return -ERANGE;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_GAIN:
> +		rval = tvp7002_write(sd, TVP7002_R_FINE_GAIN,
> +							ctrl->value & 0xff);
> +		gval = tvp7002_write(sd, TVP7002_G_FINE_GAIN,
> +							ctrl->value & 0xff);
> +		bval = tvp7002_write(sd, TVP7002_B_FINE_GAIN,
> +							ctrl->value & 0xff);
> +		if (rval < 0  || gval < 0 || bval < 0)
> +			error = -1;
> +		else
> +			error = rval;
> +		break;
> +	default:
> +		error = -1;
> +		break;
> +	}
> +
> +	if (error < 0)
> +		return -EINVAL;
> +	else
> +		return 0;
> +}
> +
> +/*
> + * tvp7002_g_register() - Get the value of a register
> + * @sd: ptr to v4l2_subdev struct
> + * @vreg: ptr to v4l2_dbg_register struct
> + *
> + * Get the value of a TVP7002 decoder device register.
> + * Returns zero when successful, -EINVAL if register read fails or
> + * access to I2C client fails, -EPERM if the call is not allowed
> + * by diabled CAP_SYS_ADMIN.
> + */
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int tvp7002_g_register(struct v4l2_subdev *sd,
> +						struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int error;
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	reg->val = tvp7002_read(sd, reg->reg & 0xff);
> +	reg->size = 1;
> +
> +	if (reg->val < 0)
> +		error = -EINVAL;
> +	else
> +		error = 0;
> +
> +	return error;

return reg->val < 0 ? -EINVAL : 0;

> +}
> +
> +/*
> + * tvp7002_s_register() - set a control
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to v4l2_control struct
> + *
> + * Get the value of a TVP7002 decoder device register.
> + * Returns zero when successful or -EINVAL if register read fails.
> + */
> +static int tvp7002_s_register(struct v4l2_subdev *sd,
> +						struct v4l2_dbg_register *reg)
> +{
> +	int error, wres;
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
> +
> +	if (wres < 0)
> +		error = -EINVAL;
> +	else
> +		error = 0;
> +
> +	return error;

Ditto.

> +}
> +#endif
> +
> +/*
> + * tvp7002_queryctrl() - Query a control
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to v4l2_queryctrl struct
> + *
> + * Query a control of a TVP7002 decoder device.
> + * Returns zero when successful or -EINVAL if register read fails.
> + */
> +static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
> +{
> +	int i, error;
> +	error = -EINVAL;
> +
> +	v4l2_info(sd, "queryctrl called\n");
> +
> +	for (i = 0; i < ARRAY_SIZE(tvp7002_qctrl); i++)
> +		if (qc->id && qc->id == tvp7002_qctrl[i].id) {
> +			memcpy(qc, &(tvp7002_qctrl[i]), sizeof(*qc));
> +			error = 0;
> +			break;
> +		}

Please use the v4l2_ctrl_query_fill function from v4l2-common.h for this.
It will ensure consistent control names and it will be easier to convert to
the upcoming control handling framework that I am working on. It's also
shorter :-)

> +
> +	return error;
> +}
> +
> +/*
> + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
> + * @sd: pointer to standard V4L2 sub-device structure
> + * @enable: streaming enable or disable
> + *
> + * Sets streaming to enable or disable, if possible.
> + */
> +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	int err = 0;
> +	struct tvp7002 *decoder = to_tvp7002(sd);
> +
> +	if (decoder->streaming == enable)
> +		return 0;
> +
> +	if (enable) {
> +		/* Power Up Sequence */
> +		err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x00);
> +		if (err) {
> +			v4l2_err(sd, "Unable to turn on decoder\n");
> +			err = -EINVAL;
> +		}
> +		err = tvp7002_write_inittab(sd, tvp7002_init_default);
> +		if (err < 0) {
> +			v4l2_err(sd, "Unable to initialize\n");
> +			err = -EINVAL;
> +		}
> +		/* Detect if not already detected */
> +		err = tvp7002_read(sd, TVP7002_CHIP_REV);
> +		if (err < 0) {
> +			v4l2_err(sd, "Unable to detect decoder\n");
> +			err = -EINVAL;
> +		}
> +		decoder->streaming = enable;
> +	} else {
> +		/* Power Down Sequence */
> +		err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x40);
> +		if (err) {
> +			v4l2_err(sd, "Unable to turn off decoder\n");
> +			return err;
> +		}
> +		decoder->streaming = enable;
> +	}
> +
> +	return err;
> +}
> +
> +/* Specific video subsystem operation handlers */
> +static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
> +	.querystd = tvp7002_querystd,
> +	.s_stream = tvp7002_s_stream,
> +	.g_fmt = tvp7002_g_fmt,
> +};
> +
> +/* V4L2 Operations handlers */
> +static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
> +	.g_chip_ident = tvp7002_g_chip_ident,
> +	.log_status = tvp7002_log_status,
> +	.g_ctrl = tvp7002_g_ctrl,
> +	.s_ctrl = tvp7002_s_ctrl,
> +	.queryctrl = tvp7002_queryctrl,
> +	.s_std = tvp7002_s_std,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = tvp7002_g_register,
> +	.s_register = tvp7002_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_subdev_ops tvp7002_ops = {
> +	.core = &tvp7002_core_ops,
> +	.video = &tvp7002_video_ops,
> +};
> +
> +/*
> + * tvp7002_reset - Reset a TVP7002 device
> + * @sd: ptr to v4l2_subdev struct
> + * @val: unsigned integer (not used)
> + *
> + * Reset the TVP7002 device
> + * Returns zero when successful or -EINVAL if register read fails.
> + */
> +static int tvp7002_reset(struct v4l2_subdev *sd, u32 val)
> +{
> +	int polarity;
> +	int error;
> +
> +	error = tvp7002_read(sd, TVP7002_CHIP_REV);
> +	if (error < 0) {
> +		error = -EINVAL;
> +		goto found_error;
> +	}
> +
> +	if (error == 0x02) {
> +		v4l2_info(sd, "rev. %02x detected.\n", error);
> +	} else {
> +		v4l2_info(sd, "unknown revision detected.\n");
> +		v4l2_info(sd, "revision number is %02x\n", error);
> +	}
> +
> +	/* Set polarity information */
> +	polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
> +			tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
> +	if (error < 0) {
> +		error = -EINVAL;
> +		goto found_error;
> +	}
> +
> +	/* Initializes TVP7002 to its default values */
> +	error = tvp7002_write_inittab(sd, tvp7002_init_default);
> +	if (error < 0) {
> +		error = -EINVAL;
> +		goto found_error;
> +	}
> +
> +found_error:
> +	return error;
> +};
> +
> +/*
> + * tvp7002_probe - Reset a TVP7002 device
> + * @sd: ptr to v4l2_subdev struct
> + * @ctrl: ptr to i2c_device_id struct
> + *
> + * Reset the TVP7002 device
> + * Returns zero when successful or -EINVAL if register read fails.
> + */
> +static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
> +{
> +	struct v4l2_subdev *sd;
> +	struct tvp7002 *core;
> +	int polarity;
> +	int error;
> +
> +	/* Check if the adapter supports the needed features */
> +	if (!i2c_check_functionality(c->adapter,
> +		I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -EIO;
> +
> +	core = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
> +
> +	if (!core)
> +		return -ENOMEM;
> +
> +	sd = &core->sd;
> +	v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
> +	v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
> +					c->addr << 1, c->adapter->name);
> +
> +	/* Set polarity information */
> +	polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
> +			tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
> +	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
> +	if (error < 0) {
> +		error = -EINVAL;
> +		goto found_error;
> +	}
> +
> +	/* Set video mode */
> +	core->video_mode = V4L2_STD_525P_60;
> +
> +	if (debug > 1)
> +		error = tvp7002_log_status(sd);

Ignore the error here. I am not actually sure how useful it is to have this
here as you can call log_status at any time (v4l2-ctl --log-status).

> +	else
> +		error = 0;
> +
> +found_error:
> +	if (error < 0)
> +		kfree(core);
> +
> +	return error;
> +}
> +
> +/*
> + * tvp7002_remove - Remove TVP7002 device support
> + * @c: ptr to i2c_client struct
> + *
> + * Reset the TVP7002 device
> + * Returns zero when successful or -EINVAL if register read fails.
> + */
> +static int tvp7002_remove(struct i2c_client *c)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(c);
> +
> +	v4l2_dbg(1, debug, sd, "tvp7002.c: removing tvp7002 adapter"
> +				"on address 0x%x\n", c->addr << 1);

Remove tvp7002 prefix.

> +
> +	v4l2_device_unregister_subdev(sd);
> +	kfree(to_tvp7002(sd));
> +	return 0;
> +}
> +
> +/* I2C Device ID table */
> +static const struct i2c_device_id tvp7002_id[] = {
> +	{ "tvp7002", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, tvp7002_id);
> +
> +/* I2C driver data */
> +static struct i2c_driver tvp7002_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "tvp7002",
> +	},
> +	.probe = tvp7002_probe,
> +	.remove = tvp7002_remove,
> +	.id_table = tvp7002_id,
> +};

I have no comments on the other patches, it's just this one that needs another
update. 

Regards,

	Hans
Murali Karicheri Sept. 2, 2009, 7:06 p.m. UTC | #2
Santiago,

you are missing a tvp7002_init() that calls i2c_add_driver(). Similarly i2c_del_driver(). See tvp514x.c or adv7353.c for examples... Also read the
polarities from platform_data in probe(). I had mentioned the second comment during earlier review.

Murali Karicheri
Software Design Engineer
Texas Instruments Inc.
Germantown, MD 20874
new phone: 301-407-9583
Old Phone : 301-515-3736 (will be deprecated)
email: m-karicheri2@ti.com

>-----Original Message-----
>From: santiago.nunez@ridgerun.com [mailto:santiago.nunez@ridgerun.com]
>Sent: Tuesday, September 01, 2009 6:55 PM
>To: Karicheri, Muralidharan
>Cc: davinci-linux-open-source@linux.davincidsp.com;
>todd.fischer@ridgerun.com; diego.dompe@ridgerun.com;
>clark.becker@ridgerun.com; Narnakaje, Snehaprabha; Santiago Nunez-Corrales
>Subject: [PATCH 5/6 v3] Updated TVP7002 driver for DM365
>
>From: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>
>This patch provides the implementation of the TVP7002 decoder
>driver for DM365. Implemented revisions by Vaibhav Hiremath
>and Hans Verkuil. Removed most of controls, cleared up logic,
>cleaned up code.
>
>Signed-off-by: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>---
> drivers/media/video/tvp7002.c | 1493
>+++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 1493 insertions(+), 0 deletions(-)
> create mode 100644 drivers/media/video/tvp7002.c
>
>diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
>new file mode 100644
>index 0000000..e8cd77e
>--- /dev/null
>+++ b/drivers/media/video/tvp7002.c
>@@ -0,0 +1,1493 @@
>+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
>+ * Digitizer with Horizontal PLL registers
>+ *
>+ * Copyright (C) 2009 Texas Instruments Inc
>+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>+ *
>+ * This code is partially based upon the TVP5150 driver
>+ * written by Mauro Carvalho Chehab (mchehab@infradead.org)
>+ * and the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
>+ *
>+ * 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.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU General Public License
>+ * along with this program; if not, write to the Free Software
>+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>+ */
>+
>+#include <linux/i2c.h>
>+#include <linux/videodev2.h>
>+#include <linux/delay.h>
>+#include <media/v4l2-device.h>
>+#include <media/tvp7002.h>
>+#include <media/v4l2-i2c-drv.h>
>+#include <media/v4l2-chip-ident.h>
>+#include <media/davinci/videohd.h>
>+#include "tvp7002_reg.h"
>+
>+MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
>+MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
>+MODULE_LICENSE("GPL");
>+
>+/* I2C retry attempts */
>+#define I2C_RETRY_COUNT                 (5)
>+
>+/* Debugging information */
>+
>+static int debug;
>+module_param(debug, int, 0);
>+MODULE_PARM_DESC(debug, "Debug level (0-2)");
>+
>+/* Reference to tvp7002_platform_data */
>+extern struct tvp7002_platform_data tvp7002_pdata;
>+
>+/* Struct for handling resolutions and associate register values */
>+struct tvp7002_resol {
>+      v4l2_std_id id;
>+      int hres;
>+      int vres;
>+      int frate;
>+      int lrate;
>+      int prate;
>+      u8 reg01;
>+      u8 reg02;
>+      u8 reg03;
>+      u8 reg04;
>+};
>+
>+/* Struct for handling register values */
>+struct i2c_reg_value {
>+      u8 reg;
>+      u8 value;
>+};
>+
>+/* Register default values (according to tvp7002 datasheet) */
>+static const struct i2c_reg_value tvp7002_init_default[] = {
>+      /* 0x00: read only */
>+      { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67 },
>+      { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20 },
>+      { TVP7002_HPLL_CRTL, 0xa8 },
>+      { TVP7002_HPLL_PHASE_SEL, 0x80 },
>+      { TVP7002_CLAMP_START, 0x32 },
>+      { TVP7002_CLAMP_W, 0x20 },
>+      { TVP7002_HSYNC_OUT_W, 0x20 },
>+      { TVP7002_B_FINE_GAIN, 0x00 },
>+      { TVP7002_G_FINE_GAIN, 0x00 },
>+      { TVP7002_R_FINE_GAIN, 0x00 },
>+      { TVP7002_B_FINE_OFF_MSBS, 0x80 },
>+      { TVP7002_G_FINE_OFF_MSBS, 0x80 },
>+      { TVP7002_R_FINE_OFF_MSBS, 0x80 },
>+      { TVP7002_SYNC_CTL_1, 0x5b },
>+      { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e },
>+      { TVP7002_SYNC_ON_G_THRS, 0x5d },
>+      { TVP7002_SYNC_SEPARATOR_THRS, 0x20 },
>+      { TVP7002_HPLL_PRE_COAST, 0x00 },
>+      { TVP7002_HPLL_POST_COAST, 0x00 },
>+      /* 0x14: read only */
>+      { TVP7002_OUT_FORMATTER, 0x00 },
>+      { TVP7002_MISC_CTL_1, 0x11 },
>+      { TVP7002_MISC_CTL_2, 0x03 },
>+      { TVP7002_MISC_CTL_3, 0x00 },
>+      { TVP7002_IN_MUX_SEL_1, 0x00 },
>+      { TVP7002_IN_MUX_SEL_2, 0xc2 },
>+      { TVP7002_B_AND_G_COARSE_GAIN, 0x77 },
>+      { TVP7002_R_COARSE_GAIN, 0x07 },
>+      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>+      { TVP7002_FINE_OFF_LSBS, 0x00 },
>+      { TVP7002_B_COARSE_OFF, 0x10 },
>+      { TVP7002_G_COARSE_OFF, 0x10 },
>+      { TVP7002_R_COARSE_OFF, 0x10 },
>+      { TVP7002_HSOUT_OUT_START, 0x0d },
>+      { TVP7002_MISC_CTL_4, 0x0d },
>+      /* 0x23: read only */
>+      /* 0x24: read only */
>+      /* 0x25: read only */
>+      { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80 },
>+      /* 0x27: read only */
>+      { TVP7002_AUTO_LVL_CTL_FILTER, 0x53 },
>+      { TVP7002_FINE_CLAMP_CTL, 0x07 },
>+      { TVP7002_PWR_CTL, 0x00 },
>+      { TVP7002_ADC_SETUP, 0x50 },
>+      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>+      { TVP7002_SOG_CLAMP, 0x80 },
>+      { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c },
>+      { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04 },
>+      { TVP7002_ALC_PLACEMENT, 0x5a },
>+      { TVP7002_MVIS_STRIPPER_W, 0x03 },
>+      { TVP7002_VSYNC_ALGN, 0x10 },
>+      { TVP7002_SYNC_BYPASS, 0x00 },
>+      /* 0x37: read only */
>+      /* 0x38: read only */
>+      /* 0x39: read only */
>+      /* 0x3a: read only */
>+      /* 0x3b: read only */
>+      /* 0x3c: read only */
>+      { TVP7002_L_LENGTH_TOL, 0x03 },
>+      { TVP7002_VIDEO_BWTH_CTL, 0x00 },
>+      { TVP7002_AVID_START_PIXEL_LSBS, 0x01 },
>+      { TVP7002_AVID_START_PIXEL_MSBS, 0x2c },
>+      { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06 },
>+      { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c },
>+      { TVP7002_VBLK_F_0_START_L_OFF, 0x05 },
>+      { TVP7002_VBLK_F_1_START_L_OFF, 0x05 },
>+      { TVP7002_VBLK_F_0_DURATION, 0x1e },
>+      { TVP7002_VBLK_F_1_DURATION, 0x1e },
>+      { TVP7002_FBIT_F_0_START_L_OFF, 0x00 },
>+      { TVP7002_FBIT_F_1_START_L_OFF, 0x00 },
>+      { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3 },
>+      { TVP7002_YUV_Y_G_COEF_MSBS, 0x16 },
>+      { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f },
>+      { TVP7002_YUV_Y_B_COEF_MSBS, 0x02 },
>+      { TVP7002_YUV_Y_R_COEF_LSBS, 0xce },
>+      { TVP7002_YUV_Y_R_COEF_MSBS, 0x06 },
>+      { TVP7002_YUV_U_G_COEF_LSBS, 0xab },
>+      { TVP7002_YUV_U_G_COEF_MSBS, 0xf3 },
>+      { TVP7002_YUV_U_B_COEF_LSBS, 0x00 },
>+      { TVP7002_YUV_U_B_COEF_MSBS, 0x10 },
>+      { TVP7002_YUV_U_R_COEF_LSBS, 0x55 },
>+      { TVP7002_YUV_U_R_COEF_MSBS, 0xfc },
>+      { TVP7002_YUV_V_G_COEF_LSBS, 0x78 },
>+      { TVP7002_YUV_V_G_COEF_MSBS, 0xf1 },
>+      { TVP7002_YUV_V_B_COEF_LSBS, 0x88 },
>+      { TVP7002_YUV_V_B_COEF_MSBS, 0xfe },
>+      { TVP7002_YUV_V_R_COEF_LSBS, 0x00 },
>+      { TVP7002_YUV_V_R_COEF_MSBS, 0x10 },
>+      { 0x5c, 0x00 }  /* end of registers */
>+};
>+
>+/* Available resolutions */
>+static struct tvp7002_resol tvp7002_resolutions[] = {
>+      {
>+              .id = V4L2_STD_525I_60,
>+              .hres = 720,
>+              .vres = 480,
>+              .frate = 30,
>+              .lrate = 15,
>+              .prate = 14,
>+              .reg01 = 0x35,
>+              .reg02 = 0xa0,
>+              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_625I_50,
>+              .hres = 720,
>+              .vres = 576,
>+              .frate = 25,
>+              .lrate = 16,
>+              .prate = 14,
>+              .reg01 = 0x36,
>+              .reg02 = 0x00,
>+              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_525P_60,
>+              .hres = 720,
>+              .vres = 480,
>+              .frate = 60,
>+              .lrate = 31,
>+              .prate = 27,
>+              .reg01 = 0x35,
>+              .reg02 = 0xa0,
>+              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_625P_50,
>+              .hres = 720,
>+              .vres = 576,
>+              .frate = 50,
>+              .lrate = 31,
>+              .prate = 27,
>+              .reg01 = 0x36,
>+              .reg02 = 0x00,
>+              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_720P_60,
>+              .hres = 1280,
>+              .vres = 720,
>+              .frate = 60,
>+              .lrate = 45,
>+              .prate = 74,
>+              .reg01 = 0x67,
>+              .reg02 = 0x20,
>+              .reg03 = TVP7002_VCO_RANGE_MED | 0x20,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_720P_50,
>+              .hres = 1280,
>+              .vres = 720,
>+              .frate = 50,
>+              .lrate = 38,
>+              .prate = 74,
>+              .reg01 = 0x7b,
>+              .reg02 = 0xc0,
>+              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_1080I_60,
>+              .hres = 1920,
>+              .vres = 1080,
>+              .frate = 60,
>+              .lrate = 34,
>+              .prate = 74,
>+              .reg01 = 0x89,
>+              .reg02 = 0x80,
>+              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_1080I_50,
>+              .hres = 1920,
>+              .vres = 1080,
>+              .frate = 50,
>+              .lrate = 28,
>+              .prate = 74,
>+              .reg01 = 0xa5,
>+              .reg02 = 0x00,
>+              .reg03 = TVP7002_VCO_RANGE_MED | 0x10,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_1080P_60,
>+              .hres = 1920,
>+              .vres = 1080,
>+              .frate = 60,
>+              .lrate = 68,
>+              .prate = 149,
>+              .reg01 = 0x89,
>+              .reg02 = 0x80,
>+              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x20,
>+              .reg04 = 0x80,
>+      },
>+      {
>+              .id = V4L2_STD_1080P_50,
>+              .hres = 1920,
>+              .vres = 1080,
>+              .frate = 50,
>+              .lrate = 56,
>+              .prate = 149,
>+              .reg01 = 0xa5,
>+              .reg02 = 0x00,
>+              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x18,
>+              .reg04 = 0x80,
>+      },
>+};
>+
>+/*
>+ * tvp7002_from_std - Map video standard to register information
>+ * @std: v4l2_std_id (u64) integer
>+ *
>+ * Returns index of std or -1 in case of error.
>+ */
>+int tvp7002_from_std(v4l2_std_id std)
>+{
>+      int res;
>+
>+      switch (std) {
>+      case V4L2_STD_525P_60:
>+              res = TVP7002_STD_480P;
>+              break;
>+      case V4L2_STD_525I_60:
>+              res = TVP7002_STD_480I;
>+              break;
>+      case V4L2_STD_625P_50:
>+              res = TVP7002_STD_576P;
>+              break;
>+      case V4L2_STD_625I_50:
>+              res = TVP7002_STD_576I;
>+              break;
>+      case V4L2_STD_720P_50:
>+              res = TVP7002_STD_720P_50;
>+              break;
>+      case V4L2_STD_720P_60:
>+              res = TVP7002_STD_720P_60;
>+              break;
>+      case V4L2_STD_1080I_50:
>+              res = TVP7002_STD_1080I_50;
>+              break;
>+      case V4L2_STD_1080I_60:
>+              res = TVP7002_STD_1080I_60;
>+              break;
>+      case V4L2_STD_1080P_50:
>+              res = TVP7002_STD_1080P_50;
>+              break;
>+      case V4L2_STD_1080P_60:
>+              res = TVP7002_STD_1080P_60;
>+              break;
>+      default:
>+              res = -1;
>+              break;
>+      }
>+
>+      return res;
>+}
>+
>+/* Device definition */
>+struct tvp7002 {
>+      struct v4l2_subdev sd;
>+      v4l2_std_id video_mode;
>+      struct v4l2_pix_format pix;
>+      int streaming;
>+};
>+
>+/* Supported controls */
>+static struct v4l2_queryctrl tvp7002_qctrl[] = {
>+      {
>+              /* This gain control uses fine grain in TVP7002 */
>+              .id = V4L2_CID_GAIN,
>+              .type = V4L2_CTRL_TYPE_INTEGER,
>+              .name = "Gain for RGB channels",
>+              .minimum = 0,
>+              .maximum = 255,
>+              .step = 1,
>+              .default_value = 0,
>+              .flags = 0,
>+      },
>+};
>+
>+/*
>+ * to_tvp7002 - Obtain device handler TVP7002
>+ * @sd: ptr to v4l2_subdev struct
>+ *
>+ * Returns device handler tvp7002.
>+ */
>+static struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
>+{
>+      return container_of(sd, struct tvp7002, sd);
>+}
>+
>+/*
>+ * tvp7002_read - Read a value from a register in an TVP7002
>+ * @sd: ptr to v4l2_subdev struct
>+ * @reg: TVP7002 register address
>+ *
>+ * Returns value read if successful, or non-zero (-1) otherwise.
>+ */
>+static int tvp7002_read(struct v4l2_subdev *sd, unsigned char addr)
>+{
>+      struct i2c_client *c = v4l2_get_subdevdata(sd);
>+      int retry;
>+      int error;
>+
>+      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>+              error = i2c_smbus_read_byte_data(c, addr);
>+
>+              if (error >= 0)
>+                      return error;
>+              msleep_interruptible(10);
>+      }
>+      v4l2_err(sd, "TVP7002 read error %d\n", error);
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_write() - Write a value to a register in TVP7002
>+ * @sd: ptr to v4l2_subdev struct
>+ * @addr: TVP7002 register address
>+ * @value: value to be written to the register
>+ *
>+ * Write a value to a register in an TVP7002 decoder device.
>+ * Returns zero if successful, or non-zero otherwise.
>+ */
>+static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
>+{
>+      struct i2c_client *c = v4l2_get_subdevdata(sd);
>+      int retry;
>+      int error;
>+
>+      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>+              error = i2c_smbus_write_byte_data(c, addr, value);
>+
>+              if (error >= 0)
>+                      return error;
>+              msleep_interruptible(10);
>+      }
>+      v4l2_err(sd, "TVP7002 write error %d\n", error);
>+      return error;
>+}
>+
>+/*
>+ * dump_reg_range() - Dump information from TVP7002 registers
>+ * @sd: ptr to v4l2_subdev struct
>+ * @init: TVP7002 start address
>+ * @end: TVP7002 end address
>+ *
>+ * Dump values at a specified register range
>+ * Returns nothing.
>+ */
>+static void dump_reg_range(struct v4l2_subdev *sd, u8 init, const u8 end)
>+{
>+      int i = 0;
>+      int result;
>+
>+      while (init != (u8)(end + 1)) {
>+              result = tvp7002_read(sd, init);
>+
>+              if (result < 0)
>+                      v4l2_err(sd, "tvp7002: reg 0x%02x unreadable\n", i);
>+              else
>+                      v4l2_info(sd, "tvp7002: @0x%02x = %02x\n", i, result);
>+
>+              init++;
>+              i++;
>+      }
>+}
>+
>+/*
>+ * Macro for handling reading error conditions in tvp7002_log_status
>+ */
>+#define TVP7002_LOG_CHK(reg, message, res) \
>+      do {\
>+              (res) = tvp7002_read(sd, (reg));\
>+              \
>+              if ((res) >= 0)\
>+                      v4l2_info(sd, "%s = 0x%02x\n", (message), (res));\
>+      } while (0)
>+/*
>+ * tvp7002_log_status() - Print information about register settings
>+ * @sd: ptr to v4l2_subdev struct
>+ *
>+ * Log register values of a TVP7002 decoder device.
>+ * Returns zero or -EINVAL if read operation fails.
>+ */
>+static int tvp7002_log_status(struct v4l2_subdev *sd)
>+{
>+      int rres;
>+
>+      TVP7002_LOG_CHK(TVP7002_CHIP_REV, "Chip revision number", rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_LSBS, "H-PLL feedback div LSB",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "H-PLL feedback div MSB",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "VCO freq range selector",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_PHASE_SEL, "ADC sampling clk phase sel",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_CLAMP_START, "Clamp start", rres);
>+      TVP7002_LOG_CHK(TVP7002_CLAMP_W, "Clamp width", rres);
>+      TVP7002_LOG_CHK(TVP7002_HSYNC_OUT_W, "HSYNC output width", rres);
>+      TVP7002_LOG_CHK(TVP7002_B_FINE_GAIN, "Digital fine grain B ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_G_FINE_GAIN, "Digital fine grain G ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_R_FINE_GAIN, "Digital fine grain R ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_B_FINE_OFF_MSBS, "Digital fine grain off B
>ch",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_G_FINE_OFF_MSBS, "Digital fine grain off G
>ch",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_R_FINE_OFF_MSBS, "Digital fine grain off R
>ch",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_FINE_OFF_LSBS, "Dig fine grain off LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_SYNC_CTL_1, "Sync control 1", rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_AND_CLAMP_CTL, "H-PLL and clamp control",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_SYNC_ON_G_THRS, "Sync-On-Green threshold",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_SYNC_SEPARATOR_THRS, "Sync separator
>thrshold",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_PRE_COAST, "H-PLL pre-coast", rres);
>+      TVP7002_LOG_CHK(TVP7002_HPLL_POST_COAST, "H-PLL post-coast", rres);
>+      TVP7002_LOG_CHK(TVP7002_SYNC_DETECT_STAT, "Sync detect status",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_OUT_FORMATTER, "Output formatter", rres);
>+      TVP7002_LOG_CHK(TVP7002_MISC_CTL_1, "Miscelaneous control 1", rres);
>+      TVP7002_LOG_CHK(TVP7002_MISC_CTL_2, "Miscelaneous control 2", rres);
>+      TVP7002_LOG_CHK(TVP7002_MISC_CTL_3, "Miscelaneous control 3", rres);
>+      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_1, "Input Mux Selector 1", rres);
>+      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_2, "Input Mux Selector 2", rres);
>+      TVP7002_LOG_CHK(TVP7002_B_AND_G_COARSE_GAIN, "B and G coarse gain",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_R_COARSE_GAIN, "R coarse gain", rres);
>+      TVP7002_LOG_CHK(TVP7002_B_COARSE_OFF, "Coarse offset for B ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_G_COARSE_OFF, "Coarse offset for G ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_R_COARSE_OFF, "Coarse offset for R ch",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_HSOUT_OUT_START, "HSYNC lead edge out start",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_MISC_CTL_4, "Miscelaneous control 4", rres);
>+      TVP7002_LOG_CHK(TVP7002_B_DGTL_ALC_OUT_LSBS, "Flt ALC out B ch LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_G_DGTL_ALC_OUT_LSBS, "Flt ALC out G ch LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_R_DGTL_ALC_OUT_LSBS, "Flt ALC out R ch LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_ENABLE, "Auto level ctrl enable",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_DGTL_ALC_OUT_MSBS, "Filt ALC out RGB chs MSB",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_FILTER, "Auto level ctrl filter",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_FINE_CLAMP_CTL, "Fine clamp control", rres);
>+      TVP7002_LOG_CHK(TVP7002_PWR_CTL, "Power control", rres);
>+      TVP7002_LOG_CHK(TVP7002_ADC_SETUP, "ADC setup", rres);
>+      TVP7002_LOG_CHK(TVP7002_COARSE_CLAMP_CTL, "Coarse clamp ctrl", rres);
>+      TVP7002_LOG_CHK(TVP7002_SOG_CLAMP, "Sync-On-Green clamp", rres);
>+      TVP7002_LOG_CHK(TVP7002_RGB_COARSE_CLAMP_CTL, "RGB coarse clamp ctrl",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_SOG_COARSE_CLAMP_CTL, "SOG coarse clamp ctrl",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_ALC_PLACEMENT, "ALC placement", rres);
>+      TVP7002_LOG_CHK(TVP7002_MVIS_STRIPPER_W, "Macrovision stripper width",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_SYNC_BYPASS, "Sync bypass", rres);
>+      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_LSBS, "Lines p Frame status
>LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_MSBS, "Lines p Frame status
>MSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_LSBS, "Clks p line status LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_MSBS, "Clks p line status MSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_HSYNC_W, "HSYNC width", rres);
>+      TVP7002_LOG_CHK(TVP7002_VSYNC_W, "VSYNC width", rres);
>+      TVP7002_LOG_CHK(TVP7002_L_LENGTH_TOL, "Line length tolerance", rres);
>+      TVP7002_LOG_CHK(TVP7002_VIDEO_BWTH_CTL, "Video bandwth control",
>rres);
>+      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_LSBS, "AVID start pixel
>LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_MSBS, "AVID start pixel
>MSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_LSBS, "AVID stop pixel LSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_MSBS, "AVID stop pixel MSBs",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_START_L_OFF, "VBLK start line off 0",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_START_L_OFF, "VBLK start line off 1",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_DURATION, "VBLK duration 0", rres);
>+      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_DURATION, "VBLK duration 1", rres);
>+      TVP7002_LOG_CHK(TVP7002_FBIT_F_0_START_L_OFF, "FBIT start line off 0",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_FBIT_F_1_START_L_OFF, "FBIT start line off 1",
>+                                                                      rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_LSBS, "YUV Y G LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_MSBS, "YUV Y G MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_LSBS, "YUV Y B LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_MSBS, "YUV Y B MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_LSBS, "YUV Y R LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_MSBS, "YUV Y R MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_LSBS, "YUV U G LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_MSBS, "YUV U G MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_LSBS, "YUV U B LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_MSBS, "YUV U B MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_LSBS, "YUV U R LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_MSBS, "YUV U R MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_LSBS, "YUV V G LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_MSBS, "YUV V G MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_LSBS, "YUV V B LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_MSBS, "YUV V B MSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_LSBS, "YUV V R LSBs", rres);
>+      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_MSBS, "YUV V R MSBs", rres);
>+
>+      return rres;
>+}
>+
>+/*
>+ * tvp7002_g_chip_ident() - Get chip identification number
>+ * @sd: ptr to v4l2_subdev struct
>+ * @chip: ptr to v4l2_dbg_chip_ident struct
>+ *
>+ * Obtains the chip's identification number.
>+ * Returns zero or -EINVAL if read operation fails.
>+ */
>+static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
>+                                      struct v4l2_dbg_chip_ident *chip)
>+{
>+      int rev;
>+      struct i2c_client *client = v4l2_get_subdevdata(sd);
>+
>+      rev = tvp7002_read(sd, TVP7002_CHIP_REV);
>+
>+      if (rev < 0)
>+              return -EINVAL;
>+
>+      return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002,
>+                                                                      rev);
>+}
>+
>+/*
>+ * tvp7002_write_inittab() - Write initialization values
>+ * @sd: ptr to v4l2_subdev struct
>+ * @regs: ptr to i2c_reg_value struct
>+ *
>+ * Write initialization values.
>+ * Returns zero or -EINVAL if read operation fails.
>+ */
>+static int tvp7002_write_inittab(struct v4l2_subdev *sd,
>+                                      const struct i2c_reg_value *regs)
>+{
>+      int i;
>+      int error;
>+      /* Initialize the first (defined) registers */
>+      while (regs->reg != 0x5c) {
>+              error = tvp7002_write(sd, regs->reg, regs->value);
>+              if (error < 0)
>+                      return -EINVAL;
>+              regs++;
>+      }
>+      /* Initialize the last unnamed registers */
>+      for (i = 0x5c; i <= 0xff; i++) {
>+              error = tvp7002_write(sd, i, 0x00);
>+              if (error < 0)
>+                      return -EINVAL;
>+              regs++;
>+      }
>+
>+      return 0;
>+}
>+
>+/*
>+ * tvp7002_set_video_mode()
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @sdf: index to structure describing format
>+ *
>+ * Set video standard according to index
>+ *
>+ * Returns 0 if operation is successful or -EINVAL otherwise
>+ */
>+static int tvp7002_set_video_mode(struct v4l2_subdev *sd, int sdf)
>+{
>+      int error;
>+
>+      if (sdf < TVP7002_STD_480I || sdf > TVP7002_STD_1080P_50) {
>+              v4l2_err(sd, "sf out of range\n");
>+              return -ERANGE;
>+      }
>+
>+      /* Print specific information about current format */
>+      v4l2_info(sd, "Setting standard display format...\n");
>+      v4l2_info(sd, "hres = %d vres=%d fr=%d lr=%d prate=%d\n",
>+                      tvp7002_resolutions[sdf].hres,
>+                      tvp7002_resolutions[sdf].vres,
>+                      tvp7002_resolutions[sdf].frate,
>+                      tvp7002_resolutions[sdf].lrate,
>+                      tvp7002_resolutions[sdf].prate);
>+      /* Set registers accordingly */
>+      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_MSBS,
>+                                      tvp7002_resolutions[sdf].reg01);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_LSBS,
>+                                      tvp7002_resolutions[sdf].reg02);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_HPLL_CRTL,
>+                                      tvp7002_resolutions[sdf].reg03);
>+      if (error < 0)
>+              return error;
>+
>+      error =  tvp7002_write(sd, TVP7002_HPLL_PHASE_SEL,
>+                                      tvp7002_resolutions[sdf].reg04);
>+      if (error < 0)
>+              return error;
>+
>+      /* Set SD/HD mode registers */
>+
>+      if (sdf < TVP7002_STD_720P_60) {
>+              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x06);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x10);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x03);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x03);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0x17);
>+              if (error < 0)
>+                      return error;
>+
>+              if (sdf < TVP7002_STD_480P) {
>+                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>+                                                                      0x0c);
>+                      if (error < 0)
>+                              return error;
>+
>+                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>+                                                                      0x24);
>+                      if (error < 0)
>+                              return error;
>+
>+              } else {
>+                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>+                                                                      0x0a);
>+                      if (error < 0)
>+                              return error;
>+
>+                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>+                                                                      0x12);
>+                      if (error < 0)
>+                              return error;
>+              }
>+              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x08);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x18);
>+              if (error < 0)
>+                      return error;
>+      } else {
>+              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x32);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x20);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x01);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x00);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0xc7);
>+              if (error < 0)
>+                      return error;
>+
>+              if(sdf < TVP7002_STD_1080I_60) {
>+                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>+                                                                      0x35);
>+                      if (error < 0)
>+                              return error;
>+              } else {
>+                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>+                                                                      0x39);
>+                      if (error < 0)
>+                              return error;
>+              }
>+              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x00);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x5a);
>+              if (error < 0)
>+                      return error;
>+
>+              if(sdf < TVP7002_STD_1080P_60) {
>+                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>+                                                                      0x07);
>+                      if (error < 0)
>+                              return error;
>+              } else {
>+                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>+                                                                      0x03);
>+                      if (error < 0)
>+                              return error;
>+              }
>+      }
>+      if (sdf < TVP7002_STD_1080P_60) {
>+              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x50);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x0f);
>+              if (error < 0)
>+                      return error;
>+      } else {
>+              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x80);
>+              if (error < 0)
>+                      return error;
>+
>+              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x00);
>+              if (error < 0)
>+                      return error;
>+      }
>+      /* Set up registers that hold the same value regardless of the
>+       * SD mode
>+       */
>+      error = tvp7002_write(sd, TVP7002_SYNC_ON_G_THRS, 0x5d);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>+      if (error < 0)
>+              return error;
>+
>+      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_get_video_mode() - V4L2 decoder interface handler for querystd
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ *
>+ * Returns the current standard detected by TVP7002. If no active input is
>+ * detected, returns -1
>+ */
>+static v4l2_std_id tvp7002_get_video_mode(struct v4l2_subdev *sd)
>+{
>+      v4l2_std_id error;
>+      int reg01, reg02, reg03;
>+      reg01 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_MSBS);
>+      reg02 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_LSBS);
>+      reg03 = tvp7002_read(sd, TVP7002_HPLL_CRTL);
>+
>+      if (reg01 < 0 || reg02 < 0 || reg03 < 0) {
>+              error = V4L2_STD_UNKNOWN;
>+              goto found_error;
>+      }
>+
>+      switch(reg01) {
>+      case 0x35:
>+              if (reg02 == 0xa0)
>+                      error = V4L2_STD_525I_60;
>+              else
>+                      error = V4L2_STD_625I_50;
>+      case 0x36:
>+              if (reg02 == 0xa0)
>+                      error = V4L2_STD_525P_60;
>+              else
>+                      error = V4L2_STD_625P_50;
>+              break;
>+      case 0x67:
>+              error = V4L2_STD_720P_60;
>+              break;
>+      case 0x7b:
>+              error = V4L2_STD_720P_50;
>+              break;
>+      case 0x89:
>+              if (reg03 == 0x98)
>+                      error = V4L2_STD_1080I_60;
>+              else
>+                      error = V4L2_STD_1080P_60;
>+              break;
>+      case 0xa5:
>+              if (reg03 == 0x90)
>+                      error = V4L2_STD_1080I_50;
>+              else
>+                      error = V4L2_STD_1080P_50;
>+              break;
>+      default:
>+              error = V4L2_STD_UNKNOWN;
>+              break;
>+      }
>+
>+found_error:
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_querystd() - V4L2 decoder interface handler for querystd
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @std_id: standard V4L2 std_id ioctl enum
>+ *
>+ * Returns the current standard detected by TVP7002. If no active input is
>+ * detected, returns -EINVAL
>+ */
>+static int tvp7002_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
>+{
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+      v4l2_std_id current_std;
>+      u8 sync_lock_status;
>+      int res;
>+
>+      if (std_id == NULL)
>+              return -EINVAL;
>+
>+      /* get the current standard */
>+      res = tvp7002_get_video_mode(sd);
>+      if (res == V4L2_STD_UNKNOWN)
>+              return -EINVAL;
>+      current_std = res;
>+
>+      /* check whether signal is locked */
>+      sync_lock_status = tvp7002_read(sd, TVP7002_SYNC_DETECT_STAT);
>+
>+      if (0x02 != (sync_lock_status & 0xff))
>+              return -EINVAL; /* No input detected */
>+
>+      decoder->video_mode = current_std;
>+      *std_id = current_std;
>+
>+      v4l2_info(sd, "Current STD: %d %d @ %d Hz\n",
>+              tvp7002_resolutions[tvp7002_from_std(current_std)].hres,
>+              tvp7002_resolutions[tvp7002_from_std(current_std)].vres,
>+              tvp7002_resolutions[tvp7002_from_std(current_std)].frate);
>+      return 0;
>+}
>+
>+/*
>+ * tvp7002_scanmode() - Returns whether format is progressive
>+ * or interlaced
>+ */
>+
>+
>+/*
>+ * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
>+ *
>+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
>+ * ioctl is used to negotiate the image capture size and pixel format
>+ * without actually making it take effect.
>+ */
>+static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format
>*f)
>+{
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+      struct v4l2_pix_format *pix;
>+      v4l2_std_id current_std;
>+      int res;
>+
>+      if (f == NULL)
>+              return -EINVAL;
>+
>+      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>+              /* only capture is supported */
>+              f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>+
>+      pix = &f->fmt.pix;
>+
>+      /* Calculate height and width based on current standard */
>+      res = tvp7002_get_video_mode(sd);
>+      if (res < 0)
>+              return -EINVAL;
>+      current_std = res;
>+
>+      decoder->video_mode = current_std;
>+      pix->width = tvp7002_resolutions[tvp7002_from_std(current_std)].hres;
>+      pix->height =
>tvp7002_resolutions[tvp7002_from_std(current_std)].vres;
>+
>+      pix->pixelformat = V4L2_PIX_FMT_UYVY;
>+
>+      pix->field = V4L2_FIELD_INTERLACED;
>+      pix->bytesperline = pix->width * 2;
>+      pix->sizeimage = pix->bytesperline * pix->height;
>+      pix->colorspace = V4L2_COLORSPACE_REC709;
>+      pix->priv = 0;
>+
>+      v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
>+                      "Width - %d, Height - %d",
>+                      "8-bit UYVY 4:2:2 Format", pix->bytesperline,
>+                      pix->width, pix->height);
>+      return 0;
>+}
>+
>+/**
>+ * tvp7002_s_fmt_cap() - V4L2 decoder interface handler for s_fmt
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
>+ *
>+ * If the requested format is supported, configures the HW to use that
>+ * format, returns error code if format not supported or HW can't be
>+ * correctly configured.
>+ */
>+static int tvp7002_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format
>*f)
>+{
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+      struct v4l2_pix_format *pix;
>+      int rval;
>+
>+      if (f == NULL)
>+              return -EINVAL;
>+
>+      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>+              /* only capture is supported */
>+              return -EINVAL;
>+
>+      pix = &f->fmt.pix;
>+      rval = tvp7002_try_fmt_cap(sd, f);
>+      if (rval)
>+              return rval;
>+
>+              decoder->pix = *pix;
>+
>+      return rval;
>+}
>+
>+/**
>+ * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @f: pointer to standard V4L2 v4l2_format structure
>+ *
>+ * Returns the decoder's current pixel format in the v4l2_format
>+ * parameter.
>+ */
>+static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
>+{
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+
>+      if (f == NULL)
>+              return -EINVAL;
>+
>+      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>+              /* only capture is supported */
>+              return -EINVAL;
>+
>+      f->fmt.pix = decoder->pix;
>+
>+      v4l2_info(sd, "Current FMT: bytesperline - %d"
>+                      "Width - %d, Height - %d",
>+                      decoder->pix.bytesperline,
>+                      decoder->pix.width, decoder->pix.height);
>+      return 0;
>+}
>+
>+/*
>+ * tvp7002_s_ctrl() - Set a control
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to v4l2_control struct
>+ *
>+ * Set a control for a TVP7002 decoder device.
>+ * Returns zero when successful or -EINVAL if register access fails.
>+ */
>+static int tvp7002_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
>+{
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+      int vmd = 0;
>+
>+      decoder->video_mode = std;
>+      vmd = tvp7002_from_std(std);
>+
>+      v4l2_info(sd, "Set video std mode to %d.\n", (int)std);
>+
>+      return tvp7002_set_video_mode(sd, vmd);
>+}
>+
>+/*
>+ * tvp7002_g_ctrl() - Get a control
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to v4l2_control struct
>+ *
>+ * Get a control for a TVP7002 decoder device.
>+ * Returns zero when successful or -EINVAL if register access fails.
>+ */
>+static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>*ctrl)
>+{
>+      int rval, gval, bval;
>+      int res;
>+
>+      v4l2_info(sd, "g_ctrl called\n");
>+
>+      switch (ctrl->id) {
>+      case V4L2_CID_GAIN:
>+              rval = tvp7002_read(sd, TVP7002_R_FINE_GAIN);
>+              gval = tvp7002_read(sd, TVP7002_G_FINE_GAIN);
>+              bval = tvp7002_read(sd, TVP7002_B_FINE_GAIN);
>+
>+              if (rval < 0 || gval < 0 || bval < 0) {
>+                      res = -1;
>+              } else if (rval != gval || rval != bval) {
>+                      res = -1;
>+              } else {
>+                      ctrl->value = rval & 0x0F;
>+                      res = ctrl->value;
>+              }
>+              break;
>+      default:
>+              res = -1;
>+              break;
>+      }
>+
>+      return res < 0 ? res : 0;
>+}
>+
>+/*
>+ * tvp7002_s_ctrl() - Set a control
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to v4l2_control struct
>+ *
>+ * Set a control in TVP7002 decoder device.
>+ * Returns zero when successful or -EINVAL if register access fails.
>+ */
>+static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>*ctrl)
>+{
>+      int rval, gval, bval;
>+      int error;
>+      u8 i, n;
>+      n = ARRAY_SIZE(tvp7002_qctrl);
>+
>+      for (i = 0; i < n; i++)
>+              if (ctrl->id == tvp7002_qctrl[i].id)
>+                      break;
>+
>+      if (i == n)
>+              return -EINVAL;
>+
>+      if (ctrl->value < tvp7002_qctrl[i].minimum ||
>+                      ctrl->value > tvp7002_qctrl[i].maximum)
>+              return -ERANGE;
>+
>+      switch (ctrl->id) {
>+      case V4L2_CID_GAIN:
>+              rval = tvp7002_write(sd, TVP7002_R_FINE_GAIN,
>+                                                      ctrl->value & 0xff);
>+              gval = tvp7002_write(sd, TVP7002_G_FINE_GAIN,
>+                                                      ctrl->value & 0xff);
>+              bval = tvp7002_write(sd, TVP7002_B_FINE_GAIN,
>+                                                      ctrl->value & 0xff);
>+              if (rval < 0  || gval < 0 || bval < 0)
>+                      error = -1;
>+              else
>+                      error = rval;
>+              break;
>+      default:
>+              error = -1;
>+              break;
>+      }
>+
>+      if (error < 0)
>+              return -EINVAL;
>+      else
>+              return 0;
>+}
>+
>+/*
>+ * tvp7002_g_register() - Get the value of a register
>+ * @sd: ptr to v4l2_subdev struct
>+ * @vreg: ptr to v4l2_dbg_register struct
>+ *
>+ * Get the value of a TVP7002 decoder device register.
>+ * Returns zero when successful, -EINVAL if register read fails or
>+ * access to I2C client fails, -EPERM if the call is not allowed
>+ * by diabled CAP_SYS_ADMIN.
>+ */
>+#ifdef CONFIG_VIDEO_ADV_DEBUG
>+static int tvp7002_g_register(struct v4l2_subdev *sd,
>+                                              struct v4l2_dbg_register *reg)
>+{
>+      struct i2c_client *client = v4l2_get_subdevdata(sd);
>+      int error;
>+
>+      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>+              return -EINVAL;
>+      if (!capable(CAP_SYS_ADMIN))
>+              return -EPERM;
>+
>+      reg->val = tvp7002_read(sd, reg->reg & 0xff);
>+      reg->size = 1;
>+
>+      if (reg->val < 0)
>+              error = -EINVAL;
>+      else
>+              error = 0;
>+
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_s_register() - set a control
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to v4l2_control struct
>+ *
>+ * Get the value of a TVP7002 decoder device register.
>+ * Returns zero when successful or -EINVAL if register read fails.
>+ */
>+static int tvp7002_s_register(struct v4l2_subdev *sd,
>+                                              struct v4l2_dbg_register *reg)
>+{
>+      int error, wres;
>+      struct i2c_client *client = v4l2_get_subdevdata(sd);
>+
>+      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>+              return -EINVAL;
>+      if (!capable(CAP_SYS_ADMIN))
>+              return -EPERM;
>+
>+      wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
>+
>+      if (wres < 0)
>+              error = -EINVAL;
>+      else
>+              error = 0;
>+
>+      return error;
>+}
>+#endif
>+
>+/*
>+ * tvp7002_queryctrl() - Query a control
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to v4l2_queryctrl struct
>+ *
>+ * Query a control of a TVP7002 decoder device.
>+ * Returns zero when successful or -EINVAL if register read fails.
>+ */
>+static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl
>*qc)
>+{
>+      int i, error;
>+      error = -EINVAL;
>+
>+      v4l2_info(sd, "queryctrl called\n");
>+
>+      for (i = 0; i < ARRAY_SIZE(tvp7002_qctrl); i++)
>+              if (qc->id && qc->id == tvp7002_qctrl[i].id) {
>+                      memcpy(qc, &(tvp7002_qctrl[i]), sizeof(*qc));
>+                      error = 0;
>+                      break;
>+              }
>+
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
>+ * @sd: pointer to standard V4L2 sub-device structure
>+ * @enable: streaming enable or disable
>+ *
>+ * Sets streaming to enable or disable, if possible.
>+ */
>+static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
>+{
>+      int err = 0;
>+      struct tvp7002 *decoder = to_tvp7002(sd);
>+
>+      if (decoder->streaming == enable)
>+              return 0;
>+
>+      if (enable) {
>+              /* Power Up Sequence */
>+              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x00);
>+              if (err) {
>+                      v4l2_err(sd, "Unable to turn on decoder\n");
>+                      err = -EINVAL;
>+              }
>+              err = tvp7002_write_inittab(sd, tvp7002_init_default);
>+              if (err < 0) {
>+                      v4l2_err(sd, "Unable to initialize\n");
>+                      err = -EINVAL;
>+              }
>+              /* Detect if not already detected */
>+              err = tvp7002_read(sd, TVP7002_CHIP_REV);
>+              if (err < 0) {
>+                      v4l2_err(sd, "Unable to detect decoder\n");
>+                      err = -EINVAL;
>+              }
>+              decoder->streaming = enable;
>+      } else {
>+              /* Power Down Sequence */
>+              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x40);
>+              if (err) {
>+                      v4l2_err(sd, "Unable to turn off decoder\n");
>+                      return err;
>+              }
>+              decoder->streaming = enable;
>+      }
>+
>+      return err;
>+}
>+
>+/* Specific video subsystem operation handlers */
>+static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
>+      .querystd = tvp7002_querystd,
>+      .s_stream = tvp7002_s_stream,
>+      .g_fmt = tvp7002_g_fmt,
>+};
>+
>+/* V4L2 Operations handlers */
>+static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
>+      .g_chip_ident = tvp7002_g_chip_ident,
>+      .log_status = tvp7002_log_status,
>+      .g_ctrl = tvp7002_g_ctrl,
>+      .s_ctrl = tvp7002_s_ctrl,
>+      .queryctrl = tvp7002_queryctrl,
>+      .s_std = tvp7002_s_std,
>+#ifdef CONFIG_VIDEO_ADV_DEBUG
>+      .g_register = tvp7002_g_register,
>+      .s_register = tvp7002_s_register,
>+#endif
>+};
>+
>+static const struct v4l2_subdev_ops tvp7002_ops = {
>+      .core = &tvp7002_core_ops,
>+      .video = &tvp7002_video_ops,
>+};
>+
>+/*
>+ * tvp7002_reset - Reset a TVP7002 device
>+ * @sd: ptr to v4l2_subdev struct
>+ * @val: unsigned integer (not used)
>+ *
>+ * Reset the TVP7002 device
>+ * Returns zero when successful or -EINVAL if register read fails.
>+ */
>+static int tvp7002_reset(struct v4l2_subdev *sd, u32 val)
>+{
>+      int polarity;
>+      int error;
>+
>+      error = tvp7002_read(sd, TVP7002_CHIP_REV);
>+      if (error < 0) {
>+              error = -EINVAL;
>+              goto found_error;
>+      }
>+
>+      if (error == 0x02) {
>+              v4l2_info(sd, "rev. %02x detected.\n", error);
>+      } else {
>+              v4l2_info(sd, "unknown revision detected.\n");
>+              v4l2_info(sd, "revision number is %02x\n", error);
>+      }
>+
>+      /* Set polarity information */
>+      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
>+                      tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>+      if (error < 0) {
>+              error = -EINVAL;
>+              goto found_error;
>+      }
>+
>+      /* Initializes TVP7002 to its default values */
>+      error = tvp7002_write_inittab(sd, tvp7002_init_default);
>+      if (error < 0) {
>+              error = -EINVAL;
>+              goto found_error;
>+      }
>+
>+found_error:
>+      return error;
>+};
>+
>+/*
>+ * tvp7002_probe - Reset a TVP7002 device
>+ * @sd: ptr to v4l2_subdev struct
>+ * @ctrl: ptr to i2c_device_id struct
>+ *
>+ * Reset the TVP7002 device
>+ * Returns zero when successful or -EINVAL if register read fails.
>+ */
>+static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id
>*id)
>+{
>+      struct v4l2_subdev *sd;
>+      struct tvp7002 *core;
>+      int polarity;
>+      int error;
>+
>+      /* Check if the adapter supports the needed features */
>+      if (!i2c_check_functionality(c->adapter,
>+              I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
>+              return -EIO;
>+
>+      core = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
>+
>+      if (!core)
>+              return -ENOMEM;
>+
>+      sd = &core->sd;
>+      v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
>+      v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
>+                                      c->addr << 1, c->adapter->name);
>+
>+      /* Set polarity information */
>+      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
>+                      tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
>+      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>+      if (error < 0) {
>+              error = -EINVAL;
>+              goto found_error;
>+      }
>+
>+      /* Set video mode */
>+      core->video_mode = V4L2_STD_525P_60;
>+
>+      if (debug > 1)
>+              error = tvp7002_log_status(sd);
>+      else
>+              error = 0;
>+
>+found_error:
>+      if (error < 0)
>+              kfree(core);
>+
>+      return error;
>+}
>+
>+/*
>+ * tvp7002_remove - Remove TVP7002 device support
>+ * @c: ptr to i2c_client struct
>+ *
>+ * Reset the TVP7002 device
>+ * Returns zero when successful or -EINVAL if register read fails.
>+ */
>+static int tvp7002_remove(struct i2c_client *c)
>+{
>+      struct v4l2_subdev *sd = i2c_get_clientdata(c);
>+
>+      v4l2_dbg(1, debug, sd, "tvp7002.c: removing tvp7002 adapter"
>+                              "on address 0x%x\n", c->addr << 1);
>+
>+      v4l2_device_unregister_subdev(sd);
>+      kfree(to_tvp7002(sd));
>+      return 0;
>+}
>+
>+/* I2C Device ID table */
>+static const struct i2c_device_id tvp7002_id[] = {
>+      { "tvp7002", 0 },
>+      { }
>+};
>+MODULE_DEVICE_TABLE(i2c, tvp7002_id);
>+
>+/* I2C driver data */
>+static struct i2c_driver tvp7002_driver = {
>+      .driver = {
>+              .owner = THIS_MODULE,
>+              .name = "tvp7002",
>+      },
>+      .probe = tvp7002_probe,
>+      .remove = tvp7002_remove,
>+      .id_table = tvp7002_id,
>+};
>--
>1.6.0.4
>
Santiago Nunez-Corrales Sept. 2, 2009, 7:17 p.m. UTC | #3
Murali.

Karicheri, Muralidharan wrote:
> Santiago,
>
> you are missing a tvp7002_init() that calls i2c_add_driver(). Similarly i2c_del_driver(). See tvp514x.c or adv7353.c for examples... Also read the
>   

Answered in previous email to Sneha. Addressing.

> polarities from platform_data in probe(). I had mentioned the second comment during earlier review.
>
>   

Please check lines 1328 and 1378. The polarity information is set via 
register 17h with respect to platform_data. That part of the code will 
change according to a suggestion by Hans, but the code is there.

Regards,

> Murali Karicheri
> Software Design Engineer
> Texas Instruments Inc.
> Germantown, MD 20874
> new phone: 301-407-9583
> Old Phone : 301-515-3736 (will be deprecated)
> email: m-karicheri2@ti.com
>
>   
>> -----Original Message-----
>> From: santiago.nunez@ridgerun.com [mailto:santiago.nunez@ridgerun.com]
>> Sent: Tuesday, September 01, 2009 6:55 PM
>> To: Karicheri, Muralidharan
>> Cc: davinci-linux-open-source@linux.davincidsp.com;
>> todd.fischer@ridgerun.com; diego.dompe@ridgerun.com;
>> clark.becker@ridgerun.com; Narnakaje, Snehaprabha; Santiago Nunez-Corrales
>> Subject: [PATCH 5/6 v3] Updated TVP7002 driver for DM365
>>
>> From: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>>
>> This patch provides the implementation of the TVP7002 decoder
>> driver for DM365. Implemented revisions by Vaibhav Hiremath
>> and Hans Verkuil. Removed most of controls, cleared up logic,
>> cleaned up code.
>>
>> Signed-off-by: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>> ---
>> drivers/media/video/tvp7002.c | 1493
>> +++++++++++++++++++++++++++++++++++++++++
>> 1 files changed, 1493 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/media/video/tvp7002.c
>>
>> diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
>> new file mode 100644
>> index 0000000..e8cd77e
>> --- /dev/null
>> +++ b/drivers/media/video/tvp7002.c
>> @@ -0,0 +1,1493 @@
>> +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
>> + * Digitizer with Horizontal PLL registers
>> + *
>> + * Copyright (C) 2009 Texas Instruments Inc
>> + * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>> + *
>> + * This code is partially based upon the TVP5150 driver
>> + * written by Mauro Carvalho Chehab (mchehab@infradead.org)
>> + * and the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + */
>> +
>> +#include <linux/i2c.h>
>> +#include <linux/videodev2.h>
>> +#include <linux/delay.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/tvp7002.h>
>> +#include <media/v4l2-i2c-drv.h>
>> +#include <media/v4l2-chip-ident.h>
>> +#include <media/davinci/videohd.h>
>> +#include "tvp7002_reg.h"
>> +
>> +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
>> +MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
>> +MODULE_LICENSE("GPL");
>> +
>> +/* I2C retry attempts */
>> +#define I2C_RETRY_COUNT                 (5)
>> +
>> +/* Debugging information */
>> +
>> +static int debug;
>> +module_param(debug, int, 0);
>> +MODULE_PARM_DESC(debug, "Debug level (0-2)");
>> +
>> +/* Reference to tvp7002_platform_data */
>> +extern struct tvp7002_platform_data tvp7002_pdata;
>> +
>> +/* Struct for handling resolutions and associate register values */
>> +struct tvp7002_resol {
>> +      v4l2_std_id id;
>> +      int hres;
>> +      int vres;
>> +      int frate;
>> +      int lrate;
>> +      int prate;
>> +      u8 reg01;
>> +      u8 reg02;
>> +      u8 reg03;
>> +      u8 reg04;
>> +};
>> +
>> +/* Struct for handling register values */
>> +struct i2c_reg_value {
>> +      u8 reg;
>> +      u8 value;
>> +};
>> +
>> +/* Register default values (according to tvp7002 datasheet) */
>> +static const struct i2c_reg_value tvp7002_init_default[] = {
>> +      /* 0x00: read only */
>> +      { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67 },
>> +      { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20 },
>> +      { TVP7002_HPLL_CRTL, 0xa8 },
>> +      { TVP7002_HPLL_PHASE_SEL, 0x80 },
>> +      { TVP7002_CLAMP_START, 0x32 },
>> +      { TVP7002_CLAMP_W, 0x20 },
>> +      { TVP7002_HSYNC_OUT_W, 0x20 },
>> +      { TVP7002_B_FINE_GAIN, 0x00 },
>> +      { TVP7002_G_FINE_GAIN, 0x00 },
>> +      { TVP7002_R_FINE_GAIN, 0x00 },
>> +      { TVP7002_B_FINE_OFF_MSBS, 0x80 },
>> +      { TVP7002_G_FINE_OFF_MSBS, 0x80 },
>> +      { TVP7002_R_FINE_OFF_MSBS, 0x80 },
>> +      { TVP7002_SYNC_CTL_1, 0x5b },
>> +      { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e },
>> +      { TVP7002_SYNC_ON_G_THRS, 0x5d },
>> +      { TVP7002_SYNC_SEPARATOR_THRS, 0x20 },
>> +      { TVP7002_HPLL_PRE_COAST, 0x00 },
>> +      { TVP7002_HPLL_POST_COAST, 0x00 },
>> +      /* 0x14: read only */
>> +      { TVP7002_OUT_FORMATTER, 0x00 },
>> +      { TVP7002_MISC_CTL_1, 0x11 },
>> +      { TVP7002_MISC_CTL_2, 0x03 },
>> +      { TVP7002_MISC_CTL_3, 0x00 },
>> +      { TVP7002_IN_MUX_SEL_1, 0x00 },
>> +      { TVP7002_IN_MUX_SEL_2, 0xc2 },
>> +      { TVP7002_B_AND_G_COARSE_GAIN, 0x77 },
>> +      { TVP7002_R_COARSE_GAIN, 0x07 },
>> +      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>> +      { TVP7002_FINE_OFF_LSBS, 0x00 },
>> +      { TVP7002_B_COARSE_OFF, 0x10 },
>> +      { TVP7002_G_COARSE_OFF, 0x10 },
>> +      { TVP7002_R_COARSE_OFF, 0x10 },
>> +      { TVP7002_HSOUT_OUT_START, 0x0d },
>> +      { TVP7002_MISC_CTL_4, 0x0d },
>> +      /* 0x23: read only */
>> +      /* 0x24: read only */
>> +      /* 0x25: read only */
>> +      { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80 },
>> +      /* 0x27: read only */
>> +      { TVP7002_AUTO_LVL_CTL_FILTER, 0x53 },
>> +      { TVP7002_FINE_CLAMP_CTL, 0x07 },
>> +      { TVP7002_PWR_CTL, 0x00 },
>> +      { TVP7002_ADC_SETUP, 0x50 },
>> +      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>> +      { TVP7002_SOG_CLAMP, 0x80 },
>> +      { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c },
>> +      { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04 },
>> +      { TVP7002_ALC_PLACEMENT, 0x5a },
>> +      { TVP7002_MVIS_STRIPPER_W, 0x03 },
>> +      { TVP7002_VSYNC_ALGN, 0x10 },
>> +      { TVP7002_SYNC_BYPASS, 0x00 },
>> +      /* 0x37: read only */
>> +      /* 0x38: read only */
>> +      /* 0x39: read only */
>> +      /* 0x3a: read only */
>> +      /* 0x3b: read only */
>> +      /* 0x3c: read only */
>> +      { TVP7002_L_LENGTH_TOL, 0x03 },
>> +      { TVP7002_VIDEO_BWTH_CTL, 0x00 },
>> +      { TVP7002_AVID_START_PIXEL_LSBS, 0x01 },
>> +      { TVP7002_AVID_START_PIXEL_MSBS, 0x2c },
>> +      { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06 },
>> +      { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c },
>> +      { TVP7002_VBLK_F_0_START_L_OFF, 0x05 },
>> +      { TVP7002_VBLK_F_1_START_L_OFF, 0x05 },
>> +      { TVP7002_VBLK_F_0_DURATION, 0x1e },
>> +      { TVP7002_VBLK_F_1_DURATION, 0x1e },
>> +      { TVP7002_FBIT_F_0_START_L_OFF, 0x00 },
>> +      { TVP7002_FBIT_F_1_START_L_OFF, 0x00 },
>> +      { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3 },
>> +      { TVP7002_YUV_Y_G_COEF_MSBS, 0x16 },
>> +      { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f },
>> +      { TVP7002_YUV_Y_B_COEF_MSBS, 0x02 },
>> +      { TVP7002_YUV_Y_R_COEF_LSBS, 0xce },
>> +      { TVP7002_YUV_Y_R_COEF_MSBS, 0x06 },
>> +      { TVP7002_YUV_U_G_COEF_LSBS, 0xab },
>> +      { TVP7002_YUV_U_G_COEF_MSBS, 0xf3 },
>> +      { TVP7002_YUV_U_B_COEF_LSBS, 0x00 },
>> +      { TVP7002_YUV_U_B_COEF_MSBS, 0x10 },
>> +      { TVP7002_YUV_U_R_COEF_LSBS, 0x55 },
>> +      { TVP7002_YUV_U_R_COEF_MSBS, 0xfc },
>> +      { TVP7002_YUV_V_G_COEF_LSBS, 0x78 },
>> +      { TVP7002_YUV_V_G_COEF_MSBS, 0xf1 },
>> +      { TVP7002_YUV_V_B_COEF_LSBS, 0x88 },
>> +      { TVP7002_YUV_V_B_COEF_MSBS, 0xfe },
>> +      { TVP7002_YUV_V_R_COEF_LSBS, 0x00 },
>> +      { TVP7002_YUV_V_R_COEF_MSBS, 0x10 },
>> +      { 0x5c, 0x00 }  /* end of registers */
>> +};
>> +
>> +/* Available resolutions */
>> +static struct tvp7002_resol tvp7002_resolutions[] = {
>> +      {
>> +              .id = V4L2_STD_525I_60,
>> +              .hres = 720,
>> +              .vres = 480,
>> +              .frate = 30,
>> +              .lrate = 15,
>> +              .prate = 14,
>> +              .reg01 = 0x35,
>> +              .reg02 = 0xa0,
>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_625I_50,
>> +              .hres = 720,
>> +              .vres = 576,
>> +              .frate = 25,
>> +              .lrate = 16,
>> +              .prate = 14,
>> +              .reg01 = 0x36,
>> +              .reg02 = 0x00,
>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_525P_60,
>> +              .hres = 720,
>> +              .vres = 480,
>> +              .frate = 60,
>> +              .lrate = 31,
>> +              .prate = 27,
>> +              .reg01 = 0x35,
>> +              .reg02 = 0xa0,
>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_625P_50,
>> +              .hres = 720,
>> +              .vres = 576,
>> +              .frate = 50,
>> +              .lrate = 31,
>> +              .prate = 27,
>> +              .reg01 = 0x36,
>> +              .reg02 = 0x00,
>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_720P_60,
>> +              .hres = 1280,
>> +              .vres = 720,
>> +              .frate = 60,
>> +              .lrate = 45,
>> +              .prate = 74,
>> +              .reg01 = 0x67,
>> +              .reg02 = 0x20,
>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x20,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_720P_50,
>> +              .hres = 1280,
>> +              .vres = 720,
>> +              .frate = 50,
>> +              .lrate = 38,
>> +              .prate = 74,
>> +              .reg01 = 0x7b,
>> +              .reg02 = 0xc0,
>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_1080I_60,
>> +              .hres = 1920,
>> +              .vres = 1080,
>> +              .frate = 60,
>> +              .lrate = 34,
>> +              .prate = 74,
>> +              .reg01 = 0x89,
>> +              .reg02 = 0x80,
>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_1080I_50,
>> +              .hres = 1920,
>> +              .vres = 1080,
>> +              .frate = 50,
>> +              .lrate = 28,
>> +              .prate = 74,
>> +              .reg01 = 0xa5,
>> +              .reg02 = 0x00,
>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x10,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_1080P_60,
>> +              .hres = 1920,
>> +              .vres = 1080,
>> +              .frate = 60,
>> +              .lrate = 68,
>> +              .prate = 149,
>> +              .reg01 = 0x89,
>> +              .reg02 = 0x80,
>> +              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x20,
>> +              .reg04 = 0x80,
>> +      },
>> +      {
>> +              .id = V4L2_STD_1080P_50,
>> +              .hres = 1920,
>> +              .vres = 1080,
>> +              .frate = 50,
>> +              .lrate = 56,
>> +              .prate = 149,
>> +              .reg01 = 0xa5,
>> +              .reg02 = 0x00,
>> +              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x18,
>> +              .reg04 = 0x80,
>> +      },
>> +};
>> +
>> +/*
>> + * tvp7002_from_std - Map video standard to register information
>> + * @std: v4l2_std_id (u64) integer
>> + *
>> + * Returns index of std or -1 in case of error.
>> + */
>> +int tvp7002_from_std(v4l2_std_id std)
>> +{
>> +      int res;
>> +
>> +      switch (std) {
>> +      case V4L2_STD_525P_60:
>> +              res = TVP7002_STD_480P;
>> +              break;
>> +      case V4L2_STD_525I_60:
>> +              res = TVP7002_STD_480I;
>> +              break;
>> +      case V4L2_STD_625P_50:
>> +              res = TVP7002_STD_576P;
>> +              break;
>> +      case V4L2_STD_625I_50:
>> +              res = TVP7002_STD_576I;
>> +              break;
>> +      case V4L2_STD_720P_50:
>> +              res = TVP7002_STD_720P_50;
>> +              break;
>> +      case V4L2_STD_720P_60:
>> +              res = TVP7002_STD_720P_60;
>> +              break;
>> +      case V4L2_STD_1080I_50:
>> +              res = TVP7002_STD_1080I_50;
>> +              break;
>> +      case V4L2_STD_1080I_60:
>> +              res = TVP7002_STD_1080I_60;
>> +              break;
>> +      case V4L2_STD_1080P_50:
>> +              res = TVP7002_STD_1080P_50;
>> +              break;
>> +      case V4L2_STD_1080P_60:
>> +              res = TVP7002_STD_1080P_60;
>> +              break;
>> +      default:
>> +              res = -1;
>> +              break;
>> +      }
>> +
>> +      return res;
>> +}
>> +
>> +/* Device definition */
>> +struct tvp7002 {
>> +      struct v4l2_subdev sd;
>> +      v4l2_std_id video_mode;
>> +      struct v4l2_pix_format pix;
>> +      int streaming;
>> +};
>> +
>> +/* Supported controls */
>> +static struct v4l2_queryctrl tvp7002_qctrl[] = {
>> +      {
>> +              /* This gain control uses fine grain in TVP7002 */
>> +              .id = V4L2_CID_GAIN,
>> +              .type = V4L2_CTRL_TYPE_INTEGER,
>> +              .name = "Gain for RGB channels",
>> +              .minimum = 0,
>> +              .maximum = 255,
>> +              .step = 1,
>> +              .default_value = 0,
>> +              .flags = 0,
>> +      },
>> +};
>> +
>> +/*
>> + * to_tvp7002 - Obtain device handler TVP7002
>> + * @sd: ptr to v4l2_subdev struct
>> + *
>> + * Returns device handler tvp7002.
>> + */
>> +static struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
>> +{
>> +      return container_of(sd, struct tvp7002, sd);
>> +}
>> +
>> +/*
>> + * tvp7002_read - Read a value from a register in an TVP7002
>> + * @sd: ptr to v4l2_subdev struct
>> + * @reg: TVP7002 register address
>> + *
>> + * Returns value read if successful, or non-zero (-1) otherwise.
>> + */
>> +static int tvp7002_read(struct v4l2_subdev *sd, unsigned char addr)
>> +{
>> +      struct i2c_client *c = v4l2_get_subdevdata(sd);
>> +      int retry;
>> +      int error;
>> +
>> +      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>> +              error = i2c_smbus_read_byte_data(c, addr);
>> +
>> +              if (error >= 0)
>> +                      return error;
>> +              msleep_interruptible(10);
>> +      }
>> +      v4l2_err(sd, "TVP7002 read error %d\n", error);
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_write() - Write a value to a register in TVP7002
>> + * @sd: ptr to v4l2_subdev struct
>> + * @addr: TVP7002 register address
>> + * @value: value to be written to the register
>> + *
>> + * Write a value to a register in an TVP7002 decoder device.
>> + * Returns zero if successful, or non-zero otherwise.
>> + */
>> +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
>> +{
>> +      struct i2c_client *c = v4l2_get_subdevdata(sd);
>> +      int retry;
>> +      int error;
>> +
>> +      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>> +              error = i2c_smbus_write_byte_data(c, addr, value);
>> +
>> +              if (error >= 0)
>> +                      return error;
>> +              msleep_interruptible(10);
>> +      }
>> +      v4l2_err(sd, "TVP7002 write error %d\n", error);
>> +      return error;
>> +}
>> +
>> +/*
>> + * dump_reg_range() - Dump information from TVP7002 registers
>> + * @sd: ptr to v4l2_subdev struct
>> + * @init: TVP7002 start address
>> + * @end: TVP7002 end address
>> + *
>> + * Dump values at a specified register range
>> + * Returns nothing.
>> + */
>> +static void dump_reg_range(struct v4l2_subdev *sd, u8 init, const u8 end)
>> +{
>> +      int i = 0;
>> +      int result;
>> +
>> +      while (init != (u8)(end + 1)) {
>> +              result = tvp7002_read(sd, init);
>> +
>> +              if (result < 0)
>> +                      v4l2_err(sd, "tvp7002: reg 0x%02x unreadable\n", i);
>> +              else
>> +                      v4l2_info(sd, "tvp7002: @0x%02x = %02x\n", i, result);
>> +
>> +              init++;
>> +              i++;
>> +      }
>> +}
>> +
>> +/*
>> + * Macro for handling reading error conditions in tvp7002_log_status
>> + */
>> +#define TVP7002_LOG_CHK(reg, message, res) \
>> +      do {\
>> +              (res) = tvp7002_read(sd, (reg));\
>> +              \
>> +              if ((res) >= 0)\
>> +                      v4l2_info(sd, "%s = 0x%02x\n", (message), (res));\
>> +      } while (0)
>> +/*
>> + * tvp7002_log_status() - Print information about register settings
>> + * @sd: ptr to v4l2_subdev struct
>> + *
>> + * Log register values of a TVP7002 decoder device.
>> + * Returns zero or -EINVAL if read operation fails.
>> + */
>> +static int tvp7002_log_status(struct v4l2_subdev *sd)
>> +{
>> +      int rres;
>> +
>> +      TVP7002_LOG_CHK(TVP7002_CHIP_REV, "Chip revision number", rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_LSBS, "H-PLL feedback div LSB",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "H-PLL feedback div MSB",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "VCO freq range selector",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_PHASE_SEL, "ADC sampling clk phase sel",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_CLAMP_START, "Clamp start", rres);
>> +      TVP7002_LOG_CHK(TVP7002_CLAMP_W, "Clamp width", rres);
>> +      TVP7002_LOG_CHK(TVP7002_HSYNC_OUT_W, "HSYNC output width", rres);
>> +      TVP7002_LOG_CHK(TVP7002_B_FINE_GAIN, "Digital fine grain B ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_G_FINE_GAIN, "Digital fine grain G ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_R_FINE_GAIN, "Digital fine grain R ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_B_FINE_OFF_MSBS, "Digital fine grain off B
>> ch",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_G_FINE_OFF_MSBS, "Digital fine grain off G
>> ch",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_R_FINE_OFF_MSBS, "Digital fine grain off R
>> ch",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_FINE_OFF_LSBS, "Dig fine grain off LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_SYNC_CTL_1, "Sync control 1", rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_AND_CLAMP_CTL, "H-PLL and clamp control",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_SYNC_ON_G_THRS, "Sync-On-Green threshold",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_SYNC_SEPARATOR_THRS, "Sync separator
>> thrshold",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_PRE_COAST, "H-PLL pre-coast", rres);
>> +      TVP7002_LOG_CHK(TVP7002_HPLL_POST_COAST, "H-PLL post-coast", rres);
>> +      TVP7002_LOG_CHK(TVP7002_SYNC_DETECT_STAT, "Sync detect status",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_OUT_FORMATTER, "Output formatter", rres);
>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_1, "Miscelaneous control 1", rres);
>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_2, "Miscelaneous control 2", rres);
>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_3, "Miscelaneous control 3", rres);
>> +      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_1, "Input Mux Selector 1", rres);
>> +      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_2, "Input Mux Selector 2", rres);
>> +      TVP7002_LOG_CHK(TVP7002_B_AND_G_COARSE_GAIN, "B and G coarse gain",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_R_COARSE_GAIN, "R coarse gain", rres);
>> +      TVP7002_LOG_CHK(TVP7002_B_COARSE_OFF, "Coarse offset for B ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_G_COARSE_OFF, "Coarse offset for G ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_R_COARSE_OFF, "Coarse offset for R ch",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_HSOUT_OUT_START, "HSYNC lead edge out start",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_4, "Miscelaneous control 4", rres);
>> +      TVP7002_LOG_CHK(TVP7002_B_DGTL_ALC_OUT_LSBS, "Flt ALC out B ch LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_G_DGTL_ALC_OUT_LSBS, "Flt ALC out G ch LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_R_DGTL_ALC_OUT_LSBS, "Flt ALC out R ch LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_ENABLE, "Auto level ctrl enable",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_DGTL_ALC_OUT_MSBS, "Filt ALC out RGB chs MSB",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_FILTER, "Auto level ctrl filter",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_FINE_CLAMP_CTL, "Fine clamp control", rres);
>> +      TVP7002_LOG_CHK(TVP7002_PWR_CTL, "Power control", rres);
>> +      TVP7002_LOG_CHK(TVP7002_ADC_SETUP, "ADC setup", rres);
>> +      TVP7002_LOG_CHK(TVP7002_COARSE_CLAMP_CTL, "Coarse clamp ctrl", rres);
>> +      TVP7002_LOG_CHK(TVP7002_SOG_CLAMP, "Sync-On-Green clamp", rres);
>> +      TVP7002_LOG_CHK(TVP7002_RGB_COARSE_CLAMP_CTL, "RGB coarse clamp ctrl",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_SOG_COARSE_CLAMP_CTL, "SOG coarse clamp ctrl",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_ALC_PLACEMENT, "ALC placement", rres);
>> +      TVP7002_LOG_CHK(TVP7002_MVIS_STRIPPER_W, "Macrovision stripper width",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_SYNC_BYPASS, "Sync bypass", rres);
>> +      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_LSBS, "Lines p Frame status
>> LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_MSBS, "Lines p Frame status
>> MSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_LSBS, "Clks p line status LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_MSBS, "Clks p line status MSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_HSYNC_W, "HSYNC width", rres);
>> +      TVP7002_LOG_CHK(TVP7002_VSYNC_W, "VSYNC width", rres);
>> +      TVP7002_LOG_CHK(TVP7002_L_LENGTH_TOL, "Line length tolerance", rres);
>> +      TVP7002_LOG_CHK(TVP7002_VIDEO_BWTH_CTL, "Video bandwth control",
>> rres);
>> +      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_LSBS, "AVID start pixel
>> LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_MSBS, "AVID start pixel
>> MSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_LSBS, "AVID stop pixel LSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_MSBS, "AVID stop pixel MSBs",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_START_L_OFF, "VBLK start line off 0",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_START_L_OFF, "VBLK start line off 1",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_DURATION, "VBLK duration 0", rres);
>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_DURATION, "VBLK duration 1", rres);
>> +      TVP7002_LOG_CHK(TVP7002_FBIT_F_0_START_L_OFF, "FBIT start line off 0",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_FBIT_F_1_START_L_OFF, "FBIT start line off 1",
>> +                                                                      rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_LSBS, "YUV Y G LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_MSBS, "YUV Y G MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_LSBS, "YUV Y B LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_MSBS, "YUV Y B MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_LSBS, "YUV Y R LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_MSBS, "YUV Y R MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_LSBS, "YUV U G LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_MSBS, "YUV U G MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_LSBS, "YUV U B LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_MSBS, "YUV U B MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_LSBS, "YUV U R LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_MSBS, "YUV U R MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_LSBS, "YUV V G LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_MSBS, "YUV V G MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_LSBS, "YUV V B LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_MSBS, "YUV V B MSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_LSBS, "YUV V R LSBs", rres);
>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_MSBS, "YUV V R MSBs", rres);
>> +
>> +      return rres;
>> +}
>> +
>> +/*
>> + * tvp7002_g_chip_ident() - Get chip identification number
>> + * @sd: ptr to v4l2_subdev struct
>> + * @chip: ptr to v4l2_dbg_chip_ident struct
>> + *
>> + * Obtains the chip's identification number.
>> + * Returns zero or -EINVAL if read operation fails.
>> + */
>> +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
>> +                                      struct v4l2_dbg_chip_ident *chip)
>> +{
>> +      int rev;
>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +
>> +      rev = tvp7002_read(sd, TVP7002_CHIP_REV);
>> +
>> +      if (rev < 0)
>> +              return -EINVAL;
>> +
>> +      return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002,
>> +                                                                      rev);
>> +}
>> +
>> +/*
>> + * tvp7002_write_inittab() - Write initialization values
>> + * @sd: ptr to v4l2_subdev struct
>> + * @regs: ptr to i2c_reg_value struct
>> + *
>> + * Write initialization values.
>> + * Returns zero or -EINVAL if read operation fails.
>> + */
>> +static int tvp7002_write_inittab(struct v4l2_subdev *sd,
>> +                                      const struct i2c_reg_value *regs)
>> +{
>> +      int i;
>> +      int error;
>> +      /* Initialize the first (defined) registers */
>> +      while (regs->reg != 0x5c) {
>> +              error = tvp7002_write(sd, regs->reg, regs->value);
>> +              if (error < 0)
>> +                      return -EINVAL;
>> +              regs++;
>> +      }
>> +      /* Initialize the last unnamed registers */
>> +      for (i = 0x5c; i <= 0xff; i++) {
>> +              error = tvp7002_write(sd, i, 0x00);
>> +              if (error < 0)
>> +                      return -EINVAL;
>> +              regs++;
>> +      }
>> +
>> +      return 0;
>> +}
>> +
>> +/*
>> + * tvp7002_set_video_mode()
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @sdf: index to structure describing format
>> + *
>> + * Set video standard according to index
>> + *
>> + * Returns 0 if operation is successful or -EINVAL otherwise
>> + */
>> +static int tvp7002_set_video_mode(struct v4l2_subdev *sd, int sdf)
>> +{
>> +      int error;
>> +
>> +      if (sdf < TVP7002_STD_480I || sdf > TVP7002_STD_1080P_50) {
>> +              v4l2_err(sd, "sf out of range\n");
>> +              return -ERANGE;
>> +      }
>> +
>> +      /* Print specific information about current format */
>> +      v4l2_info(sd, "Setting standard display format...\n");
>> +      v4l2_info(sd, "hres = %d vres=%d fr=%d lr=%d prate=%d\n",
>> +                      tvp7002_resolutions[sdf].hres,
>> +                      tvp7002_resolutions[sdf].vres,
>> +                      tvp7002_resolutions[sdf].frate,
>> +                      tvp7002_resolutions[sdf].lrate,
>> +                      tvp7002_resolutions[sdf].prate);
>> +      /* Set registers accordingly */
>> +      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_MSBS,
>> +                                      tvp7002_resolutions[sdf].reg01);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_LSBS,
>> +                                      tvp7002_resolutions[sdf].reg02);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_HPLL_CRTL,
>> +                                      tvp7002_resolutions[sdf].reg03);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error =  tvp7002_write(sd, TVP7002_HPLL_PHASE_SEL,
>> +                                      tvp7002_resolutions[sdf].reg04);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      /* Set SD/HD mode registers */
>> +
>> +      if (sdf < TVP7002_STD_720P_60) {
>> +              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x06);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x10);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x03);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x03);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0x17);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              if (sdf < TVP7002_STD_480P) {
>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>> +                                                                      0x0c);
>> +                      if (error < 0)
>> +                              return error;
>> +
>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>> +                                                                      0x24);
>> +                      if (error < 0)
>> +                              return error;
>> +
>> +              } else {
>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>> +                                                                      0x0a);
>> +                      if (error < 0)
>> +                              return error;
>> +
>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>> +                                                                      0x12);
>> +                      if (error < 0)
>> +                              return error;
>> +              }
>> +              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x08);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x18);
>> +              if (error < 0)
>> +                      return error;
>> +      } else {
>> +              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x32);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x20);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x01);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x00);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0xc7);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              if(sdf < TVP7002_STD_1080I_60) {
>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>> +                                                                      0x35);
>> +                      if (error < 0)
>> +                              return error;
>> +              } else {
>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>> +                                                                      0x39);
>> +                      if (error < 0)
>> +                              return error;
>> +              }
>> +              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x00);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x5a);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              if(sdf < TVP7002_STD_1080P_60) {
>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>> +                                                                      0x07);
>> +                      if (error < 0)
>> +                              return error;
>> +              } else {
>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>> +                                                                      0x03);
>> +                      if (error < 0)
>> +                              return error;
>> +              }
>> +      }
>> +      if (sdf < TVP7002_STD_1080P_60) {
>> +              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x50);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x0f);
>> +              if (error < 0)
>> +                      return error;
>> +      } else {
>> +              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x80);
>> +              if (error < 0)
>> +                      return error;
>> +
>> +              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x00);
>> +              if (error < 0)
>> +                      return error;
>> +      }
>> +      /* Set up registers that hold the same value regardless of the
>> +       * SD mode
>> +       */
>> +      error = tvp7002_write(sd, TVP7002_SYNC_ON_G_THRS, 0x5d);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>> +      if (error < 0)
>> +              return error;
>> +
>> +      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_get_video_mode() - V4L2 decoder interface handler for querystd
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + *
>> + * Returns the current standard detected by TVP7002. If no active input is
>> + * detected, returns -1
>> + */
>> +static v4l2_std_id tvp7002_get_video_mode(struct v4l2_subdev *sd)
>> +{
>> +      v4l2_std_id error;
>> +      int reg01, reg02, reg03;
>> +      reg01 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_MSBS);
>> +      reg02 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_LSBS);
>> +      reg03 = tvp7002_read(sd, TVP7002_HPLL_CRTL);
>> +
>> +      if (reg01 < 0 || reg02 < 0 || reg03 < 0) {
>> +              error = V4L2_STD_UNKNOWN;
>> +              goto found_error;
>> +      }
>> +
>> +      switch(reg01) {
>> +      case 0x35:
>> +              if (reg02 == 0xa0)
>> +                      error = V4L2_STD_525I_60;
>> +              else
>> +                      error = V4L2_STD_625I_50;
>> +      case 0x36:
>> +              if (reg02 == 0xa0)
>> +                      error = V4L2_STD_525P_60;
>> +              else
>> +                      error = V4L2_STD_625P_50;
>> +              break;
>> +      case 0x67:
>> +              error = V4L2_STD_720P_60;
>> +              break;
>> +      case 0x7b:
>> +              error = V4L2_STD_720P_50;
>> +              break;
>> +      case 0x89:
>> +              if (reg03 == 0x98)
>> +                      error = V4L2_STD_1080I_60;
>> +              else
>> +                      error = V4L2_STD_1080P_60;
>> +              break;
>> +      case 0xa5:
>> +              if (reg03 == 0x90)
>> +                      error = V4L2_STD_1080I_50;
>> +              else
>> +                      error = V4L2_STD_1080P_50;
>> +              break;
>> +      default:
>> +              error = V4L2_STD_UNKNOWN;
>> +              break;
>> +      }
>> +
>> +found_error:
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_querystd() - V4L2 decoder interface handler for querystd
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @std_id: standard V4L2 std_id ioctl enum
>> + *
>> + * Returns the current standard detected by TVP7002. If no active input is
>> + * detected, returns -EINVAL
>> + */
>> +static int tvp7002_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
>> +{
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +      v4l2_std_id current_std;
>> +      u8 sync_lock_status;
>> +      int res;
>> +
>> +      if (std_id == NULL)
>> +              return -EINVAL;
>> +
>> +      /* get the current standard */
>> +      res = tvp7002_get_video_mode(sd);
>> +      if (res == V4L2_STD_UNKNOWN)
>> +              return -EINVAL;
>> +      current_std = res;
>> +
>> +      /* check whether signal is locked */
>> +      sync_lock_status = tvp7002_read(sd, TVP7002_SYNC_DETECT_STAT);
>> +
>> +      if (0x02 != (sync_lock_status & 0xff))
>> +              return -EINVAL; /* No input detected */
>> +
>> +      decoder->video_mode = current_std;
>> +      *std_id = current_std;
>> +
>> +      v4l2_info(sd, "Current STD: %d %d @ %d Hz\n",
>> +              tvp7002_resolutions[tvp7002_from_std(current_std)].hres,
>> +              tvp7002_resolutions[tvp7002_from_std(current_std)].vres,
>> +              tvp7002_resolutions[tvp7002_from_std(current_std)].frate);
>> +      return 0;
>> +}
>> +
>> +/*
>> + * tvp7002_scanmode() - Returns whether format is progressive
>> + * or interlaced
>> + */
>> +
>> +
>> +/*
>> + * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
>> + *
>> + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
>> + * ioctl is used to negotiate the image capture size and pixel format
>> + * without actually making it take effect.
>> + */
>> +static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format
>> *f)
>> +{
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +      struct v4l2_pix_format *pix;
>> +      v4l2_std_id current_std;
>> +      int res;
>> +
>> +      if (f == NULL)
>> +              return -EINVAL;
>> +
>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +              /* only capture is supported */
>> +              f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +
>> +      pix = &f->fmt.pix;
>> +
>> +      /* Calculate height and width based on current standard */
>> +      res = tvp7002_get_video_mode(sd);
>> +      if (res < 0)
>> +              return -EINVAL;
>> +      current_std = res;
>> +
>> +      decoder->video_mode = current_std;
>> +      pix->width = tvp7002_resolutions[tvp7002_from_std(current_std)].hres;
>> +      pix->height =
>> tvp7002_resolutions[tvp7002_from_std(current_std)].vres;
>> +
>> +      pix->pixelformat = V4L2_PIX_FMT_UYVY;
>> +
>> +      pix->field = V4L2_FIELD_INTERLACED;
>> +      pix->bytesperline = pix->width * 2;
>> +      pix->sizeimage = pix->bytesperline * pix->height;
>> +      pix->colorspace = V4L2_COLORSPACE_REC709;
>> +      pix->priv = 0;
>> +
>> +      v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
>> +                      "Width - %d, Height - %d",
>> +                      "8-bit UYVY 4:2:2 Format", pix->bytesperline,
>> +                      pix->width, pix->height);
>> +      return 0;
>> +}
>> +
>> +/**
>> + * tvp7002_s_fmt_cap() - V4L2 decoder interface handler for s_fmt
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
>> + *
>> + * If the requested format is supported, configures the HW to use that
>> + * format, returns error code if format not supported or HW can't be
>> + * correctly configured.
>> + */
>> +static int tvp7002_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format
>> *f)
>> +{
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +      struct v4l2_pix_format *pix;
>> +      int rval;
>> +
>> +      if (f == NULL)
>> +              return -EINVAL;
>> +
>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +              /* only capture is supported */
>> +              return -EINVAL;
>> +
>> +      pix = &f->fmt.pix;
>> +      rval = tvp7002_try_fmt_cap(sd, f);
>> +      if (rval)
>> +              return rval;
>> +
>> +              decoder->pix = *pix;
>> +
>> +      return rval;
>> +}
>> +
>> +/**
>> + * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @f: pointer to standard V4L2 v4l2_format structure
>> + *
>> + * Returns the decoder's current pixel format in the v4l2_format
>> + * parameter.
>> + */
>> +static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
>> +{
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +
>> +      if (f == NULL)
>> +              return -EINVAL;
>> +
>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +              /* only capture is supported */
>> +              return -EINVAL;
>> +
>> +      f->fmt.pix = decoder->pix;
>> +
>> +      v4l2_info(sd, "Current FMT: bytesperline - %d"
>> +                      "Width - %d, Height - %d",
>> +                      decoder->pix.bytesperline,
>> +                      decoder->pix.width, decoder->pix.height);
>> +      return 0;
>> +}
>> +
>> +/*
>> + * tvp7002_s_ctrl() - Set a control
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to v4l2_control struct
>> + *
>> + * Set a control for a TVP7002 decoder device.
>> + * Returns zero when successful or -EINVAL if register access fails.
>> + */
>> +static int tvp7002_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
>> +{
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +      int vmd = 0;
>> +
>> +      decoder->video_mode = std;
>> +      vmd = tvp7002_from_std(std);
>> +
>> +      v4l2_info(sd, "Set video std mode to %d.\n", (int)std);
>> +
>> +      return tvp7002_set_video_mode(sd, vmd);
>> +}
>> +
>> +/*
>> + * tvp7002_g_ctrl() - Get a control
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to v4l2_control struct
>> + *
>> + * Get a control for a TVP7002 decoder device.
>> + * Returns zero when successful or -EINVAL if register access fails.
>> + */
>> +static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>> *ctrl)
>> +{
>> +      int rval, gval, bval;
>> +      int res;
>> +
>> +      v4l2_info(sd, "g_ctrl called\n");
>> +
>> +      switch (ctrl->id) {
>> +      case V4L2_CID_GAIN:
>> +              rval = tvp7002_read(sd, TVP7002_R_FINE_GAIN);
>> +              gval = tvp7002_read(sd, TVP7002_G_FINE_GAIN);
>> +              bval = tvp7002_read(sd, TVP7002_B_FINE_GAIN);
>> +
>> +              if (rval < 0 || gval < 0 || bval < 0) {
>> +                      res = -1;
>> +              } else if (rval != gval || rval != bval) {
>> +                      res = -1;
>> +              } else {
>> +                      ctrl->value = rval & 0x0F;
>> +                      res = ctrl->value;
>> +              }
>> +              break;
>> +      default:
>> +              res = -1;
>> +              break;
>> +      }
>> +
>> +      return res < 0 ? res : 0;
>> +}
>> +
>> +/*
>> + * tvp7002_s_ctrl() - Set a control
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to v4l2_control struct
>> + *
>> + * Set a control in TVP7002 decoder device.
>> + * Returns zero when successful or -EINVAL if register access fails.
>> + */
>> +static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>> *ctrl)
>> +{
>> +      int rval, gval, bval;
>> +      int error;
>> +      u8 i, n;
>> +      n = ARRAY_SIZE(tvp7002_qctrl);
>> +
>> +      for (i = 0; i < n; i++)
>> +              if (ctrl->id == tvp7002_qctrl[i].id)
>> +                      break;
>> +
>> +      if (i == n)
>> +              return -EINVAL;
>> +
>> +      if (ctrl->value < tvp7002_qctrl[i].minimum ||
>> +                      ctrl->value > tvp7002_qctrl[i].maximum)
>> +              return -ERANGE;
>> +
>> +      switch (ctrl->id) {
>> +      case V4L2_CID_GAIN:
>> +              rval = tvp7002_write(sd, TVP7002_R_FINE_GAIN,
>> +                                                      ctrl->value & 0xff);
>> +              gval = tvp7002_write(sd, TVP7002_G_FINE_GAIN,
>> +                                                      ctrl->value & 0xff);
>> +              bval = tvp7002_write(sd, TVP7002_B_FINE_GAIN,
>> +                                                      ctrl->value & 0xff);
>> +              if (rval < 0  || gval < 0 || bval < 0)
>> +                      error = -1;
>> +              else
>> +                      error = rval;
>> +              break;
>> +      default:
>> +              error = -1;
>> +              break;
>> +      }
>> +
>> +      if (error < 0)
>> +              return -EINVAL;
>> +      else
>> +              return 0;
>> +}
>> +
>> +/*
>> + * tvp7002_g_register() - Get the value of a register
>> + * @sd: ptr to v4l2_subdev struct
>> + * @vreg: ptr to v4l2_dbg_register struct
>> + *
>> + * Get the value of a TVP7002 decoder device register.
>> + * Returns zero when successful, -EINVAL if register read fails or
>> + * access to I2C client fails, -EPERM if the call is not allowed
>> + * by diabled CAP_SYS_ADMIN.
>> + */
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> +static int tvp7002_g_register(struct v4l2_subdev *sd,
>> +                                              struct v4l2_dbg_register *reg)
>> +{
>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +      int error;
>> +
>> +      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>> +              return -EINVAL;
>> +      if (!capable(CAP_SYS_ADMIN))
>> +              return -EPERM;
>> +
>> +      reg->val = tvp7002_read(sd, reg->reg & 0xff);
>> +      reg->size = 1;
>> +
>> +      if (reg->val < 0)
>> +              error = -EINVAL;
>> +      else
>> +              error = 0;
>> +
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_s_register() - set a control
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to v4l2_control struct
>> + *
>> + * Get the value of a TVP7002 decoder device register.
>> + * Returns zero when successful or -EINVAL if register read fails.
>> + */
>> +static int tvp7002_s_register(struct v4l2_subdev *sd,
>> +                                              struct v4l2_dbg_register *reg)
>> +{
>> +      int error, wres;
>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +
>> +      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>> +              return -EINVAL;
>> +      if (!capable(CAP_SYS_ADMIN))
>> +              return -EPERM;
>> +
>> +      wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
>> +
>> +      if (wres < 0)
>> +              error = -EINVAL;
>> +      else
>> +              error = 0;
>> +
>> +      return error;
>> +}
>> +#endif
>> +
>> +/*
>> + * tvp7002_queryctrl() - Query a control
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to v4l2_queryctrl struct
>> + *
>> + * Query a control of a TVP7002 decoder device.
>> + * Returns zero when successful or -EINVAL if register read fails.
>> + */
>> +static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl
>> *qc)
>> +{
>> +      int i, error;
>> +      error = -EINVAL;
>> +
>> +      v4l2_info(sd, "queryctrl called\n");
>> +
>> +      for (i = 0; i < ARRAY_SIZE(tvp7002_qctrl); i++)
>> +              if (qc->id && qc->id == tvp7002_qctrl[i].id) {
>> +                      memcpy(qc, &(tvp7002_qctrl[i]), sizeof(*qc));
>> +                      error = 0;
>> +                      break;
>> +              }
>> +
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
>> + * @sd: pointer to standard V4L2 sub-device structure
>> + * @enable: streaming enable or disable
>> + *
>> + * Sets streaming to enable or disable, if possible.
>> + */
>> +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +      int err = 0;
>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>> +
>> +      if (decoder->streaming == enable)
>> +              return 0;
>> +
>> +      if (enable) {
>> +              /* Power Up Sequence */
>> +              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x00);
>> +              if (err) {
>> +                      v4l2_err(sd, "Unable to turn on decoder\n");
>> +                      err = -EINVAL;
>> +              }
>> +              err = tvp7002_write_inittab(sd, tvp7002_init_default);
>> +              if (err < 0) {
>> +                      v4l2_err(sd, "Unable to initialize\n");
>> +                      err = -EINVAL;
>> +              }
>> +              /* Detect if not already detected */
>> +              err = tvp7002_read(sd, TVP7002_CHIP_REV);
>> +              if (err < 0) {
>> +                      v4l2_err(sd, "Unable to detect decoder\n");
>> +                      err = -EINVAL;
>> +              }
>> +              decoder->streaming = enable;
>> +      } else {
>> +              /* Power Down Sequence */
>> +              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x40);
>> +              if (err) {
>> +                      v4l2_err(sd, "Unable to turn off decoder\n");
>> +                      return err;
>> +              }
>> +              decoder->streaming = enable;
>> +      }
>> +
>> +      return err;
>> +}
>> +
>> +/* Specific video subsystem operation handlers */
>> +static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
>> +      .querystd = tvp7002_querystd,
>> +      .s_stream = tvp7002_s_stream,
>> +      .g_fmt = tvp7002_g_fmt,
>> +};
>> +
>> +/* V4L2 Operations handlers */
>> +static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
>> +      .g_chip_ident = tvp7002_g_chip_ident,
>> +      .log_status = tvp7002_log_status,
>> +      .g_ctrl = tvp7002_g_ctrl,
>> +      .s_ctrl = tvp7002_s_ctrl,
>> +      .queryctrl = tvp7002_queryctrl,
>> +      .s_std = tvp7002_s_std,
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> +      .g_register = tvp7002_g_register,
>> +      .s_register = tvp7002_s_register,
>> +#endif
>> +};
>> +
>> +static const struct v4l2_subdev_ops tvp7002_ops = {
>> +      .core = &tvp7002_core_ops,
>> +      .video = &tvp7002_video_ops,
>> +};
>> +
>> +/*
>> + * tvp7002_reset - Reset a TVP7002 device
>> + * @sd: ptr to v4l2_subdev struct
>> + * @val: unsigned integer (not used)
>> + *
>> + * Reset the TVP7002 device
>> + * Returns zero when successful or -EINVAL if register read fails.
>> + */
>> +static int tvp7002_reset(struct v4l2_subdev *sd, u32 val)
>> +{
>> +      int polarity;
>> +      int error;
>> +
>> +      error = tvp7002_read(sd, TVP7002_CHIP_REV);
>> +      if (error < 0) {
>> +              error = -EINVAL;
>> +              goto found_error;
>> +      }
>> +
>> +      if (error == 0x02) {
>> +              v4l2_info(sd, "rev. %02x detected.\n", error);
>> +      } else {
>> +              v4l2_info(sd, "unknown revision detected.\n");
>> +              v4l2_info(sd, "revision number is %02x\n", error);
>> +      }
>> +
>> +      /* Set polarity information */
>> +      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
>> +                      tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>> +      if (error < 0) {
>> +              error = -EINVAL;
>> +              goto found_error;
>> +      }
>> +
>> +      /* Initializes TVP7002 to its default values */
>> +      error = tvp7002_write_inittab(sd, tvp7002_init_default);
>> +      if (error < 0) {
>> +              error = -EINVAL;
>> +              goto found_error;
>> +      }
>> +
>> +found_error:
>> +      return error;
>> +};
>> +
>> +/*
>> + * tvp7002_probe - Reset a TVP7002 device
>> + * @sd: ptr to v4l2_subdev struct
>> + * @ctrl: ptr to i2c_device_id struct
>> + *
>> + * Reset the TVP7002 device
>> + * Returns zero when successful or -EINVAL if register read fails.
>> + */
>> +static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id
>> *id)
>> +{
>> +      struct v4l2_subdev *sd;
>> +      struct tvp7002 *core;
>> +      int polarity;
>> +      int error;
>> +
>> +      /* Check if the adapter supports the needed features */
>> +      if (!i2c_check_functionality(c->adapter,
>> +              I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
>> +              return -EIO;
>> +
>> +      core = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
>> +
>> +      if (!core)
>> +              return -ENOMEM;
>> +
>> +      sd = &core->sd;
>> +      v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
>> +      v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
>> +                                      c->addr << 1, c->adapter->name);
>> +
>> +      /* Set polarity information */
>> +      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
>> +                      tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>> +      if (error < 0) {
>> +              error = -EINVAL;
>> +              goto found_error;
>> +      }
>> +
>> +      /* Set video mode */
>> +      core->video_mode = V4L2_STD_525P_60;
>> +
>> +      if (debug > 1)
>> +              error = tvp7002_log_status(sd);
>> +      else
>> +              error = 0;
>> +
>> +found_error:
>> +      if (error < 0)
>> +              kfree(core);
>> +
>> +      return error;
>> +}
>> +
>> +/*
>> + * tvp7002_remove - Remove TVP7002 device support
>> + * @c: ptr to i2c_client struct
>> + *
>> + * Reset the TVP7002 device
>> + * Returns zero when successful or -EINVAL if register read fails.
>> + */
>> +static int tvp7002_remove(struct i2c_client *c)
>> +{
>> +      struct v4l2_subdev *sd = i2c_get_clientdata(c);
>> +
>> +      v4l2_dbg(1, debug, sd, "tvp7002.c: removing tvp7002 adapter"
>> +                              "on address 0x%x\n", c->addr << 1);
>> +
>> +      v4l2_device_unregister_subdev(sd);
>> +      kfree(to_tvp7002(sd));
>> +      return 0;
>> +}
>> +
>> +/* I2C Device ID table */
>> +static const struct i2c_device_id tvp7002_id[] = {
>> +      { "tvp7002", 0 },
>> +      { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, tvp7002_id);
>> +
>> +/* I2C driver data */
>> +static struct i2c_driver tvp7002_driver = {
>> +      .driver = {
>> +              .owner = THIS_MODULE,
>> +              .name = "tvp7002",
>> +      },
>> +      .probe = tvp7002_probe,
>> +      .remove = tvp7002_remove,
>> +      .id_table = tvp7002_id,
>> +};
>> --
>> 1.6.0.4
>>
>>     
>
>
Murali Karicheri Sept. 2, 2009, 7:29 p.m. UTC | #4
Murali Karicheri
Software Design Engineer
Texas Instruments Inc.
Germantown, MD 20874
new phone: 301-407-9583
Old Phone : 301-515-3736 (will be deprecated)
email: m-karicheri2@ti.com

>-----Original Message-----
>From: Santiago Nunez-Corrales [mailto:snunez@ridgerun.com]
>Sent: Wednesday, September 02, 2009 3:18 PM
>To: Karicheri, Muralidharan
>Cc: santiago.nunez@ridgerun.com; davinci-linux-open-
>source@linux.davincidsp.com; todd.fischer@ridgerun.com;
>diego.dompe@ridgerun.com; clark.becker@ridgerun.com; Narnakaje, Snehaprabha
>Subject: Re: [PATCH 5/6 v3] Updated TVP7002 driver for DM365
>
>Murali.
>
>Karicheri, Muralidharan wrote:
>> Santiago,
>>
>> you are missing a tvp7002_init() that calls i2c_add_driver(). Similarly
>i2c_del_driver(). See tvp514x.c or adv7353.c for examples... Also read the
>>
>
>Answered in previous email to Sneha. Addressing.
>
[MK] Ok.
>> polarities from platform_data in probe(). I had mentioned the second
>comment during earlier review.
>>
>>
>
>Please check lines 1328 and 1378. The polarity information is set via
>register 17h with respect to platform_data. That part of the code will
>change according to a suggestion by Hans, but the code is there.
>
[MK]See how tvp514x.c handles this. You need to refer the code to fix it.
>Regards,
>
>> Murali Karicheri
>> Software Design Engineer
>> Texas Instruments Inc.
>> Germantown, MD 20874
>> new phone: 301-407-9583
>> Old Phone : 301-515-3736 (will be deprecated)
>> email: m-karicheri2@ti.com
>>
>>
>>> -----Original Message-----
>>> From: santiago.nunez@ridgerun.com [mailto:santiago.nunez@ridgerun.com]
>>> Sent: Tuesday, September 01, 2009 6:55 PM
>>> To: Karicheri, Muralidharan
>>> Cc: davinci-linux-open-source@linux.davincidsp.com;
>>> todd.fischer@ridgerun.com; diego.dompe@ridgerun.com;
>>> clark.becker@ridgerun.com; Narnakaje, Snehaprabha; Santiago Nunez-
>Corrales
>>> Subject: [PATCH 5/6 v3] Updated TVP7002 driver for DM365
>>>
>>> From: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>>>
>>> This patch provides the implementation of the TVP7002 decoder
>>> driver for DM365. Implemented revisions by Vaibhav Hiremath
>>> and Hans Verkuil. Removed most of controls, cleared up logic,
>>> cleaned up code.
>>>
>>> Signed-off-by: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>>> ---
>>> drivers/media/video/tvp7002.c | 1493
>>> +++++++++++++++++++++++++++++++++++++++++
>>> 1 files changed, 1493 insertions(+), 0 deletions(-)
>>> create mode 100644 drivers/media/video/tvp7002.c
>>>
>>> diff --git a/drivers/media/video/tvp7002.c
>b/drivers/media/video/tvp7002.c
>>> new file mode 100644
>>> index 0000000..e8cd77e
>>> --- /dev/null
>>> +++ b/drivers/media/video/tvp7002.c
>>> @@ -0,0 +1,1493 @@
>>> +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
>>> + * Digitizer with Horizontal PLL registers
>>> + *
>>> + * Copyright (C) 2009 Texas Instruments Inc
>>> + * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
>>> + *
>>> + * This code is partially based upon the TVP5150 driver
>>> + * written by Mauro Carvalho Chehab (mchehab@infradead.org)
>>> + * and the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
>>> + *
>>> + * 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.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>>> + */
>>> +
>>> +#include <linux/i2c.h>
>>> +#include <linux/videodev2.h>
>>> +#include <linux/delay.h>
>>> +#include <media/v4l2-device.h>
>>> +#include <media/tvp7002.h>
>>> +#include <media/v4l2-i2c-drv.h>
>>> +#include <media/v4l2-chip-ident.h>
>>> +#include <media/davinci/videohd.h>
>>> +#include "tvp7002_reg.h"
>>> +
>>> +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
>>> +MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
>>> +MODULE_LICENSE("GPL");
>>> +
>>> +/* I2C retry attempts */
>>> +#define I2C_RETRY_COUNT                 (5)
>>> +
>>> +/* Debugging information */
>>> +
>>> +static int debug;
>>> +module_param(debug, int, 0);
>>> +MODULE_PARM_DESC(debug, "Debug level (0-2)");
>>> +
>>> +/* Reference to tvp7002_platform_data */
>>> +extern struct tvp7002_platform_data tvp7002_pdata;
>>> +
>>> +/* Struct for handling resolutions and associate register values */
>>> +struct tvp7002_resol {
>>> +      v4l2_std_id id;
>>> +      int hres;
>>> +      int vres;
>>> +      int frate;
>>> +      int lrate;
>>> +      int prate;
>>> +      u8 reg01;
>>> +      u8 reg02;
>>> +      u8 reg03;
>>> +      u8 reg04;
>>> +};
>>> +
>>> +/* Struct for handling register values */
>>> +struct i2c_reg_value {
>>> +      u8 reg;
>>> +      u8 value;
>>> +};
>>> +
>>> +/* Register default values (according to tvp7002 datasheet) */
>>> +static const struct i2c_reg_value tvp7002_init_default[] = {
>>> +      /* 0x00: read only */
>>> +      { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67 },
>>> +      { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20 },
>>> +      { TVP7002_HPLL_CRTL, 0xa8 },
>>> +      { TVP7002_HPLL_PHASE_SEL, 0x80 },
>>> +      { TVP7002_CLAMP_START, 0x32 },
>>> +      { TVP7002_CLAMP_W, 0x20 },
>>> +      { TVP7002_HSYNC_OUT_W, 0x20 },
>>> +      { TVP7002_B_FINE_GAIN, 0x00 },
>>> +      { TVP7002_G_FINE_GAIN, 0x00 },
>>> +      { TVP7002_R_FINE_GAIN, 0x00 },
>>> +      { TVP7002_B_FINE_OFF_MSBS, 0x80 },
>>> +      { TVP7002_G_FINE_OFF_MSBS, 0x80 },
>>> +      { TVP7002_R_FINE_OFF_MSBS, 0x80 },
>>> +      { TVP7002_SYNC_CTL_1, 0x5b },
>>> +      { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e },
>>> +      { TVP7002_SYNC_ON_G_THRS, 0x5d },
>>> +      { TVP7002_SYNC_SEPARATOR_THRS, 0x20 },
>>> +      { TVP7002_HPLL_PRE_COAST, 0x00 },
>>> +      { TVP7002_HPLL_POST_COAST, 0x00 },
>>> +      /* 0x14: read only */
>>> +      { TVP7002_OUT_FORMATTER, 0x00 },
>>> +      { TVP7002_MISC_CTL_1, 0x11 },
>>> +      { TVP7002_MISC_CTL_2, 0x03 },
>>> +      { TVP7002_MISC_CTL_3, 0x00 },
>>> +      { TVP7002_IN_MUX_SEL_1, 0x00 },
>>> +      { TVP7002_IN_MUX_SEL_2, 0xc2 },
>>> +      { TVP7002_B_AND_G_COARSE_GAIN, 0x77 },
>>> +      { TVP7002_R_COARSE_GAIN, 0x07 },
>>> +      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>>> +      { TVP7002_FINE_OFF_LSBS, 0x00 },
>>> +      { TVP7002_B_COARSE_OFF, 0x10 },
>>> +      { TVP7002_G_COARSE_OFF, 0x10 },
>>> +      { TVP7002_R_COARSE_OFF, 0x10 },
>>> +      { TVP7002_HSOUT_OUT_START, 0x0d },
>>> +      { TVP7002_MISC_CTL_4, 0x0d },
>>> +      /* 0x23: read only */
>>> +      /* 0x24: read only */
>>> +      /* 0x25: read only */
>>> +      { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80 },
>>> +      /* 0x27: read only */
>>> +      { TVP7002_AUTO_LVL_CTL_FILTER, 0x53 },
>>> +      { TVP7002_FINE_CLAMP_CTL, 0x07 },
>>> +      { TVP7002_PWR_CTL, 0x00 },
>>> +      { TVP7002_ADC_SETUP, 0x50 },
>>> +      { TVP7002_COARSE_CLAMP_CTL, 0x00 },
>>> +      { TVP7002_SOG_CLAMP, 0x80 },
>>> +      { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c },
>>> +      { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04 },
>>> +      { TVP7002_ALC_PLACEMENT, 0x5a },
>>> +      { TVP7002_MVIS_STRIPPER_W, 0x03 },
>>> +      { TVP7002_VSYNC_ALGN, 0x10 },
>>> +      { TVP7002_SYNC_BYPASS, 0x00 },
>>> +      /* 0x37: read only */
>>> +      /* 0x38: read only */
>>> +      /* 0x39: read only */
>>> +      /* 0x3a: read only */
>>> +      /* 0x3b: read only */
>>> +      /* 0x3c: read only */
>>> +      { TVP7002_L_LENGTH_TOL, 0x03 },
>>> +      { TVP7002_VIDEO_BWTH_CTL, 0x00 },
>>> +      { TVP7002_AVID_START_PIXEL_LSBS, 0x01 },
>>> +      { TVP7002_AVID_START_PIXEL_MSBS, 0x2c },
>>> +      { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06 },
>>> +      { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c },
>>> +      { TVP7002_VBLK_F_0_START_L_OFF, 0x05 },
>>> +      { TVP7002_VBLK_F_1_START_L_OFF, 0x05 },
>>> +      { TVP7002_VBLK_F_0_DURATION, 0x1e },
>>> +      { TVP7002_VBLK_F_1_DURATION, 0x1e },
>>> +      { TVP7002_FBIT_F_0_START_L_OFF, 0x00 },
>>> +      { TVP7002_FBIT_F_1_START_L_OFF, 0x00 },
>>> +      { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3 },
>>> +      { TVP7002_YUV_Y_G_COEF_MSBS, 0x16 },
>>> +      { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f },
>>> +      { TVP7002_YUV_Y_B_COEF_MSBS, 0x02 },
>>> +      { TVP7002_YUV_Y_R_COEF_LSBS, 0xce },
>>> +      { TVP7002_YUV_Y_R_COEF_MSBS, 0x06 },
>>> +      { TVP7002_YUV_U_G_COEF_LSBS, 0xab },
>>> +      { TVP7002_YUV_U_G_COEF_MSBS, 0xf3 },
>>> +      { TVP7002_YUV_U_B_COEF_LSBS, 0x00 },
>>> +      { TVP7002_YUV_U_B_COEF_MSBS, 0x10 },
>>> +      { TVP7002_YUV_U_R_COEF_LSBS, 0x55 },
>>> +      { TVP7002_YUV_U_R_COEF_MSBS, 0xfc },
>>> +      { TVP7002_YUV_V_G_COEF_LSBS, 0x78 },
>>> +      { TVP7002_YUV_V_G_COEF_MSBS, 0xf1 },
>>> +      { TVP7002_YUV_V_B_COEF_LSBS, 0x88 },
>>> +      { TVP7002_YUV_V_B_COEF_MSBS, 0xfe },
>>> +      { TVP7002_YUV_V_R_COEF_LSBS, 0x00 },
>>> +      { TVP7002_YUV_V_R_COEF_MSBS, 0x10 },
>>> +      { 0x5c, 0x00 }  /* end of registers */
>>> +};
>>> +
>>> +/* Available resolutions */
>>> +static struct tvp7002_resol tvp7002_resolutions[] = {
>>> +      {
>>> +              .id = V4L2_STD_525I_60,
>>> +              .hres = 720,
>>> +              .vres = 480,
>>> +              .frate = 30,
>>> +              .lrate = 15,
>>> +              .prate = 14,
>>> +              .reg01 = 0x35,
>>> +              .reg02 = 0xa0,
>>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_625I_50,
>>> +              .hres = 720,
>>> +              .vres = 576,
>>> +              .frate = 25,
>>> +              .lrate = 16,
>>> +              .prate = 14,
>>> +              .reg01 = 0x36,
>>> +              .reg02 = 0x00,
>>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_525P_60,
>>> +              .hres = 720,
>>> +              .vres = 480,
>>> +              .frate = 60,
>>> +              .lrate = 31,
>>> +              .prate = 27,
>>> +              .reg01 = 0x35,
>>> +              .reg02 = 0xa0,
>>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_625P_50,
>>> +              .hres = 720,
>>> +              .vres = 576,
>>> +              .frate = 50,
>>> +              .lrate = 31,
>>> +              .prate = 27,
>>> +              .reg01 = 0x36,
>>> +              .reg02 = 0x00,
>>> +              .reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_720P_60,
>>> +              .hres = 1280,
>>> +              .vres = 720,
>>> +              .frate = 60,
>>> +              .lrate = 45,
>>> +              .prate = 74,
>>> +              .reg01 = 0x67,
>>> +              .reg02 = 0x20,
>>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x20,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_720P_50,
>>> +              .hres = 1280,
>>> +              .vres = 720,
>>> +              .frate = 50,
>>> +              .lrate = 38,
>>> +              .prate = 74,
>>> +              .reg01 = 0x7b,
>>> +              .reg02 = 0xc0,
>>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_1080I_60,
>>> +              .hres = 1920,
>>> +              .vres = 1080,
>>> +              .frate = 60,
>>> +              .lrate = 34,
>>> +              .prate = 74,
>>> +              .reg01 = 0x89,
>>> +              .reg02 = 0x80,
>>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_1080I_50,
>>> +              .hres = 1920,
>>> +              .vres = 1080,
>>> +              .frate = 50,
>>> +              .lrate = 28,
>>> +              .prate = 74,
>>> +              .reg01 = 0xa5,
>>> +              .reg02 = 0x00,
>>> +              .reg03 = TVP7002_VCO_RANGE_MED | 0x10,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_1080P_60,
>>> +              .hres = 1920,
>>> +              .vres = 1080,
>>> +              .frate = 60,
>>> +              .lrate = 68,
>>> +              .prate = 149,
>>> +              .reg01 = 0x89,
>>> +              .reg02 = 0x80,
>>> +              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x20,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +      {
>>> +              .id = V4L2_STD_1080P_50,
>>> +              .hres = 1920,
>>> +              .vres = 1080,
>>> +              .frate = 50,
>>> +              .lrate = 56,
>>> +              .prate = 149,
>>> +              .reg01 = 0xa5,
>>> +              .reg02 = 0x00,
>>> +              .reg03 = TVP7002_VCO_RANGE_HIGH | 0x18,
>>> +              .reg04 = 0x80,
>>> +      },
>>> +};
>>> +
>>> +/*
>>> + * tvp7002_from_std - Map video standard to register information
>>> + * @std: v4l2_std_id (u64) integer
>>> + *
>>> + * Returns index of std or -1 in case of error.
>>> + */
>>> +int tvp7002_from_std(v4l2_std_id std)
>>> +{
>>> +      int res;
>>> +
>>> +      switch (std) {
>>> +      case V4L2_STD_525P_60:
>>> +              res = TVP7002_STD_480P;
>>> +              break;
>>> +      case V4L2_STD_525I_60:
>>> +              res = TVP7002_STD_480I;
>>> +              break;
>>> +      case V4L2_STD_625P_50:
>>> +              res = TVP7002_STD_576P;
>>> +              break;
>>> +      case V4L2_STD_625I_50:
>>> +              res = TVP7002_STD_576I;
>>> +              break;
>>> +      case V4L2_STD_720P_50:
>>> +              res = TVP7002_STD_720P_50;
>>> +              break;
>>> +      case V4L2_STD_720P_60:
>>> +              res = TVP7002_STD_720P_60;
>>> +              break;
>>> +      case V4L2_STD_1080I_50:
>>> +              res = TVP7002_STD_1080I_50;
>>> +              break;
>>> +      case V4L2_STD_1080I_60:
>>> +              res = TVP7002_STD_1080I_60;
>>> +              break;
>>> +      case V4L2_STD_1080P_50:
>>> +              res = TVP7002_STD_1080P_50;
>>> +              break;
>>> +      case V4L2_STD_1080P_60:
>>> +              res = TVP7002_STD_1080P_60;
>>> +              break;
>>> +      default:
>>> +              res = -1;
>>> +              break;
>>> +      }
>>> +
>>> +      return res;
>>> +}
>>> +
>>> +/* Device definition */
>>> +struct tvp7002 {
>>> +      struct v4l2_subdev sd;
>>> +      v4l2_std_id video_mode;
>>> +      struct v4l2_pix_format pix;
>>> +      int streaming;
>>> +};
>>> +
>>> +/* Supported controls */
>>> +static struct v4l2_queryctrl tvp7002_qctrl[] = {
>>> +      {
>>> +              /* This gain control uses fine grain in TVP7002 */
>>> +              .id = V4L2_CID_GAIN,
>>> +              .type = V4L2_CTRL_TYPE_INTEGER,
>>> +              .name = "Gain for RGB channels",
>>> +              .minimum = 0,
>>> +              .maximum = 255,
>>> +              .step = 1,
>>> +              .default_value = 0,
>>> +              .flags = 0,
>>> +      },
>>> +};
>>> +
>>> +/*
>>> + * to_tvp7002 - Obtain device handler TVP7002
>>> + * @sd: ptr to v4l2_subdev struct
>>> + *
>>> + * Returns device handler tvp7002.
>>> + */
>>> +static struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
>>> +{
>>> +      return container_of(sd, struct tvp7002, sd);
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_read - Read a value from a register in an TVP7002
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @reg: TVP7002 register address
>>> + *
>>> + * Returns value read if successful, or non-zero (-1) otherwise.
>>> + */
>>> +static int tvp7002_read(struct v4l2_subdev *sd, unsigned char addr)
>>> +{
>>> +      struct i2c_client *c = v4l2_get_subdevdata(sd);
>>> +      int retry;
>>> +      int error;
>>> +
>>> +      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>>> +              error = i2c_smbus_read_byte_data(c, addr);
>>> +
>>> +              if (error >= 0)
>>> +                      return error;
>>> +              msleep_interruptible(10);
>>> +      }
>>> +      v4l2_err(sd, "TVP7002 read error %d\n", error);
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_write() - Write a value to a register in TVP7002
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @addr: TVP7002 register address
>>> + * @value: value to be written to the register
>>> + *
>>> + * Write a value to a register in an TVP7002 decoder device.
>>> + * Returns zero if successful, or non-zero otherwise.
>>> + */
>>> +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
>>> +{
>>> +      struct i2c_client *c = v4l2_get_subdevdata(sd);
>>> +      int retry;
>>> +      int error;
>>> +
>>> +      for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
>>> +              error = i2c_smbus_write_byte_data(c, addr, value);
>>> +
>>> +              if (error >= 0)
>>> +                      return error;
>>> +              msleep_interruptible(10);
>>> +      }
>>> +      v4l2_err(sd, "TVP7002 write error %d\n", error);
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * dump_reg_range() - Dump information from TVP7002 registers
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @init: TVP7002 start address
>>> + * @end: TVP7002 end address
>>> + *
>>> + * Dump values at a specified register range
>>> + * Returns nothing.
>>> + */
>>> +static void dump_reg_range(struct v4l2_subdev *sd, u8 init, const u8
>end)
>>> +{
>>> +      int i = 0;
>>> +      int result;
>>> +
>>> +      while (init != (u8)(end + 1)) {
>>> +              result = tvp7002_read(sd, init);
>>> +
>>> +              if (result < 0)
>>> +                      v4l2_err(sd, "tvp7002: reg 0x%02x unreadable\n",
>i);
>>> +              else
>>> +                      v4l2_info(sd, "tvp7002: @0x%02x = %02x\n", i,
>result);
>>> +
>>> +              init++;
>>> +              i++;
>>> +      }
>>> +}
>>> +
>>> +/*
>>> + * Macro for handling reading error conditions in tvp7002_log_status
>>> + */
>>> +#define TVP7002_LOG_CHK(reg, message, res) \
>>> +      do {\
>>> +              (res) = tvp7002_read(sd, (reg));\
>>> +              \
>>> +              if ((res) >= 0)\
>>> +                      v4l2_info(sd, "%s = 0x%02x\n", (message),
>(res));\
>>> +      } while (0)
>>> +/*
>>> + * tvp7002_log_status() - Print information about register settings
>>> + * @sd: ptr to v4l2_subdev struct
>>> + *
>>> + * Log register values of a TVP7002 decoder device.
>>> + * Returns zero or -EINVAL if read operation fails.
>>> + */
>>> +static int tvp7002_log_status(struct v4l2_subdev *sd)
>>> +{
>>> +      int rres;
>>> +
>>> +      TVP7002_LOG_CHK(TVP7002_CHIP_REV, "Chip revision number", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_LSBS, "H-PLL feedback div
>LSB",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "H-PLL feedback div
>MSB",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "VCO freq range
>selector",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_PHASE_SEL, "ADC sampling clk phase
>sel",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_CLAMP_START, "Clamp start", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_CLAMP_W, "Clamp width", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HSYNC_OUT_W, "HSYNC output width", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_B_FINE_GAIN, "Digital fine grain B ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_G_FINE_GAIN, "Digital fine grain G ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_R_FINE_GAIN, "Digital fine grain R ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_B_FINE_OFF_MSBS, "Digital fine grain off
>B
>>> ch",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_G_FINE_OFF_MSBS, "Digital fine grain off
>G
>>> ch",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_R_FINE_OFF_MSBS, "Digital fine grain off
>R
>>> ch",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_FINE_OFF_LSBS, "Dig fine grain off LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SYNC_CTL_1, "Sync control 1", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_AND_CLAMP_CTL, "H-PLL and clamp
>control",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SYNC_ON_G_THRS, "Sync-On-Green threshold",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SYNC_SEPARATOR_THRS, "Sync separator
>>> thrshold",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_PRE_COAST, "H-PLL pre-coast", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HPLL_POST_COAST, "H-PLL post-coast",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SYNC_DETECT_STAT, "Sync detect status",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_OUT_FORMATTER, "Output formatter", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_1, "Miscelaneous control 1",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_2, "Miscelaneous control 2",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_3, "Miscelaneous control 3",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_1, "Input Mux Selector 1",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_2, "Input Mux Selector 2",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_B_AND_G_COARSE_GAIN, "B and G coarse
>gain",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_R_COARSE_GAIN, "R coarse gain", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_B_COARSE_OFF, "Coarse offset for B ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_G_COARSE_OFF, "Coarse offset for G ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_R_COARSE_OFF, "Coarse offset for R ch",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HSOUT_OUT_START, "HSYNC lead edge out
>start",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_MISC_CTL_4, "Miscelaneous control 4",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_B_DGTL_ALC_OUT_LSBS, "Flt ALC out B ch
>LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_G_DGTL_ALC_OUT_LSBS, "Flt ALC out G ch
>LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_R_DGTL_ALC_OUT_LSBS, "Flt ALC out R ch
>LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_ENABLE, "Auto level ctrl
>enable",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_DGTL_ALC_OUT_MSBS, "Filt ALC out RGB chs
>MSB",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_FILTER, "Auto level ctrl
>filter",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_FINE_CLAMP_CTL, "Fine clamp control",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_PWR_CTL, "Power control", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_ADC_SETUP, "ADC setup", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_COARSE_CLAMP_CTL, "Coarse clamp ctrl",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SOG_CLAMP, "Sync-On-Green clamp", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_RGB_COARSE_CLAMP_CTL, "RGB coarse clamp
>ctrl",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SOG_COARSE_CLAMP_CTL, "SOG coarse clamp
>ctrl",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_ALC_PLACEMENT, "ALC placement", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_MVIS_STRIPPER_W, "Macrovision stripper
>width",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_SYNC_BYPASS, "Sync bypass", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_LSBS, "Lines p Frame status
>>> LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_MSBS, "Lines p Frame status
>>> MSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_LSBS, "Clks p line status
>LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_MSBS, "Clks p line status
>MSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_HSYNC_W, "HSYNC width", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VSYNC_W, "VSYNC width", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_L_LENGTH_TOL, "Line length tolerance",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VIDEO_BWTH_CTL, "Video bandwth control",
>>> rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_LSBS, "AVID start pixel
>>> LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_MSBS, "AVID start pixel
>>> MSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_LSBS, "AVID stop pixel
>LSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_MSBS, "AVID stop pixel
>MSBs",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_START_L_OFF, "VBLK start line
>off 0",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_START_L_OFF, "VBLK start line
>off 1",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_0_DURATION, "VBLK duration 0",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_VBLK_F_1_DURATION, "VBLK duration 1",
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_FBIT_F_0_START_L_OFF, "FBIT start line
>off 0",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_FBIT_F_1_START_L_OFF, "FBIT start line
>off 1",
>>> +
>rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_LSBS, "YUV Y G LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_MSBS, "YUV Y G MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_LSBS, "YUV Y B LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_MSBS, "YUV Y B MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_LSBS, "YUV Y R LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_MSBS, "YUV Y R MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_LSBS, "YUV U G LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_MSBS, "YUV U G MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_LSBS, "YUV U B LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_MSBS, "YUV U B MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_LSBS, "YUV U R LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_MSBS, "YUV U R MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_LSBS, "YUV V G LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_MSBS, "YUV V G MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_LSBS, "YUV V B LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_MSBS, "YUV V B MSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_LSBS, "YUV V R LSBs", rres);
>>> +      TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_MSBS, "YUV V R MSBs", rres);
>>> +
>>> +      return rres;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_g_chip_ident() - Get chip identification number
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @chip: ptr to v4l2_dbg_chip_ident struct
>>> + *
>>> + * Obtains the chip's identification number.
>>> + * Returns zero or -EINVAL if read operation fails.
>>> + */
>>> +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
>>> +                                      struct v4l2_dbg_chip_ident *chip)
>>> +{
>>> +      int rev;
>>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>>> +
>>> +      rev = tvp7002_read(sd, TVP7002_CHIP_REV);
>>> +
>>> +      if (rev < 0)
>>> +              return -EINVAL;
>>> +
>>> +      return v4l2_chip_ident_i2c_client(client, chip,
>V4L2_IDENT_TVP7002,
>>> +
>rev);
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_write_inittab() - Write initialization values
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @regs: ptr to i2c_reg_value struct
>>> + *
>>> + * Write initialization values.
>>> + * Returns zero or -EINVAL if read operation fails.
>>> + */
>>> +static int tvp7002_write_inittab(struct v4l2_subdev *sd,
>>> +                                      const struct i2c_reg_value *regs)
>>> +{
>>> +      int i;
>>> +      int error;
>>> +      /* Initialize the first (defined) registers */
>>> +      while (regs->reg != 0x5c) {
>>> +              error = tvp7002_write(sd, regs->reg, regs->value);
>>> +              if (error < 0)
>>> +                      return -EINVAL;
>>> +              regs++;
>>> +      }
>>> +      /* Initialize the last unnamed registers */
>>> +      for (i = 0x5c; i <= 0xff; i++) {
>>> +              error = tvp7002_write(sd, i, 0x00);
>>> +              if (error < 0)
>>> +                      return -EINVAL;
>>> +              regs++;
>>> +      }
>>> +
>>> +      return 0;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_set_video_mode()
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @sdf: index to structure describing format
>>> + *
>>> + * Set video standard according to index
>>> + *
>>> + * Returns 0 if operation is successful or -EINVAL otherwise
>>> + */
>>> +static int tvp7002_set_video_mode(struct v4l2_subdev *sd, int sdf)
>>> +{
>>> +      int error;
>>> +
>>> +      if (sdf < TVP7002_STD_480I || sdf > TVP7002_STD_1080P_50) {
>>> +              v4l2_err(sd, "sf out of range\n");
>>> +              return -ERANGE;
>>> +      }
>>> +
>>> +      /* Print specific information about current format */
>>> +      v4l2_info(sd, "Setting standard display format...\n");
>>> +      v4l2_info(sd, "hres = %d vres=%d fr=%d lr=%d prate=%d\n",
>>> +                      tvp7002_resolutions[sdf].hres,
>>> +                      tvp7002_resolutions[sdf].vres,
>>> +                      tvp7002_resolutions[sdf].frate,
>>> +                      tvp7002_resolutions[sdf].lrate,
>>> +                      tvp7002_resolutions[sdf].prate);
>>> +      /* Set registers accordingly */
>>> +      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_MSBS,
>>> +                                      tvp7002_resolutions[sdf].reg01);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_LSBS,
>>> +                                      tvp7002_resolutions[sdf].reg02);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_HPLL_CRTL,
>>> +                                      tvp7002_resolutions[sdf].reg03);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error =  tvp7002_write(sd, TVP7002_HPLL_PHASE_SEL,
>>> +                                      tvp7002_resolutions[sdf].reg04);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      /* Set SD/HD mode registers */
>>> +
>>> +      if (sdf < TVP7002_STD_720P_60) {
>>> +              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x06);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x10);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x03);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x03);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0x17);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              if (sdf < TVP7002_STD_480P) {
>>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>>> +
>0x0c);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +
>>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>>> +
>0x24);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +
>>> +              } else {
>>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>>> +
>0x0a);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +
>>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>>> +
>0x12);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +              }
>>> +              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x08);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x18);
>>> +              if (error < 0)
>>> +                      return error;
>>> +      } else {
>>> +              error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x32);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x20);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x01);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x00);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0xc7);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              if(sdf < TVP7002_STD_1080I_60) {
>>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>>> +
>0x35);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +              } else {
>>> +                      error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
>>> +
>0x39);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +              }
>>> +              error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x00);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x5a);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              if(sdf < TVP7002_STD_1080P_60) {
>>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>>> +
>0x07);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +              } else {
>>> +                      error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
>>> +
>0x03);
>>> +                      if (error < 0)
>>> +                              return error;
>>> +              }
>>> +      }
>>> +      if (sdf < TVP7002_STD_1080P_60) {
>>> +              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x50);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x0f);
>>> +              if (error < 0)
>>> +                      return error;
>>> +      } else {
>>> +              error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x80);
>>> +              if (error < 0)
>>> +                      return error;
>>> +
>>> +              error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x00);
>>> +              if (error < 0)
>>> +                      return error;
>>> +      }
>>> +      /* Set up registers that hold the same value regardless of the
>>> +       * SD mode
>>> +       */
>>> +      error = tvp7002_write(sd, TVP7002_SYNC_ON_G_THRS, 0x5d);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
>>> +      if (error < 0)
>>> +              return error;
>>> +
>>> +      error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_get_video_mode() - V4L2 decoder interface handler for
>querystd
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + *
>>> + * Returns the current standard detected by TVP7002. If no active input
>is
>>> + * detected, returns -1
>>> + */
>>> +static v4l2_std_id tvp7002_get_video_mode(struct v4l2_subdev *sd)
>>> +{
>>> +      v4l2_std_id error;
>>> +      int reg01, reg02, reg03;
>>> +      reg01 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_MSBS);
>>> +      reg02 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_LSBS);
>>> +      reg03 = tvp7002_read(sd, TVP7002_HPLL_CRTL);
>>> +
>>> +      if (reg01 < 0 || reg02 < 0 || reg03 < 0) {
>>> +              error = V4L2_STD_UNKNOWN;
>>> +              goto found_error;
>>> +      }
>>> +
>>> +      switch(reg01) {
>>> +      case 0x35:
>>> +              if (reg02 == 0xa0)
>>> +                      error = V4L2_STD_525I_60;
>>> +              else
>>> +                      error = V4L2_STD_625I_50;
>>> +      case 0x36:
>>> +              if (reg02 == 0xa0)
>>> +                      error = V4L2_STD_525P_60;
>>> +              else
>>> +                      error = V4L2_STD_625P_50;
>>> +              break;
>>> +      case 0x67:
>>> +              error = V4L2_STD_720P_60;
>>> +              break;
>>> +      case 0x7b:
>>> +              error = V4L2_STD_720P_50;
>>> +              break;
>>> +      case 0x89:
>>> +              if (reg03 == 0x98)
>>> +                      error = V4L2_STD_1080I_60;
>>> +              else
>>> +                      error = V4L2_STD_1080P_60;
>>> +              break;
>>> +      case 0xa5:
>>> +              if (reg03 == 0x90)
>>> +                      error = V4L2_STD_1080I_50;
>>> +              else
>>> +                      error = V4L2_STD_1080P_50;
>>> +              break;
>>> +      default:
>>> +              error = V4L2_STD_UNKNOWN;
>>> +              break;
>>> +      }
>>> +
>>> +found_error:
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_querystd() - V4L2 decoder interface handler for querystd
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @std_id: standard V4L2 std_id ioctl enum
>>> + *
>>> + * Returns the current standard detected by TVP7002. If no active input
>is
>>> + * detected, returns -EINVAL
>>> + */
>>> +static int tvp7002_querystd(struct v4l2_subdev *sd, v4l2_std_id
>*std_id)
>>> +{
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +      v4l2_std_id current_std;
>>> +      u8 sync_lock_status;
>>> +      int res;
>>> +
>>> +      if (std_id == NULL)
>>> +              return -EINVAL;
>>> +
>>> +      /* get the current standard */
>>> +      res = tvp7002_get_video_mode(sd);
>>> +      if (res == V4L2_STD_UNKNOWN)
>>> +              return -EINVAL;
>>> +      current_std = res;
>>> +
>>> +      /* check whether signal is locked */
>>> +      sync_lock_status = tvp7002_read(sd, TVP7002_SYNC_DETECT_STAT);
>>> +
>>> +      if (0x02 != (sync_lock_status & 0xff))
>>> +              return -EINVAL; /* No input detected */
>>> +
>>> +      decoder->video_mode = current_std;
>>> +      *std_id = current_std;
>>> +
>>> +      v4l2_info(sd, "Current STD: %d %d @ %d Hz\n",
>>> +              tvp7002_resolutions[tvp7002_from_std(current_std)].hres,
>>> +              tvp7002_resolutions[tvp7002_from_std(current_std)].vres,
>>> +
>tvp7002_resolutions[tvp7002_from_std(current_std)].frate);
>>> +      return 0;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_scanmode() - Returns whether format is progressive
>>> + * or interlaced
>>> + */
>>> +
>>> +
>>> +/*
>>> + * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
>>> + *
>>> + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
>>> + * ioctl is used to negotiate the image capture size and pixel format
>>> + * without actually making it take effect.
>>> + */
>>> +static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct
>v4l2_format
>>> *f)
>>> +{
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +      struct v4l2_pix_format *pix;
>>> +      v4l2_std_id current_std;
>>> +      int res;
>>> +
>>> +      if (f == NULL)
>>> +              return -EINVAL;
>>> +
>>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>> +              /* only capture is supported */
>>> +              f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>> +
>>> +      pix = &f->fmt.pix;
>>> +
>>> +      /* Calculate height and width based on current standard */
>>> +      res = tvp7002_get_video_mode(sd);
>>> +      if (res < 0)
>>> +              return -EINVAL;
>>> +      current_std = res;
>>> +
>>> +      decoder->video_mode = current_std;
>>> +      pix->width =
>tvp7002_resolutions[tvp7002_from_std(current_std)].hres;
>>> +      pix->height =
>>> tvp7002_resolutions[tvp7002_from_std(current_std)].vres;
>>> +
>>> +      pix->pixelformat = V4L2_PIX_FMT_UYVY;
>>> +
>>> +      pix->field = V4L2_FIELD_INTERLACED;
>>> +      pix->bytesperline = pix->width * 2;
>>> +      pix->sizeimage = pix->bytesperline * pix->height;
>>> +      pix->colorspace = V4L2_COLORSPACE_REC709;
>>> +      pix->priv = 0;
>>> +
>>> +      v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline -
> %d"
>>> +                      "Width - %d, Height - %d",
>>> +                      "8-bit UYVY 4:2:2 Format", pix->bytesperline,
>>> +                      pix->width, pix->height);
>>> +      return 0;
>>> +}
>>> +
>>> +/**
>>> + * tvp7002_s_fmt_cap() - V4L2 decoder interface handler for s_fmt
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
>>> + *
>>> + * If the requested format is supported, configures the HW to use that
>>> + * format, returns error code if format not supported or HW can't be
>>> + * correctly configured.
>>> + */
>>> +static int tvp7002_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format
>>> *f)
>>> +{
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +      struct v4l2_pix_format *pix;
>>> +      int rval;
>>> +
>>> +      if (f == NULL)
>>> +              return -EINVAL;
>>> +
>>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>> +              /* only capture is supported */
>>> +              return -EINVAL;
>>> +
>>> +      pix = &f->fmt.pix;
>>> +      rval = tvp7002_try_fmt_cap(sd, f);
>>> +      if (rval)
>>> +              return rval;
>>> +
>>> +              decoder->pix = *pix;
>>> +
>>> +      return rval;
>>> +}
>>> +
>>> +/**
>>> + * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @f: pointer to standard V4L2 v4l2_format structure
>>> + *
>>> + * Returns the decoder's current pixel format in the v4l2_format
>>> + * parameter.
>>> + */
>>> +static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
>>> +{
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +
>>> +      if (f == NULL)
>>> +              return -EINVAL;
>>> +
>>> +      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>> +              /* only capture is supported */
>>> +              return -EINVAL;
>>> +
>>> +      f->fmt.pix = decoder->pix;
>>> +
>>> +      v4l2_info(sd, "Current FMT: bytesperline - %d"
>>> +                      "Width - %d, Height - %d",
>>> +                      decoder->pix.bytesperline,
>>> +                      decoder->pix.width, decoder->pix.height);
>>> +      return 0;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_s_ctrl() - Set a control
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to v4l2_control struct
>>> + *
>>> + * Set a control for a TVP7002 decoder device.
>>> + * Returns zero when successful or -EINVAL if register access fails.
>>> + */
>>> +static int tvp7002_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
>>> +{
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +      int vmd = 0;
>>> +
>>> +      decoder->video_mode = std;
>>> +      vmd = tvp7002_from_std(std);
>>> +
>>> +      v4l2_info(sd, "Set video std mode to %d.\n", (int)std);
>>> +
>>> +      return tvp7002_set_video_mode(sd, vmd);
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_g_ctrl() - Get a control
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to v4l2_control struct
>>> + *
>>> + * Get a control for a TVP7002 decoder device.
>>> + * Returns zero when successful or -EINVAL if register access fails.
>>> + */
>>> +static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>>> *ctrl)
>>> +{
>>> +      int rval, gval, bval;
>>> +      int res;
>>> +
>>> +      v4l2_info(sd, "g_ctrl called\n");
>>> +
>>> +      switch (ctrl->id) {
>>> +      case V4L2_CID_GAIN:
>>> +              rval = tvp7002_read(sd, TVP7002_R_FINE_GAIN);
>>> +              gval = tvp7002_read(sd, TVP7002_G_FINE_GAIN);
>>> +              bval = tvp7002_read(sd, TVP7002_B_FINE_GAIN);
>>> +
>>> +              if (rval < 0 || gval < 0 || bval < 0) {
>>> +                      res = -1;
>>> +              } else if (rval != gval || rval != bval) {
>>> +                      res = -1;
>>> +              } else {
>>> +                      ctrl->value = rval & 0x0F;
>>> +                      res = ctrl->value;
>>> +              }
>>> +              break;
>>> +      default:
>>> +              res = -1;
>>> +              break;
>>> +      }
>>> +
>>> +      return res < 0 ? res : 0;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_s_ctrl() - Set a control
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to v4l2_control struct
>>> + *
>>> + * Set a control in TVP7002 decoder device.
>>> + * Returns zero when successful or -EINVAL if register access fails.
>>> + */
>>> +static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>>> *ctrl)
>>> +{
>>> +      int rval, gval, bval;
>>> +      int error;
>>> +      u8 i, n;
>>> +      n = ARRAY_SIZE(tvp7002_qctrl);
>>> +
>>> +      for (i = 0; i < n; i++)
>>> +              if (ctrl->id == tvp7002_qctrl[i].id)
>>> +                      break;
>>> +
>>> +      if (i == n)
>>> +              return -EINVAL;
>>> +
>>> +      if (ctrl->value < tvp7002_qctrl[i].minimum ||
>>> +                      ctrl->value > tvp7002_qctrl[i].maximum)
>>> +              return -ERANGE;
>>> +
>>> +      switch (ctrl->id) {
>>> +      case V4L2_CID_GAIN:
>>> +              rval = tvp7002_write(sd, TVP7002_R_FINE_GAIN,
>>> +                                                      ctrl->value &
>0xff);
>>> +              gval = tvp7002_write(sd, TVP7002_G_FINE_GAIN,
>>> +                                                      ctrl->value &
>0xff);
>>> +              bval = tvp7002_write(sd, TVP7002_B_FINE_GAIN,
>>> +                                                      ctrl->value &
>0xff);
>>> +              if (rval < 0  || gval < 0 || bval < 0)
>>> +                      error = -1;
>>> +              else
>>> +                      error = rval;
>>> +              break;
>>> +      default:
>>> +              error = -1;
>>> +              break;
>>> +      }
>>> +
>>> +      if (error < 0)
>>> +              return -EINVAL;
>>> +      else
>>> +              return 0;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_g_register() - Get the value of a register
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @vreg: ptr to v4l2_dbg_register struct
>>> + *
>>> + * Get the value of a TVP7002 decoder device register.
>>> + * Returns zero when successful, -EINVAL if register read fails or
>>> + * access to I2C client fails, -EPERM if the call is not allowed
>>> + * by diabled CAP_SYS_ADMIN.
>>> + */
>>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>>> +static int tvp7002_g_register(struct v4l2_subdev *sd,
>>> +                                              struct v4l2_dbg_register
>*reg)
>>> +{
>>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>>> +      int error;
>>> +
>>> +      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>>> +              return -EINVAL;
>>> +      if (!capable(CAP_SYS_ADMIN))
>>> +              return -EPERM;
>>> +
>>> +      reg->val = tvp7002_read(sd, reg->reg & 0xff);
>>> +      reg->size = 1;
>>> +
>>> +      if (reg->val < 0)
>>> +              error = -EINVAL;
>>> +      else
>>> +              error = 0;
>>> +
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_s_register() - set a control
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to v4l2_control struct
>>> + *
>>> + * Get the value of a TVP7002 decoder device register.
>>> + * Returns zero when successful or -EINVAL if register read fails.
>>> + */
>>> +static int tvp7002_s_register(struct v4l2_subdev *sd,
>>> +                                              struct v4l2_dbg_register
>*reg)
>>> +{
>>> +      int error, wres;
>>> +      struct i2c_client *client = v4l2_get_subdevdata(sd);
>>> +
>>> +      if (!v4l2_chip_match_i2c_client(client, &reg->match))
>>> +              return -EINVAL;
>>> +      if (!capable(CAP_SYS_ADMIN))
>>> +              return -EPERM;
>>> +
>>> +      wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
>>> +
>>> +      if (wres < 0)
>>> +              error = -EINVAL;
>>> +      else
>>> +              error = 0;
>>> +
>>> +      return error;
>>> +}
>>> +#endif
>>> +
>>> +/*
>>> + * tvp7002_queryctrl() - Query a control
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to v4l2_queryctrl struct
>>> + *
>>> + * Query a control of a TVP7002 decoder device.
>>> + * Returns zero when successful or -EINVAL if register read fails.
>>> + */
>>> +static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct
>v4l2_queryctrl
>>> *qc)
>>> +{
>>> +      int i, error;
>>> +      error = -EINVAL;
>>> +
>>> +      v4l2_info(sd, "queryctrl called\n");
>>> +
>>> +      for (i = 0; i < ARRAY_SIZE(tvp7002_qctrl); i++)
>>> +              if (qc->id && qc->id == tvp7002_qctrl[i].id) {
>>> +                      memcpy(qc, &(tvp7002_qctrl[i]), sizeof(*qc));
>>> +                      error = 0;
>>> +                      break;
>>> +              }
>>> +
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
>>> + * @sd: pointer to standard V4L2 sub-device structure
>>> + * @enable: streaming enable or disable
>>> + *
>>> + * Sets streaming to enable or disable, if possible.
>>> + */
>>> +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
>>> +{
>>> +      int err = 0;
>>> +      struct tvp7002 *decoder = to_tvp7002(sd);
>>> +
>>> +      if (decoder->streaming == enable)
>>> +              return 0;
>>> +
>>> +      if (enable) {
>>> +              /* Power Up Sequence */
>>> +              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x00);
>>> +              if (err) {
>>> +                      v4l2_err(sd, "Unable to turn on decoder\n");
>>> +                      err = -EINVAL;
>>> +              }
>>> +              err = tvp7002_write_inittab(sd, tvp7002_init_default);
>>> +              if (err < 0) {
>>> +                      v4l2_err(sd, "Unable to initialize\n");
>>> +                      err = -EINVAL;
>>> +              }
>>> +              /* Detect if not already detected */
>>> +              err = tvp7002_read(sd, TVP7002_CHIP_REV);
>>> +              if (err < 0) {
>>> +                      v4l2_err(sd, "Unable to detect decoder\n");
>>> +                      err = -EINVAL;
>>> +              }
>>> +              decoder->streaming = enable;
>>> +      } else {
>>> +              /* Power Down Sequence */
>>> +              err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x40);
>>> +              if (err) {
>>> +                      v4l2_err(sd, "Unable to turn off decoder\n");
>>> +                      return err;
>>> +              }
>>> +              decoder->streaming = enable;
>>> +      }
>>> +
>>> +      return err;
>>> +}
>>> +
>>> +/* Specific video subsystem operation handlers */
>>> +static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
>>> +      .querystd = tvp7002_querystd,
>>> +      .s_stream = tvp7002_s_stream,
>>> +      .g_fmt = tvp7002_g_fmt,
>>> +};
>>> +
>>> +/* V4L2 Operations handlers */
>>> +static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
>>> +      .g_chip_ident = tvp7002_g_chip_ident,
>>> +      .log_status = tvp7002_log_status,
>>> +      .g_ctrl = tvp7002_g_ctrl,
>>> +      .s_ctrl = tvp7002_s_ctrl,
>>> +      .queryctrl = tvp7002_queryctrl,
>>> +      .s_std = tvp7002_s_std,
>>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>>> +      .g_register = tvp7002_g_register,
>>> +      .s_register = tvp7002_s_register,
>>> +#endif
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops tvp7002_ops = {
>>> +      .core = &tvp7002_core_ops,
>>> +      .video = &tvp7002_video_ops,
>>> +};
>>> +
>>> +/*
>>> + * tvp7002_reset - Reset a TVP7002 device
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @val: unsigned integer (not used)
>>> + *
>>> + * Reset the TVP7002 device
>>> + * Returns zero when successful or -EINVAL if register read fails.
>>> + */
>>> +static int tvp7002_reset(struct v4l2_subdev *sd, u32 val)
>>> +{
>>> +      int polarity;
>>> +      int error;
>>> +
>>> +      error = tvp7002_read(sd, TVP7002_CHIP_REV);
>>> +      if (error < 0) {
>>> +              error = -EINVAL;
>>> +              goto found_error;
>>> +      }
>>> +
>>> +      if (error == 0x02) {
>>> +              v4l2_info(sd, "rev. %02x detected.\n", error);
>>> +      } else {
>>> +              v4l2_info(sd, "unknown revision detected.\n");
>>> +              v4l2_info(sd, "revision number is %02x\n", error);
>>> +      }
>>> +
>>> +      /* Set polarity information */
>>> +      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity
>&
>>> +                      tvp7002_pdata.vs_polarity &
>tvp7002_pdata.fid_polarity;
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>>> +      if (error < 0) {
>>> +              error = -EINVAL;
>>> +              goto found_error;
>>> +      }
>>> +
>>> +      /* Initializes TVP7002 to its default values */
>>> +      error = tvp7002_write_inittab(sd, tvp7002_init_default);
>>> +      if (error < 0) {
>>> +              error = -EINVAL;
>>> +              goto found_error;
>>> +      }
>>> +
>>> +found_error:
>>> +      return error;
>>> +};
>>> +
>>> +/*
>>> + * tvp7002_probe - Reset a TVP7002 device
>>> + * @sd: ptr to v4l2_subdev struct
>>> + * @ctrl: ptr to i2c_device_id struct
>>> + *
>>> + * Reset the TVP7002 device
>>> + * Returns zero when successful or -EINVAL if register read fails.
>>> + */
>>> +static int tvp7002_probe(struct i2c_client *c, const struct
>i2c_device_id
>>> *id)
>>> +{
>>> +      struct v4l2_subdev *sd;
>>> +      struct tvp7002 *core;
>>> +      int polarity;
>>> +      int error;
>>> +
>>> +      /* Check if the adapter supports the needed features */
>>> +      if (!i2c_check_functionality(c->adapter,
>>> +              I2C_FUNC_SMBUS_READ_BYTE |
>I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
>>> +              return -EIO;
>>> +
>>> +      core = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
>>> +
>>> +      if (!core)
>>> +              return -ENOMEM;
>>> +
>>> +      sd = &core->sd;
>>> +      v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
>>> +      v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
>>> +                                      c->addr << 1, c->adapter->name);
>>> +
>>> +      /* Set polarity information */
>>> +      polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity
>&
>>> +                      tvp7002_pdata.vs_polarity &
>tvp7002_pdata.fid_polarity;
>>> +      error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
>>> +      if (error < 0) {
>>> +              error = -EINVAL;
>>> +              goto found_error;
>>> +      }
>>> +
>>> +      /* Set video mode */
>>> +      core->video_mode = V4L2_STD_525P_60;
>>> +
>>> +      if (debug > 1)
>>> +              error = tvp7002_log_status(sd);
>>> +      else
>>> +              error = 0;
>>> +
>>> +found_error:
>>> +      if (error < 0)
>>> +              kfree(core);
>>> +
>>> +      return error;
>>> +}
>>> +
>>> +/*
>>> + * tvp7002_remove - Remove TVP7002 device support
>>> + * @c: ptr to i2c_client struct
>>> + *
>>> + * Reset the TVP7002 device
>>> + * Returns zero when successful or -EINVAL if register read fails.
>>> + */
>>> +static int tvp7002_remove(struct i2c_client *c)
>>> +{
>>> +      struct v4l2_subdev *sd = i2c_get_clientdata(c);
>>> +
>>> +      v4l2_dbg(1, debug, sd, "tvp7002.c: removing tvp7002 adapter"
>>> +                              "on address 0x%x\n", c->addr << 1);
>>> +
>>> +      v4l2_device_unregister_subdev(sd);
>>> +      kfree(to_tvp7002(sd));
>>> +      return 0;
>>> +}
>>> +
>>> +/* I2C Device ID table */
>>> +static const struct i2c_device_id tvp7002_id[] = {
>>> +      { "tvp7002", 0 },
>>> +      { }
>>> +};
>>> +MODULE_DEVICE_TABLE(i2c, tvp7002_id);
>>> +
>>> +/* I2C driver data */
>>> +static struct i2c_driver tvp7002_driver = {
>>> +      .driver = {
>>> +              .owner = THIS_MODULE,
>>> +              .name = "tvp7002",
>>> +      },
>>> +      .probe = tvp7002_probe,
>>> +      .remove = tvp7002_remove,
>>> +      .id_table = tvp7002_id,
>>> +};
>>> --
>>> 1.6.0.4
>>>
>>>
>>
>>
>
>
>--
>Santiago Nunez-Corrales, Eng.
>RidgeRun Engineering, LLC
>
>Guayabos, Curridabat
>San Jose, Costa Rica
>+(506) 2271 1487
>+(506) 8313 0536
>http://www.ridgerun.com
>
>
diff mbox

Patch

diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
new file mode 100644
index 0000000..e8cd77e
--- /dev/null
+++ b/drivers/media/video/tvp7002.c
@@ -0,0 +1,1493 @@ 
+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
+ * Digitizer with Horizontal PLL registers
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
+ *
+ * This code is partially based upon the TVP5150 driver
+ * written by Mauro Carvalho Chehab (mchehab@infradead.org)
+ * and the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <media/v4l2-device.h>
+#include <media/tvp7002.h>
+#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/davinci/videohd.h>
+#include "tvp7002_reg.h"
+
+MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
+MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
+MODULE_LICENSE("GPL");
+
+/* I2C retry attempts */
+#define I2C_RETRY_COUNT                 (5)
+
+/* Debugging information */
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* Reference to tvp7002_platform_data */
+extern struct tvp7002_platform_data tvp7002_pdata;
+
+/* Struct for handling resolutions and associate register values */
+struct tvp7002_resol {
+	v4l2_std_id id;
+	int hres;
+	int vres;
+	int frate;
+	int lrate;
+	int prate;
+	u8 reg01;
+	u8 reg02;
+	u8 reg03;
+	u8 reg04;
+};
+
+/* Struct for handling register values */
+struct i2c_reg_value {
+	u8 reg;
+	u8 value;
+};
+
+/* Register default values (according to tvp7002 datasheet) */
+static const struct i2c_reg_value tvp7002_init_default[] = {
+	/* 0x00: read only */
+	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67 },
+	{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x20 },
+	{ TVP7002_HPLL_CRTL, 0xa8 },
+	{ TVP7002_HPLL_PHASE_SEL, 0x80 },
+	{ TVP7002_CLAMP_START, 0x32 },
+	{ TVP7002_CLAMP_W, 0x20 },
+	{ TVP7002_HSYNC_OUT_W, 0x20 },
+	{ TVP7002_B_FINE_GAIN, 0x00 },
+	{ TVP7002_G_FINE_GAIN, 0x00 },
+	{ TVP7002_R_FINE_GAIN, 0x00 },
+	{ TVP7002_B_FINE_OFF_MSBS, 0x80 },
+	{ TVP7002_G_FINE_OFF_MSBS, 0x80 },
+	{ TVP7002_R_FINE_OFF_MSBS, 0x80 },
+	{ TVP7002_SYNC_CTL_1, 0x5b },
+	{ TVP7002_HPLL_AND_CLAMP_CTL, 0x2e },
+	{ TVP7002_SYNC_ON_G_THRS, 0x5d },
+	{ TVP7002_SYNC_SEPARATOR_THRS, 0x20 },
+	{ TVP7002_HPLL_PRE_COAST, 0x00 },
+	{ TVP7002_HPLL_POST_COAST, 0x00 },
+	/* 0x14: read only */
+	{ TVP7002_OUT_FORMATTER, 0x00 },
+	{ TVP7002_MISC_CTL_1, 0x11 },
+	{ TVP7002_MISC_CTL_2, 0x03 },
+	{ TVP7002_MISC_CTL_3, 0x00 },
+	{ TVP7002_IN_MUX_SEL_1, 0x00 },
+	{ TVP7002_IN_MUX_SEL_2, 0xc2 },
+	{ TVP7002_B_AND_G_COARSE_GAIN, 0x77 },
+	{ TVP7002_R_COARSE_GAIN, 0x07 },
+	{ TVP7002_COARSE_CLAMP_CTL, 0x00 },
+	{ TVP7002_FINE_OFF_LSBS, 0x00 },
+	{ TVP7002_B_COARSE_OFF, 0x10 },
+	{ TVP7002_G_COARSE_OFF, 0x10 },
+	{ TVP7002_R_COARSE_OFF, 0x10 },
+	{ TVP7002_HSOUT_OUT_START, 0x0d	},
+	{ TVP7002_MISC_CTL_4, 0x0d },
+	/* 0x23: read only */
+	/* 0x24: read only */
+	/* 0x25: read only */
+	{ TVP7002_AUTO_LVL_CTL_ENABLE, 0x80 },
+	/* 0x27: read only */
+	{ TVP7002_AUTO_LVL_CTL_FILTER, 0x53 },
+	{ TVP7002_FINE_CLAMP_CTL, 0x07 },
+	{ TVP7002_PWR_CTL, 0x00 },
+	{ TVP7002_ADC_SETUP, 0x50 },
+	{ TVP7002_COARSE_CLAMP_CTL, 0x00 },
+	{ TVP7002_SOG_CLAMP, 0x80 },
+	{ TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c },
+	{ TVP7002_SOG_COARSE_CLAMP_CTL, 0x04 },
+	{ TVP7002_ALC_PLACEMENT, 0x5a },
+	{ TVP7002_MVIS_STRIPPER_W, 0x03 },
+	{ TVP7002_VSYNC_ALGN, 0x10 },
+	{ TVP7002_SYNC_BYPASS, 0x00 },
+	/* 0x37: read only */
+	/* 0x38: read only */
+	/* 0x39: read only */
+	/* 0x3a: read only */
+	/* 0x3b: read only */
+	/* 0x3c: read only */
+	{ TVP7002_L_LENGTH_TOL, 0x03 },
+	{ TVP7002_VIDEO_BWTH_CTL, 0x00 },
+	{ TVP7002_AVID_START_PIXEL_LSBS, 0x01 },
+	{ TVP7002_AVID_START_PIXEL_MSBS, 0x2c },
+	{ TVP7002_AVID_STOP_PIXEL_LSBS, 0x06 },
+	{ TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c },
+	{ TVP7002_VBLK_F_0_START_L_OFF, 0x05 },
+	{ TVP7002_VBLK_F_1_START_L_OFF, 0x05 },
+	{ TVP7002_VBLK_F_0_DURATION, 0x1e },
+	{ TVP7002_VBLK_F_1_DURATION, 0x1e },
+	{ TVP7002_FBIT_F_0_START_L_OFF, 0x00 },
+	{ TVP7002_FBIT_F_1_START_L_OFF, 0x00 },
+	{ TVP7002_YUV_Y_G_COEF_LSBS, 0xe3 },
+	{ TVP7002_YUV_Y_G_COEF_MSBS, 0x16 },
+	{ TVP7002_YUV_Y_B_COEF_LSBS, 0x4f },
+	{ TVP7002_YUV_Y_B_COEF_MSBS, 0x02 },
+	{ TVP7002_YUV_Y_R_COEF_LSBS, 0xce },
+	{ TVP7002_YUV_Y_R_COEF_MSBS, 0x06 },
+	{ TVP7002_YUV_U_G_COEF_LSBS, 0xab },
+	{ TVP7002_YUV_U_G_COEF_MSBS, 0xf3 },
+	{ TVP7002_YUV_U_B_COEF_LSBS, 0x00 },
+	{ TVP7002_YUV_U_B_COEF_MSBS, 0x10 },
+	{ TVP7002_YUV_U_R_COEF_LSBS, 0x55 },
+	{ TVP7002_YUV_U_R_COEF_MSBS, 0xfc },
+	{ TVP7002_YUV_V_G_COEF_LSBS, 0x78 },
+	{ TVP7002_YUV_V_G_COEF_MSBS, 0xf1 },
+	{ TVP7002_YUV_V_B_COEF_LSBS, 0x88 },
+	{ TVP7002_YUV_V_B_COEF_MSBS, 0xfe },
+	{ TVP7002_YUV_V_R_COEF_LSBS, 0x00 },
+	{ TVP7002_YUV_V_R_COEF_MSBS, 0x10 },
+	{ 0x5c, 0x00 }	/* end of registers */
+};
+
+/* Available resolutions */
+static struct tvp7002_resol tvp7002_resolutions[] = {
+	{
+		.id = V4L2_STD_525I_60,
+		.hres = 720,
+		.vres = 480,
+		.frate = 30,
+		.lrate = 15,
+		.prate = 14,
+		.reg01 = 0x35,
+		.reg02 = 0xa0,
+		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_625I_50,
+		.hres = 720,
+		.vres = 576,
+		.frate = 25,
+		.lrate = 16,
+		.prate = 14,
+		.reg01 = 0x36,
+		.reg02 = 0x00,
+		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_525P_60,
+		.hres = 720,
+		.vres = 480,
+		.frate = 60,
+		.lrate = 31,
+		.prate = 27,
+		.reg01 = 0x35,
+		.reg02 = 0xa0,
+		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_625P_50,
+		.hres = 720,
+		.vres = 576,
+		.frate = 50,
+		.lrate = 31,
+		.prate = 27,
+		.reg01 = 0x36,
+		.reg02 = 0x00,
+		.reg03 = TVP7002_VCO_RANGE_ULOW | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_720P_60,
+		.hres = 1280,
+		.vres = 720,
+		.frate = 60,
+		.lrate = 45,
+		.prate = 74,
+		.reg01 = 0x67,
+		.reg02 = 0x20,
+		.reg03 = TVP7002_VCO_RANGE_MED | 0x20,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_720P_50,
+		.hres = 1280,
+		.vres = 720,
+		.frate = 50,
+		.lrate = 38,
+		.prate = 74,
+		.reg01 = 0x7b,
+		.reg02 = 0xc0,
+		.reg03 = TVP7002_VCO_RANGE_MED | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_1080I_60,
+		.hres = 1920,
+		.vres = 1080,
+		.frate = 60,
+		.lrate = 34,
+		.prate = 74,
+		.reg01 = 0x89,
+		.reg02 = 0x80,
+		.reg03 = TVP7002_VCO_RANGE_MED | 0x18,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_1080I_50,
+		.hres = 1920,
+		.vres = 1080,
+		.frate = 50,
+		.lrate = 28,
+		.prate = 74,
+		.reg01 = 0xa5,
+		.reg02 = 0x00,
+		.reg03 = TVP7002_VCO_RANGE_MED | 0x10,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_1080P_60,
+		.hres = 1920,
+		.vres = 1080,
+		.frate = 60,
+		.lrate = 68,
+		.prate = 149,
+		.reg01 = 0x89,
+		.reg02 = 0x80,
+		.reg03 = TVP7002_VCO_RANGE_HIGH | 0x20,
+		.reg04 = 0x80,
+	},
+	{
+		.id = V4L2_STD_1080P_50,
+		.hres = 1920,
+		.vres = 1080,
+		.frate = 50,
+		.lrate = 56,
+		.prate = 149,
+		.reg01 = 0xa5,
+		.reg02 = 0x00,
+		.reg03 = TVP7002_VCO_RANGE_HIGH | 0x18,
+		.reg04 = 0x80,
+	},
+};
+
+/*
+ * tvp7002_from_std - Map video standard to register information
+ * @std: v4l2_std_id (u64) integer
+ *
+ * Returns index of std or -1 in case of error.
+ */
+int tvp7002_from_std(v4l2_std_id std)
+{
+	int res;
+
+	switch (std) {
+	case V4L2_STD_525P_60:
+		res = TVP7002_STD_480P;
+		break;
+	case V4L2_STD_525I_60:
+		res = TVP7002_STD_480I;
+		break;
+	case V4L2_STD_625P_50:
+		res = TVP7002_STD_576P;
+		break;
+	case V4L2_STD_625I_50:
+		res = TVP7002_STD_576I;
+		break;
+	case V4L2_STD_720P_50:
+		res = TVP7002_STD_720P_50;
+		break;
+	case V4L2_STD_720P_60:
+		res = TVP7002_STD_720P_60;
+		break;
+	case V4L2_STD_1080I_50:
+		res = TVP7002_STD_1080I_50;
+		break;
+	case V4L2_STD_1080I_60:
+		res = TVP7002_STD_1080I_60;
+		break;
+	case V4L2_STD_1080P_50:
+		res = TVP7002_STD_1080P_50;
+		break;
+	case V4L2_STD_1080P_60:
+		res = TVP7002_STD_1080P_60;
+		break;
+	default:
+		res = -1;
+		break;
+	}
+
+	return res;
+}
+
+/* Device definition */
+struct tvp7002 {
+	struct v4l2_subdev sd;
+	v4l2_std_id video_mode;
+	struct v4l2_pix_format pix;
+	int streaming;
+};
+
+/* Supported controls */
+static struct v4l2_queryctrl tvp7002_qctrl[] = {
+	{
+		/* This gain control uses fine grain in TVP7002 */
+		.id = V4L2_CID_GAIN,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Gain for RGB channels",
+		.minimum = 0,
+		.maximum = 255,
+		.step = 1,
+		.default_value = 0,
+		.flags = 0,
+	},
+};
+
+/*
+ * to_tvp7002 - Obtain device handler TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Returns device handler tvp7002.
+ */
+static struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct tvp7002, sd);
+}
+
+/*
+ * tvp7002_read - Read a value from a register in an TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP7002 register address
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp7002_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(sd);
+	int retry;
+	int error;
+
+	for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+		error = i2c_smbus_read_byte_data(c, addr);
+
+		if (error >= 0)
+			return error;
+		msleep_interruptible(10);
+	}
+	v4l2_err(sd, "TVP7002 read error %d\n", error);
+	return error;
+}
+
+/*
+ * tvp7002_write() - Write a value to a register in TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @addr: TVP7002 register address
+ * @value: value to be written to the register
+ *
+ * Write a value to a register in an TVP7002 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(sd);
+	int retry;
+	int error;
+
+	for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+		error = i2c_smbus_write_byte_data(c, addr, value);
+
+		if (error >= 0)
+			return error;
+		msleep_interruptible(10);
+	}
+	v4l2_err(sd, "TVP7002 write error %d\n", error);
+	return error;
+}
+
+/*
+ * dump_reg_range() - Dump information from TVP7002 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @init: TVP7002 start address
+ * @end: TVP7002 end address
+ *
+ * Dump values at a specified register range
+ * Returns nothing.
+ */
+static void dump_reg_range(struct v4l2_subdev *sd, u8 init, const u8 end)
+{
+	int i = 0;
+	int result;
+
+	while (init != (u8)(end + 1)) {
+		result = tvp7002_read(sd, init);
+
+		if (result < 0)
+			v4l2_err(sd, "tvp7002: reg 0x%02x unreadable\n", i);
+		else
+			v4l2_info(sd, "tvp7002: @0x%02x = %02x\n", i, result);
+
+		init++;
+		i++;
+	}
+}
+
+/*
+ * Macro for handling reading error conditions in tvp7002_log_status
+ */
+#define TVP7002_LOG_CHK(reg, message, res) \
+	do {\
+		(res) = tvp7002_read(sd, (reg));\
+		\
+		if ((res) >= 0)\
+			v4l2_info(sd, "%s = 0x%02x\n", (message), (res));\
+	} while (0)
+/*
+ * tvp7002_log_status() - Print information about register settings
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Log register values of a TVP7002 decoder device.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_log_status(struct v4l2_subdev *sd)
+{
+	int rres;
+
+	TVP7002_LOG_CHK(TVP7002_CHIP_REV, "Chip revision number", rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_LSBS, "H-PLL feedback div LSB",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "H-PLL feedback div MSB",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_FDBK_DIV_MSBS, "VCO freq range selector",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_PHASE_SEL, "ADC sampling clk phase sel",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_CLAMP_START, "Clamp start", rres);
+	TVP7002_LOG_CHK(TVP7002_CLAMP_W, "Clamp width", rres);
+	TVP7002_LOG_CHK(TVP7002_HSYNC_OUT_W, "HSYNC output width", rres);
+	TVP7002_LOG_CHK(TVP7002_B_FINE_GAIN, "Digital fine grain B ch", rres);
+	TVP7002_LOG_CHK(TVP7002_G_FINE_GAIN, "Digital fine grain G ch", rres);
+	TVP7002_LOG_CHK(TVP7002_R_FINE_GAIN, "Digital fine grain R ch", rres);
+	TVP7002_LOG_CHK(TVP7002_B_FINE_OFF_MSBS, "Digital fine grain off B ch",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_G_FINE_OFF_MSBS, "Digital fine grain off G ch",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_R_FINE_OFF_MSBS, "Digital fine grain off R ch",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_FINE_OFF_LSBS, "Dig fine grain off LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_SYNC_CTL_1, "Sync control 1", rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_AND_CLAMP_CTL, "H-PLL and clamp control",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_SYNC_ON_G_THRS, "Sync-On-Green threshold",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_SYNC_SEPARATOR_THRS, "Sync separator thrshold",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_PRE_COAST, "H-PLL pre-coast", rres);
+	TVP7002_LOG_CHK(TVP7002_HPLL_POST_COAST, "H-PLL post-coast", rres);
+	TVP7002_LOG_CHK(TVP7002_SYNC_DETECT_STAT, "Sync detect status", rres);
+	TVP7002_LOG_CHK(TVP7002_OUT_FORMATTER, "Output formatter", rres);
+	TVP7002_LOG_CHK(TVP7002_MISC_CTL_1, "Miscelaneous control 1", rres);
+	TVP7002_LOG_CHK(TVP7002_MISC_CTL_2, "Miscelaneous control 2", rres);
+	TVP7002_LOG_CHK(TVP7002_MISC_CTL_3, "Miscelaneous control 3", rres);
+	TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_1, "Input Mux Selector 1", rres);
+	TVP7002_LOG_CHK(TVP7002_IN_MUX_SEL_2, "Input Mux Selector 2", rres);
+	TVP7002_LOG_CHK(TVP7002_B_AND_G_COARSE_GAIN, "B and G coarse gain",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_R_COARSE_GAIN, "R coarse gain", rres);
+	TVP7002_LOG_CHK(TVP7002_B_COARSE_OFF, "Coarse offset for B ch", rres);
+	TVP7002_LOG_CHK(TVP7002_G_COARSE_OFF, "Coarse offset for G ch", rres);
+	TVP7002_LOG_CHK(TVP7002_R_COARSE_OFF, "Coarse offset for R ch", rres);
+	TVP7002_LOG_CHK(TVP7002_HSOUT_OUT_START, "HSYNC lead edge out start",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_MISC_CTL_4, "Miscelaneous control 4", rres);
+	TVP7002_LOG_CHK(TVP7002_B_DGTL_ALC_OUT_LSBS, "Flt ALC out B ch LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_G_DGTL_ALC_OUT_LSBS, "Flt ALC out G ch LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_R_DGTL_ALC_OUT_LSBS, "Flt ALC out R ch LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_ENABLE, "Auto level ctrl enable",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_DGTL_ALC_OUT_MSBS, "Filt ALC out RGB chs MSB",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_AUTO_LVL_CTL_FILTER, "Auto level ctrl filter",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_FINE_CLAMP_CTL, "Fine clamp control", rres);
+	TVP7002_LOG_CHK(TVP7002_PWR_CTL, "Power control", rres);
+	TVP7002_LOG_CHK(TVP7002_ADC_SETUP, "ADC setup", rres);
+	TVP7002_LOG_CHK(TVP7002_COARSE_CLAMP_CTL, "Coarse clamp ctrl", rres);
+	TVP7002_LOG_CHK(TVP7002_SOG_CLAMP, "Sync-On-Green clamp", rres);
+	TVP7002_LOG_CHK(TVP7002_RGB_COARSE_CLAMP_CTL, "RGB coarse clamp ctrl",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_SOG_COARSE_CLAMP_CTL, "SOG coarse clamp ctrl",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_ALC_PLACEMENT, "ALC placement", rres);
+	TVP7002_LOG_CHK(TVP7002_MVIS_STRIPPER_W, "Macrovision stripper width",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_SYNC_BYPASS, "Sync bypass", rres);
+	TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_LSBS, "Lines p Frame status LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_L_FRAME_STAT_MSBS, "Lines p Frame status MSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_LSBS, "Clks p line status LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_CLK_L_STAT_MSBS, "Clks p line status MSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_HSYNC_W, "HSYNC width", rres);
+	TVP7002_LOG_CHK(TVP7002_VSYNC_W, "VSYNC width", rres);
+	TVP7002_LOG_CHK(TVP7002_L_LENGTH_TOL, "Line length tolerance", rres);
+	TVP7002_LOG_CHK(TVP7002_VIDEO_BWTH_CTL, "Video bandwth control", rres);
+	TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_LSBS, "AVID start pixel LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_AVID_START_PIXEL_MSBS, "AVID start pixel MSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_LSBS, "AVID stop pixel LSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_AVID_STOP_PIXEL_MSBS, "AVID stop pixel MSBs",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_VBLK_F_0_START_L_OFF, "VBLK start line off 0",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_VBLK_F_1_START_L_OFF, "VBLK start line off 1",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_VBLK_F_0_DURATION, "VBLK duration 0", rres);
+	TVP7002_LOG_CHK(TVP7002_VBLK_F_1_DURATION, "VBLK duration 1", rres);
+	TVP7002_LOG_CHK(TVP7002_FBIT_F_0_START_L_OFF, "FBIT start line off 0",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_FBIT_F_1_START_L_OFF, "FBIT start line off 1",
+									rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_LSBS, "YUV Y G LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_G_COEF_MSBS, "YUV Y G MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_LSBS, "YUV Y B LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_B_COEF_MSBS, "YUV Y B MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_LSBS, "YUV Y R LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_Y_R_COEF_MSBS, "YUV Y R MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_LSBS, "YUV U G LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_G_COEF_MSBS, "YUV U G MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_LSBS, "YUV U B LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_B_COEF_MSBS, "YUV U B MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_LSBS, "YUV U R LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_U_R_COEF_MSBS, "YUV U R MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_LSBS, "YUV V G LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_G_COEF_MSBS, "YUV V G MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_LSBS, "YUV V B LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_B_COEF_MSBS, "YUV V B MSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_LSBS, "YUV V R LSBs", rres);
+	TVP7002_LOG_CHK(TVP7002_YUV_V_R_COEF_MSBS, "YUV V R MSBs", rres);
+
+	return rres;
+}
+
+/*
+ * tvp7002_g_chip_ident() - Get chip identification number
+ * @sd: ptr to v4l2_subdev struct
+ * @chip: ptr to v4l2_dbg_chip_ident struct
+ *
+ * Obtains the chip's identification number.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
+					struct v4l2_dbg_chip_ident *chip)
+{
+	int rev;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	rev = tvp7002_read(sd, TVP7002_CHIP_REV);
+
+	if (rev < 0)
+		return -EINVAL;
+
+	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002,
+									rev);
+}
+
+/*
+ * tvp7002_write_inittab() - Write initialization values
+ * @sd: ptr to v4l2_subdev struct
+ * @regs: ptr to i2c_reg_value struct
+ *
+ * Write initialization values.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_write_inittab(struct v4l2_subdev *sd,
+					const struct i2c_reg_value *regs)
+{
+	int i;
+	int error;
+	/* Initialize the first (defined) registers */
+	while (regs->reg != 0x5c) {
+		error = tvp7002_write(sd, regs->reg, regs->value);
+		if (error < 0)
+			return -EINVAL;
+		regs++;
+	}
+	/* Initialize the last unnamed registers */
+	for (i = 0x5c; i <= 0xff; i++) {
+		error = tvp7002_write(sd, i, 0x00);
+		if (error < 0)
+			return -EINVAL;
+		regs++;
+	}
+
+	return 0;
+}
+
+/*
+ * tvp7002_set_video_mode()
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @sdf: index to structure describing format
+ *
+ * Set video standard according to index
+ *
+ * Returns 0 if operation is successful or -EINVAL otherwise
+ */
+static int tvp7002_set_video_mode(struct v4l2_subdev *sd, int sdf)
+{
+	int error;
+
+	if (sdf < TVP7002_STD_480I || sdf > TVP7002_STD_1080P_50) {
+		v4l2_err(sd, "sf out of range\n");
+		return -ERANGE;
+	}
+
+	/* Print specific information about current format */
+	v4l2_info(sd, "Setting standard display format...\n");
+	v4l2_info(sd, "hres = %d vres=%d fr=%d lr=%d prate=%d\n",
+			tvp7002_resolutions[sdf].hres,
+			tvp7002_resolutions[sdf].vres,
+			tvp7002_resolutions[sdf].frate,
+			tvp7002_resolutions[sdf].lrate,
+			tvp7002_resolutions[sdf].prate);
+	/* Set registers accordingly */
+	error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_MSBS,
+					tvp7002_resolutions[sdf].reg01);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_HPLL_FDBK_DIV_LSBS,
+					tvp7002_resolutions[sdf].reg02);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_HPLL_CRTL, 
+					tvp7002_resolutions[sdf].reg03);
+	if (error < 0)
+		return error;
+
+	error =  tvp7002_write(sd, TVP7002_HPLL_PHASE_SEL,
+					tvp7002_resolutions[sdf].reg04);
+	if (error < 0)
+		return error;
+
+	/* Set SD/HD mode registers */
+
+	if (sdf < TVP7002_STD_720P_60) {
+		error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x06);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x10);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x03);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x03);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0x17);
+		if (error < 0)
+			return error;
+
+		if (sdf < TVP7002_STD_480P) {
+			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
+									0x0c);
+			if (error < 0)
+				return error;
+
+			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
+									0x24);
+			if (error < 0)
+				return error;
+
+		} else {
+			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
+									0x0a);
+			if (error < 0)
+				return error;
+
+			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
+									0x12);
+			if (error < 0)
+				return error;
+		}
+		error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x08);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x18);
+		if (error < 0)
+			return error;
+	} else {
+		error = tvp7002_write(sd, TVP7002_CLAMP_START, 0x32);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_CLAMP_W, 0x20);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_HPLL_PRE_COAST, 0x01);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_HPLL_POST_COAST, 0x00);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_2, 0xc7);
+		if (error < 0)
+			return error;
+
+		if(sdf < TVP7002_STD_1080I_60) {
+			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
+									0x35);
+			if (error < 0)
+				return error;
+		} else {
+			error = tvp7002_write(sd, TVP7002_HSOUT_OUT_START,
+									0x39);
+			if (error < 0)
+				return error;
+		}
+		error = tvp7002_write(sd, TVP7002_MISC_CTL_4, 0x00);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_ALC_PLACEMENT, 0x5a);
+		if (error < 0)
+			return error;
+
+		if(sdf < TVP7002_STD_1080P_60) {
+			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
+									0x07);
+			if (error < 0)
+				return error;
+		} else {
+			error = tvp7002_write(sd, TVP7002_MVIS_STRIPPER_W,
+									0x03);
+			if (error < 0)
+				return error;
+		}
+	}
+	if (sdf < TVP7002_STD_1080P_60) {
+		error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x50);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x0f);
+		if (error < 0)
+			return error;
+	} else {
+		error = tvp7002_write(sd, TVP7002_ADC_SETUP, 0x80);
+		if (error < 0)
+			return error;
+
+		error = tvp7002_write(sd, TVP7002_VIDEO_BWTH_CTL, 0x00);
+		if (error < 0)
+			return error;
+	}
+	/* Set up registers that hold the same value regardless of the
+	 * SD mode
+	 */
+	error = tvp7002_write(sd, TVP7002_SYNC_ON_G_THRS, 0x5d);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_SYNC_SEPARATOR_THRS, 0x40);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_3, 0x01);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_IN_MUX_SEL_1, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_VSYNC_ALGN, 0x00);
+	if (error < 0)
+		return error;
+
+	error = tvp7002_write(sd, TVP7002_L_LENGTH_TOL, 0x06);
+	return error;
+}
+
+/*
+ * tvp7002_get_video_mode() - V4L2 decoder interface handler for querystd
+ * @sd: pointer to standard V4L2 sub-device structure
+ *
+ * Returns the current standard detected by TVP7002. If no active input is
+ * detected, returns -1
+ */
+static v4l2_std_id tvp7002_get_video_mode(struct v4l2_subdev *sd)
+{
+	v4l2_std_id error;
+	int reg01, reg02, reg03;
+	reg01 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_MSBS);
+	reg02 = tvp7002_read(sd, TVP7002_HPLL_FDBK_DIV_LSBS);
+	reg03 = tvp7002_read(sd, TVP7002_HPLL_CRTL);
+
+	if (reg01 < 0 || reg02 < 0 || reg03 < 0) {
+		error = V4L2_STD_UNKNOWN;
+		goto found_error;
+	}
+
+	switch(reg01) {
+	case 0x35:
+		if (reg02 == 0xa0)
+			error = V4L2_STD_525I_60;
+		else
+			error = V4L2_STD_625I_50;
+	case 0x36:
+		if (reg02 == 0xa0)
+			error = V4L2_STD_525P_60;
+		else
+			error = V4L2_STD_625P_50;
+		break;
+	case 0x67:
+		error = V4L2_STD_720P_60;
+		break;
+	case 0x7b:
+		error = V4L2_STD_720P_50;
+		break;
+	case 0x89:
+		if (reg03 == 0x98)
+			error = V4L2_STD_1080I_60;
+		else
+			error = V4L2_STD_1080P_60;
+		break;
+	case 0xa5:
+		if (reg03 == 0x90)
+			error = V4L2_STD_1080I_50;
+		else
+			error = V4L2_STD_1080P_50;
+		break;
+	default:
+		error = V4L2_STD_UNKNOWN;
+		break;
+	}
+
+found_error:
+	return error;
+}
+
+/*
+ * tvp7002_querystd() - V4L2 decoder interface handler for querystd
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP7002. If no active input is
+ * detected, returns -EINVAL
+ */
+static int tvp7002_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
+{
+	struct tvp7002 *decoder = to_tvp7002(sd);
+	v4l2_std_id current_std;
+	u8 sync_lock_status;
+	int res;
+
+	if (std_id == NULL)
+		return -EINVAL;
+
+	/* get the current standard */
+	res = tvp7002_get_video_mode(sd);
+	if (res == V4L2_STD_UNKNOWN)
+		return -EINVAL;
+	current_std = res;
+
+	/* check whether signal is locked */
+	sync_lock_status = tvp7002_read(sd, TVP7002_SYNC_DETECT_STAT);
+
+	if (0x02 != (sync_lock_status & 0xff))
+		return -EINVAL;	/* No input detected */
+
+	decoder->video_mode = current_std;
+	*std_id = current_std;
+
+	v4l2_info(sd, "Current STD: %d %d @ %d Hz\n",
+		tvp7002_resolutions[tvp7002_from_std(current_std)].hres,
+		tvp7002_resolutions[tvp7002_from_std(current_std)].vres,
+		tvp7002_resolutions[tvp7002_from_std(current_std)].frate);
+	return 0;
+}
+
+/*
+ * tvp7002_scanmode() - Returns whether format is progressive
+ * or interlaced
+ */
+
+
+/*
+ * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+	struct tvp7002 *decoder = to_tvp7002(sd);
+	struct v4l2_pix_format *pix;
+	v4l2_std_id current_std;
+	int res;
+
+	if (f == NULL)
+		return -EINVAL;
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		/* only capture is supported */
+		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	pix = &f->fmt.pix;
+
+	/* Calculate height and width based on current standard */
+	res = tvp7002_get_video_mode(sd);
+	if (res < 0)
+		return -EINVAL;
+	current_std = res;
+
+	decoder->video_mode = current_std;
+	pix->width = tvp7002_resolutions[tvp7002_from_std(current_std)].hres;
+	pix->height = tvp7002_resolutions[tvp7002_from_std(current_std)].vres;
+
+	pix->pixelformat = V4L2_PIX_FMT_UYVY;
+
+	pix->field = V4L2_FIELD_INTERLACED;
+	pix->bytesperline = pix->width * 2;
+	pix->sizeimage = pix->bytesperline * pix->height;
+	pix->colorspace = V4L2_COLORSPACE_REC709;
+	pix->priv = 0;
+
+	v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
+			"Width - %d, Height - %d",
+			"8-bit UYVY 4:2:2 Format", pix->bytesperline,
+			pix->width, pix->height);
+	return 0;
+}
+
+/**
+ * tvp7002_s_fmt_cap() - V4L2 decoder interface handler for s_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int tvp7002_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+	struct tvp7002 *decoder = to_tvp7002(sd);
+	struct v4l2_pix_format *pix;
+	int rval;
+
+	if (f == NULL)
+		return -EINVAL;
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		/* only capture is supported */
+		return -EINVAL;
+
+	pix = &f->fmt.pix;
+	rval = tvp7002_try_fmt_cap(sd, f);
+	if (rval)
+		return rval;
+
+		decoder->pix = *pix;
+
+	return rval;
+}
+
+/**
+ * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+	struct tvp7002 *decoder = to_tvp7002(sd);
+
+	if (f == NULL)
+		return -EINVAL;
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		/* only capture is supported */
+		return -EINVAL;
+
+	f->fmt.pix = decoder->pix;
+
+	v4l2_info(sd, "Current FMT: bytesperline - %d"
+			"Width - %d, Height - %d",
+			decoder->pix.bytesperline,
+			decoder->pix.width, decoder->pix.height);
+	return 0;
+}
+
+/*
+ * tvp7002_s_ctrl() - Set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Set a control for a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct tvp7002 *decoder = to_tvp7002(sd);
+	int vmd = 0;
+
+	decoder->video_mode = std;
+	vmd = tvp7002_from_std(std);
+
+	v4l2_info(sd, "Set video std mode to %d.\n", (int)std);
+
+	return tvp7002_set_video_mode(sd, vmd);
+}
+
+/*
+ * tvp7002_g_ctrl() - Get a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Get a control for a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	int rval, gval, bval;
+	int res;
+
+	v4l2_info(sd, "g_ctrl called\n");
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		rval = tvp7002_read(sd, TVP7002_R_FINE_GAIN);
+		gval = tvp7002_read(sd, TVP7002_G_FINE_GAIN);
+		bval = tvp7002_read(sd, TVP7002_B_FINE_GAIN);
+
+		if (rval < 0 || gval < 0 || bval < 0) {
+			res = -1;
+		} else if (rval != gval || rval != bval) {
+			res = -1;
+		} else {
+			ctrl->value = rval & 0x0F;
+			res = ctrl->value;
+		}
+		break;
+	default:
+		res = -1;
+		break;
+	}
+
+	return res < 0 ? res : 0;
+}
+
+/*
+ * tvp7002_s_ctrl() - Set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Set a control in TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	int rval, gval, bval;
+	int error;
+	u8 i, n;
+	n = ARRAY_SIZE(tvp7002_qctrl);
+
+	for (i = 0; i < n; i++)
+		if (ctrl->id == tvp7002_qctrl[i].id)
+			break;
+
+	if (i == n)
+		return -EINVAL;
+
+	if (ctrl->value < tvp7002_qctrl[i].minimum ||
+			ctrl->value > tvp7002_qctrl[i].maximum)
+		return -ERANGE;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		rval = tvp7002_write(sd, TVP7002_R_FINE_GAIN,
+							ctrl->value & 0xff);
+		gval = tvp7002_write(sd, TVP7002_G_FINE_GAIN,
+							ctrl->value & 0xff);
+		bval = tvp7002_write(sd, TVP7002_B_FINE_GAIN,
+							ctrl->value & 0xff);
+		if (rval < 0  || gval < 0 || bval < 0)
+			error = -1;
+		else
+			error = rval;
+		break;
+	default:
+		error = -1;
+		break;
+	}
+
+	if (error < 0)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+/*
+ * tvp7002_g_register() - Get the value of a register
+ * @sd: ptr to v4l2_subdev struct
+ * @vreg: ptr to v4l2_dbg_register struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful, -EINVAL if register read fails or
+ * access to I2C client fails, -EPERM if the call is not allowed
+ * by diabled CAP_SYS_ADMIN.
+ */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tvp7002_g_register(struct v4l2_subdev *sd,
+						struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int error;
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	reg->val = tvp7002_read(sd, reg->reg & 0xff);
+	reg->size = 1;
+
+	if (reg->val < 0)
+		error = -EINVAL;
+	else
+		error = 0;
+
+	return error;
+}
+
+/*
+ * tvp7002_s_register() - set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_s_register(struct v4l2_subdev *sd,
+						struct v4l2_dbg_register *reg)
+{
+	int error, wres;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
+
+	if (wres < 0)
+		error = -EINVAL;
+	else
+		error = 0;
+
+	return error;
+}
+#endif
+
+/*
+ * tvp7002_queryctrl() - Query a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_queryctrl struct
+ *
+ * Query a control of a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+	int i, error;
+	error = -EINVAL;
+
+	v4l2_info(sd, "queryctrl called\n");
+
+	for (i = 0; i < ARRAY_SIZE(tvp7002_qctrl); i++)
+		if (qc->id && qc->id == tvp7002_qctrl[i].id) {
+			memcpy(qc, &(tvp7002_qctrl[i]), sizeof(*qc));
+			error = 0;
+			break;
+		}
+
+	return error;
+}
+
+/*
+ * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	int err = 0;
+	struct tvp7002 *decoder = to_tvp7002(sd);
+
+	if (decoder->streaming == enable)
+		return 0;
+
+	if (enable) {
+		/* Power Up Sequence */
+		err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x00);
+		if (err) {
+			v4l2_err(sd, "Unable to turn on decoder\n");
+			err = -EINVAL;
+		}
+		err = tvp7002_write_inittab(sd, tvp7002_init_default);
+		if (err < 0) {
+			v4l2_err(sd, "Unable to initialize\n");
+			err = -EINVAL;
+		}
+		/* Detect if not already detected */
+		err = tvp7002_read(sd, TVP7002_CHIP_REV);
+		if (err < 0) {
+			v4l2_err(sd, "Unable to detect decoder\n");
+			err = -EINVAL;
+		}
+		decoder->streaming = enable;
+	} else {
+		/* Power Down Sequence */
+		err = tvp7002_write(sd, TVP7002_PWR_CTL, 0x40);
+		if (err) {
+			v4l2_err(sd, "Unable to turn off decoder\n");
+			return err;
+		}
+		decoder->streaming = enable;
+	}
+
+	return err;
+}
+
+/* Specific video subsystem operation handlers */
+static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
+	.querystd = tvp7002_querystd,
+	.s_stream = tvp7002_s_stream,
+	.g_fmt = tvp7002_g_fmt,
+};
+
+/* V4L2 Operations handlers */
+static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
+	.g_chip_ident = tvp7002_g_chip_ident,
+	.log_status = tvp7002_log_status,
+	.g_ctrl = tvp7002_g_ctrl,
+	.s_ctrl = tvp7002_s_ctrl,
+	.queryctrl = tvp7002_queryctrl,
+	.s_std = tvp7002_s_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = tvp7002_g_register,
+	.s_register = tvp7002_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_ops tvp7002_ops = {
+	.core = &tvp7002_core_ops,
+	.video = &tvp7002_video_ops,
+};
+
+/*
+ * tvp7002_reset - Reset a TVP7002 device
+ * @sd: ptr to v4l2_subdev struct
+ * @val: unsigned integer (not used)
+ *
+ * Reset the TVP7002 device
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_reset(struct v4l2_subdev *sd, u32 val)
+{
+	int polarity;
+	int error;
+
+	error = tvp7002_read(sd, TVP7002_CHIP_REV);
+	if (error < 0) {
+		error = -EINVAL;
+		goto found_error;
+	}
+
+	if (error == 0x02) {
+		v4l2_info(sd, "rev. %02x detected.\n", error);
+	} else {
+		v4l2_info(sd, "unknown revision detected.\n");
+		v4l2_info(sd, "revision number is %02x\n", error);
+	}
+
+	/* Set polarity information */
+	polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
+			tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
+	if (error < 0) {
+		error = -EINVAL;
+		goto found_error;
+	}
+
+	/* Initializes TVP7002 to its default values */
+	error = tvp7002_write_inittab(sd, tvp7002_init_default);
+	if (error < 0) {
+		error = -EINVAL;
+		goto found_error;
+	}
+
+found_error:
+	return error;
+};
+
+/*
+ * tvp7002_probe - Reset a TVP7002 device
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to i2c_device_id struct
+ *
+ * Reset the TVP7002 device
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+	struct v4l2_subdev *sd;
+	struct tvp7002 *core;
+	int polarity;
+	int error;
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(c->adapter,
+		I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		return -EIO;
+
+	core = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
+
+	if (!core)
+		return -ENOMEM;
+
+	sd = &core->sd;
+	v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
+	v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
+					c->addr << 1, c->adapter->name);
+
+	/* Set polarity information */
+	polarity = tvp7002_pdata.clk_polarity & tvp7002_pdata.hs_polarity &
+			tvp7002_pdata.vs_polarity & tvp7002_pdata.fid_polarity;
+	error = tvp7002_write(sd, TVP7002_MISC_CTL_2, polarity);
+	if (error < 0) {
+		error = -EINVAL;
+		goto found_error;
+	}
+
+	/* Set video mode */
+	core->video_mode = V4L2_STD_525P_60;
+
+	if (debug > 1)
+		error = tvp7002_log_status(sd);
+	else
+		error = 0;
+
+found_error:
+	if (error < 0)
+		kfree(core);
+
+	return error;
+}
+
+/*
+ * tvp7002_remove - Remove TVP7002 device support
+ * @c: ptr to i2c_client struct
+ *
+ * Reset the TVP7002 device
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_remove(struct i2c_client *c)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(c);
+
+	v4l2_dbg(1, debug, sd, "tvp7002.c: removing tvp7002 adapter"
+				"on address 0x%x\n", c->addr << 1);
+
+	v4l2_device_unregister_subdev(sd);
+	kfree(to_tvp7002(sd));
+	return 0;
+}
+
+/* I2C Device ID table */
+static const struct i2c_device_id tvp7002_id[] = {
+	{ "tvp7002", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tvp7002_id);
+
+/* I2C driver data */
+static struct i2c_driver tvp7002_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "tvp7002",
+	},
+	.probe = tvp7002_probe,
+	.remove = tvp7002_remove,
+	.id_table = tvp7002_id,
+};