Message ID | 1251845698-18150-1-git-send-email-santiago.nunez@ridgerun.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
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, ®->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, ®->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
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, ®->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, ®->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, 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, ®->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, ®->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 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, ®->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, ®->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 --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, ®->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, ®->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, +};