1cb7a01acSMauro Carvalho Chehab /* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics 2cb7a01acSMauro Carvalho Chehab * Digitizer with Horizontal PLL registers 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2009 Texas Instruments Inc 5cb7a01acSMauro Carvalho Chehab * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com> 6cb7a01acSMauro Carvalho Chehab * 7cb7a01acSMauro Carvalho Chehab * This code is partially based upon the TVP5150 driver 832590819SMauro Carvalho Chehab * written by Mauro Carvalho Chehab <mchehab@kernel.org>, 9cb7a01acSMauro Carvalho Chehab * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com> 10cb7a01acSMauro Carvalho Chehab * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by 11cb7a01acSMauro Carvalho Chehab * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). 12cb7a01acSMauro Carvalho Chehab * 13cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 14cb7a01acSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 15cb7a01acSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 16cb7a01acSMauro Carvalho Chehab * (at your option) any later version. 17cb7a01acSMauro Carvalho Chehab * 18cb7a01acSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 19cb7a01acSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 20cb7a01acSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21cb7a01acSMauro Carvalho Chehab * GNU General Public License for more details. 22cb7a01acSMauro Carvalho Chehab */ 23cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 25cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 26cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 27cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 28b91670a0SSachin Kamat #include <linux/of.h> 29fd9fdb78SPhilipp Zabel #include <linux/of_graph.h> 30cb7a01acSMauro Carvalho Chehab #include <linux/v4l2-dv-timings.h> 31b5dcee22SMauro Carvalho Chehab #include <media/i2c/tvp7002.h> 3225ba2c80SLad, Prabhakar #include <media/v4l2-async.h> 33cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 34cb7a01acSMauro Carvalho Chehab #include <media/v4l2-common.h> 35cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 36859969b3SSakari Ailus #include <media/v4l2-fwnode.h> 37c0d9644fSLad, Prabhakar 38cb7a01acSMauro Carvalho Chehab #include "tvp7002_reg.h" 39cb7a01acSMauro Carvalho Chehab 40cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); 41cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>"); 42cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 43cb7a01acSMauro Carvalho Chehab 44cb7a01acSMauro Carvalho Chehab /* I2C retry attempts */ 45cb7a01acSMauro Carvalho Chehab #define I2C_RETRY_COUNT (5) 46cb7a01acSMauro Carvalho Chehab 47cb7a01acSMauro Carvalho Chehab /* End of registers */ 48cb7a01acSMauro Carvalho Chehab #define TVP7002_EOR 0x5c 49cb7a01acSMauro Carvalho Chehab 50cb7a01acSMauro Carvalho Chehab /* Read write definition for registers */ 51cb7a01acSMauro Carvalho Chehab #define TVP7002_READ 0 52cb7a01acSMauro Carvalho Chehab #define TVP7002_WRITE 1 53cb7a01acSMauro Carvalho Chehab #define TVP7002_RESERVED 2 54cb7a01acSMauro Carvalho Chehab 55cb7a01acSMauro Carvalho Chehab /* Interlaced vs progressive mask and shift */ 56cb7a01acSMauro Carvalho Chehab #define TVP7002_IP_SHIFT 5 57cb7a01acSMauro Carvalho Chehab #define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) 58cb7a01acSMauro Carvalho Chehab 59cb7a01acSMauro Carvalho Chehab /* Shift for CPL and LPF registers */ 60cb7a01acSMauro Carvalho Chehab #define TVP7002_CL_SHIFT 8 61cb7a01acSMauro Carvalho Chehab #define TVP7002_CL_MASK 0x0f 62cb7a01acSMauro Carvalho Chehab 63cb7a01acSMauro Carvalho Chehab /* Debug functions */ 64cb7a01acSMauro Carvalho Chehab static bool debug; 65cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644); 66cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-2)"); 67cb7a01acSMauro Carvalho Chehab 68cb7a01acSMauro Carvalho Chehab /* Structure for register values */ 69cb7a01acSMauro Carvalho Chehab struct i2c_reg_value { 70cb7a01acSMauro Carvalho Chehab u8 reg; 71cb7a01acSMauro Carvalho Chehab u8 value; 72cb7a01acSMauro Carvalho Chehab u8 type; 73cb7a01acSMauro Carvalho Chehab }; 74cb7a01acSMauro Carvalho Chehab 75cb7a01acSMauro Carvalho Chehab /* 76cb7a01acSMauro Carvalho Chehab * Register default values (according to tvp7002 datasheet) 77cb7a01acSMauro Carvalho Chehab * In the case of read-only registers, the value (0xff) is 78cb7a01acSMauro Carvalho Chehab * never written. R/W functionality is controlled by the 79cb7a01acSMauro Carvalho Chehab * writable bit in the register struct definition. 80cb7a01acSMauro Carvalho Chehab */ 81cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_init_default[] = { 82cb7a01acSMauro Carvalho Chehab { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, 83cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, 84cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, 85cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, 86cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, 87cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 88cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 89cb7a01acSMauro Carvalho Chehab { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, 90cb7a01acSMauro Carvalho Chehab { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, 91cb7a01acSMauro Carvalho Chehab { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, 92cb7a01acSMauro Carvalho Chehab { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, 93cb7a01acSMauro Carvalho Chehab { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 94cb7a01acSMauro Carvalho Chehab { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 95cb7a01acSMauro Carvalho Chehab { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 96cb7a01acSMauro Carvalho Chehab { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, 97cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, 98cb7a01acSMauro Carvalho Chehab { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, 99cb7a01acSMauro Carvalho Chehab { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, 100cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, 101cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 102cb7a01acSMauro Carvalho Chehab { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, 103cb7a01acSMauro Carvalho Chehab { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, 104cb7a01acSMauro Carvalho Chehab { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, 105cb7a01acSMauro Carvalho Chehab { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, 106cb7a01acSMauro Carvalho Chehab { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, 107cb7a01acSMauro Carvalho Chehab { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, 108cb7a01acSMauro Carvalho Chehab { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, 109cb7a01acSMauro Carvalho Chehab { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, 110cb7a01acSMauro Carvalho Chehab { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, 111cb7a01acSMauro Carvalho Chehab { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, 112cb7a01acSMauro Carvalho Chehab { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, 113cb7a01acSMauro Carvalho Chehab { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, 114cb7a01acSMauro Carvalho Chehab { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, 115cb7a01acSMauro Carvalho Chehab { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, 116cb7a01acSMauro Carvalho Chehab { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, 117cb7a01acSMauro Carvalho Chehab { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 118cb7a01acSMauro Carvalho Chehab { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 119cb7a01acSMauro Carvalho Chehab { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 120cb7a01acSMauro Carvalho Chehab { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, 121cb7a01acSMauro Carvalho Chehab { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, 122cb7a01acSMauro Carvalho Chehab { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, 123cb7a01acSMauro Carvalho Chehab { 0x29, 0x08, TVP7002_RESERVED }, 124cb7a01acSMauro Carvalho Chehab { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, 125cb7a01acSMauro Carvalho Chehab /* PWR_CTL is controlled only by the probe and reset functions */ 126cb7a01acSMauro Carvalho Chehab { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, 127cb7a01acSMauro Carvalho Chehab { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, 128cb7a01acSMauro Carvalho Chehab { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, 129cb7a01acSMauro Carvalho Chehab { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, 130cb7a01acSMauro Carvalho Chehab { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, 131cb7a01acSMauro Carvalho Chehab { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, 132cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 133cb7a01acSMauro Carvalho Chehab { 0x32, 0x18, TVP7002_RESERVED }, 134cb7a01acSMauro Carvalho Chehab { 0x33, 0x60, TVP7002_RESERVED }, 135cb7a01acSMauro Carvalho Chehab { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, 136cb7a01acSMauro Carvalho Chehab { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, 137cb7a01acSMauro Carvalho Chehab { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, 138cb7a01acSMauro Carvalho Chehab { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, 139cb7a01acSMauro Carvalho Chehab { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, 140cb7a01acSMauro Carvalho Chehab { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, 141cb7a01acSMauro Carvalho Chehab { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, 142cb7a01acSMauro Carvalho Chehab { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, 143cb7a01acSMauro Carvalho Chehab { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, 144cb7a01acSMauro Carvalho Chehab { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, 145cb7a01acSMauro Carvalho Chehab { 0x3e, 0x60, TVP7002_RESERVED }, 146cb7a01acSMauro Carvalho Chehab { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, 147cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, 148cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, 149cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 150cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, 151cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 152cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 153cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, 154cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 155cb7a01acSMauro Carvalho Chehab { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, 156cb7a01acSMauro Carvalho Chehab { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 157cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, 158cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, 159cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, 160cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, 161cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, 162cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, 163cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, 164cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, 165cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, 166cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, 167cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, 168cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, 169cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, 170cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, 171cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, 172cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, 173cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, 174cb7a01acSMauro Carvalho Chehab { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, 175cb7a01acSMauro Carvalho Chehab /* This signals end of register values */ 176cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 177cb7a01acSMauro Carvalho Chehab }; 178cb7a01acSMauro Carvalho Chehab 179cb7a01acSMauro Carvalho Chehab /* Register parameters for 480P */ 180cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_480P[] = { 181cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, 182cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, 183cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, 184cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, 185cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 186cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, 187cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 188cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, 189cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, 190cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, 191cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, 192cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, 193cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, 194cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, 195cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, 196cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, 197cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 198cb7a01acSMauro Carvalho Chehab }; 199cb7a01acSMauro Carvalho Chehab 200cb7a01acSMauro Carvalho Chehab /* Register parameters for 576P */ 201cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_576P[] = { 202cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, 203cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, 204cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, 205cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, 206cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 207cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, 208cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 209cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, 210cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 211cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 212cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 213cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, 214cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, 215cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, 216cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, 217cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, 218cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 219cb7a01acSMauro Carvalho Chehab }; 220cb7a01acSMauro Carvalho Chehab 221cb7a01acSMauro Carvalho Chehab /* Register parameters for 1080I60 */ 222cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_1080I60[] = { 223cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, 224cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, 225cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 226cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 227cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 228cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 229cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 230cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 231cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 232cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 233cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 234cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 235cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 236cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 237cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 238cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 239cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 240cb7a01acSMauro Carvalho Chehab }; 241cb7a01acSMauro Carvalho Chehab 242cb7a01acSMauro Carvalho Chehab /* Register parameters for 1080P60 */ 243cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_1080P60[] = { 244cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, 245cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, 246cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, 247cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 248cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 249cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 250cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 251cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 252cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 253cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 254cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 255cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 256cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 257cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 258cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 259cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 260cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 261cb7a01acSMauro Carvalho Chehab }; 262cb7a01acSMauro Carvalho Chehab 263cb7a01acSMauro Carvalho Chehab /* Register parameters for 1080I50 */ 264cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_1080I50[] = { 265cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, 266cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, 267cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 268cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 269cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 270cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 271cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 272cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 273cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 274cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 275cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 276cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 277cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 278cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 279cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 280cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 281cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 282cb7a01acSMauro Carvalho Chehab }; 283cb7a01acSMauro Carvalho Chehab 284cb7a01acSMauro Carvalho Chehab /* Register parameters for 720P60 */ 285cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_720P60[] = { 286cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, 287cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, 288cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, 289cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, 290cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 291cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, 292cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, 293cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 294cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 295cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 296cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 297cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 298cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 299cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 300cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, 301cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 302cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 303cb7a01acSMauro Carvalho Chehab }; 304cb7a01acSMauro Carvalho Chehab 305cb7a01acSMauro Carvalho Chehab /* Register parameters for 720P50 */ 306cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp7002_parms_720P50[] = { 307cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, 308cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, 309cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 310cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, 311cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 312cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, 313cb7a01acSMauro Carvalho Chehab { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, 314cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 315cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 316cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 317cb7a01acSMauro Carvalho Chehab { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 318cb7a01acSMauro Carvalho Chehab { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 319cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 320cb7a01acSMauro Carvalho Chehab { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 321cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 322cb7a01acSMauro Carvalho Chehab { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 323cb7a01acSMauro Carvalho Chehab { TVP7002_EOR, 0xff, TVP7002_RESERVED } 324cb7a01acSMauro Carvalho Chehab }; 325cb7a01acSMauro Carvalho Chehab 326f0cd015eSHans Verkuil /* Timings definition for handling device operation */ 327f0cd015eSHans Verkuil struct tvp7002_timings_definition { 328cb7a01acSMauro Carvalho Chehab struct v4l2_dv_timings timings; 329cb7a01acSMauro Carvalho Chehab const struct i2c_reg_value *p_settings; 330cb7a01acSMauro Carvalho Chehab enum v4l2_colorspace color_space; 331cb7a01acSMauro Carvalho Chehab enum v4l2_field scanmode; 332cb7a01acSMauro Carvalho Chehab u16 progressive; 333cb7a01acSMauro Carvalho Chehab u16 lines_per_frame; 334cb7a01acSMauro Carvalho Chehab u16 cpl_min; 335cb7a01acSMauro Carvalho Chehab u16 cpl_max; 336cb7a01acSMauro Carvalho Chehab }; 337cb7a01acSMauro Carvalho Chehab 338f0cd015eSHans Verkuil /* Struct list for digital video timings */ 339f0cd015eSHans Verkuil static const struct tvp7002_timings_definition tvp7002_timings[] = { 340cb7a01acSMauro Carvalho Chehab { 341cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_1280X720P60, 342cb7a01acSMauro Carvalho Chehab tvp7002_parms_720P60, 343cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_REC709, 344cb7a01acSMauro Carvalho Chehab V4L2_FIELD_NONE, 345cb7a01acSMauro Carvalho Chehab 1, 346cb7a01acSMauro Carvalho Chehab 0x2EE, 347cb7a01acSMauro Carvalho Chehab 135, 348cb7a01acSMauro Carvalho Chehab 153 349cb7a01acSMauro Carvalho Chehab }, 350cb7a01acSMauro Carvalho Chehab { 351cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_1920X1080I60, 352cb7a01acSMauro Carvalho Chehab tvp7002_parms_1080I60, 353cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_REC709, 354cb7a01acSMauro Carvalho Chehab V4L2_FIELD_INTERLACED, 355cb7a01acSMauro Carvalho Chehab 0, 356cb7a01acSMauro Carvalho Chehab 0x465, 357cb7a01acSMauro Carvalho Chehab 181, 358cb7a01acSMauro Carvalho Chehab 205 359cb7a01acSMauro Carvalho Chehab }, 360cb7a01acSMauro Carvalho Chehab { 361cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_1920X1080I50, 362cb7a01acSMauro Carvalho Chehab tvp7002_parms_1080I50, 363cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_REC709, 364cb7a01acSMauro Carvalho Chehab V4L2_FIELD_INTERLACED, 365cb7a01acSMauro Carvalho Chehab 0, 366cb7a01acSMauro Carvalho Chehab 0x465, 367cb7a01acSMauro Carvalho Chehab 217, 368cb7a01acSMauro Carvalho Chehab 245 369cb7a01acSMauro Carvalho Chehab }, 370cb7a01acSMauro Carvalho Chehab { 371cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_1280X720P50, 372cb7a01acSMauro Carvalho Chehab tvp7002_parms_720P50, 373cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_REC709, 374cb7a01acSMauro Carvalho Chehab V4L2_FIELD_NONE, 375cb7a01acSMauro Carvalho Chehab 1, 376cb7a01acSMauro Carvalho Chehab 0x2EE, 377cb7a01acSMauro Carvalho Chehab 163, 378cb7a01acSMauro Carvalho Chehab 183 379cb7a01acSMauro Carvalho Chehab }, 380cb7a01acSMauro Carvalho Chehab { 381cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_1920X1080P60, 382cb7a01acSMauro Carvalho Chehab tvp7002_parms_1080P60, 383cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_REC709, 384cb7a01acSMauro Carvalho Chehab V4L2_FIELD_NONE, 385cb7a01acSMauro Carvalho Chehab 1, 386cb7a01acSMauro Carvalho Chehab 0x465, 387cb7a01acSMauro Carvalho Chehab 90, 388cb7a01acSMauro Carvalho Chehab 102 389cb7a01acSMauro Carvalho Chehab }, 390cb7a01acSMauro Carvalho Chehab { 391cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_720X480P59_94, 392cb7a01acSMauro Carvalho Chehab tvp7002_parms_480P, 393cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 394cb7a01acSMauro Carvalho Chehab V4L2_FIELD_NONE, 395cb7a01acSMauro Carvalho Chehab 1, 396cb7a01acSMauro Carvalho Chehab 0x20D, 397cb7a01acSMauro Carvalho Chehab 0xffff, 398cb7a01acSMauro Carvalho Chehab 0xffff 399cb7a01acSMauro Carvalho Chehab }, 400cb7a01acSMauro Carvalho Chehab { 401cb7a01acSMauro Carvalho Chehab V4L2_DV_BT_CEA_720X576P50, 402cb7a01acSMauro Carvalho Chehab tvp7002_parms_576P, 403cb7a01acSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 404cb7a01acSMauro Carvalho Chehab V4L2_FIELD_NONE, 405cb7a01acSMauro Carvalho Chehab 1, 406cb7a01acSMauro Carvalho Chehab 0x271, 407cb7a01acSMauro Carvalho Chehab 0xffff, 408cb7a01acSMauro Carvalho Chehab 0xffff 409cb7a01acSMauro Carvalho Chehab } 410cb7a01acSMauro Carvalho Chehab }; 411cb7a01acSMauro Carvalho Chehab 412f0cd015eSHans Verkuil #define NUM_TIMINGS ARRAY_SIZE(tvp7002_timings) 413cb7a01acSMauro Carvalho Chehab 414cb7a01acSMauro Carvalho Chehab /* Device definition */ 415cb7a01acSMauro Carvalho Chehab struct tvp7002 { 416cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 417cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler hdl; 418cb7a01acSMauro Carvalho Chehab const struct tvp7002_config *pdata; 419cb7a01acSMauro Carvalho Chehab 420cb7a01acSMauro Carvalho Chehab int ver; 421cb7a01acSMauro Carvalho Chehab int streaming; 422cb7a01acSMauro Carvalho Chehab 423f0cd015eSHans Verkuil const struct tvp7002_timings_definition *current_timings; 42463eb2ca1SLad, Prabhakar struct media_pad pad; 425cb7a01acSMauro Carvalho Chehab }; 426cb7a01acSMauro Carvalho Chehab 427cb7a01acSMauro Carvalho Chehab /* 428cb7a01acSMauro Carvalho Chehab * to_tvp7002 - Obtain device handler TVP7002 429cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 430cb7a01acSMauro Carvalho Chehab * 431cb7a01acSMauro Carvalho Chehab * Returns device handler tvp7002. 432cb7a01acSMauro Carvalho Chehab */ 433cb7a01acSMauro Carvalho Chehab static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) 434cb7a01acSMauro Carvalho Chehab { 435cb7a01acSMauro Carvalho Chehab return container_of(sd, struct tvp7002, sd); 436cb7a01acSMauro Carvalho Chehab } 437cb7a01acSMauro Carvalho Chehab 438cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 439cb7a01acSMauro Carvalho Chehab { 440cb7a01acSMauro Carvalho Chehab return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; 441cb7a01acSMauro Carvalho Chehab } 442cb7a01acSMauro Carvalho Chehab 443cb7a01acSMauro Carvalho Chehab /* 444cb7a01acSMauro Carvalho Chehab * tvp7002_read - Read a value from a register in an TVP7002 445cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 446cb7a01acSMauro Carvalho Chehab * @addr: TVP7002 register address 447cb7a01acSMauro Carvalho Chehab * @dst: pointer to 8-bit destination 448cb7a01acSMauro Carvalho Chehab * 449cb7a01acSMauro Carvalho Chehab * Returns value read if successful, or non-zero (-1) otherwise. 450cb7a01acSMauro Carvalho Chehab */ 451cb7a01acSMauro Carvalho Chehab static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) 452cb7a01acSMauro Carvalho Chehab { 453cb7a01acSMauro Carvalho Chehab struct i2c_client *c = v4l2_get_subdevdata(sd); 454cb7a01acSMauro Carvalho Chehab int retry; 455cb7a01acSMauro Carvalho Chehab int error; 456cb7a01acSMauro Carvalho Chehab 457cb7a01acSMauro Carvalho Chehab for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { 458cb7a01acSMauro Carvalho Chehab error = i2c_smbus_read_byte_data(c, addr); 459cb7a01acSMauro Carvalho Chehab 460cb7a01acSMauro Carvalho Chehab if (error >= 0) { 461cb7a01acSMauro Carvalho Chehab *dst = (u8)error; 462cb7a01acSMauro Carvalho Chehab return 0; 463cb7a01acSMauro Carvalho Chehab } 464cb7a01acSMauro Carvalho Chehab 465cb7a01acSMauro Carvalho Chehab msleep_interruptible(10); 466cb7a01acSMauro Carvalho Chehab } 467cb7a01acSMauro Carvalho Chehab v4l2_err(sd, "TVP7002 read error %d\n", error); 468cb7a01acSMauro Carvalho Chehab return error; 469cb7a01acSMauro Carvalho Chehab } 470cb7a01acSMauro Carvalho Chehab 471cb7a01acSMauro Carvalho Chehab /* 472cb7a01acSMauro Carvalho Chehab * tvp7002_read_err() - Read a register value with error code 473cb7a01acSMauro Carvalho Chehab * @sd: pointer to standard V4L2 sub-device structure 474cb7a01acSMauro Carvalho Chehab * @reg: destination register 475cb7a01acSMauro Carvalho Chehab * @val: value to be read 476cb7a01acSMauro Carvalho Chehab * @err: pointer to error value 477cb7a01acSMauro Carvalho Chehab * 478cb7a01acSMauro Carvalho Chehab * Read a value in a register and save error value in pointer. 479cb7a01acSMauro Carvalho Chehab * Also update the register table if successful 480cb7a01acSMauro Carvalho Chehab */ 481cb7a01acSMauro Carvalho Chehab static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, 482cb7a01acSMauro Carvalho Chehab u8 *dst, int *err) 483cb7a01acSMauro Carvalho Chehab { 484cb7a01acSMauro Carvalho Chehab if (!*err) 485cb7a01acSMauro Carvalho Chehab *err = tvp7002_read(sd, reg, dst); 486cb7a01acSMauro Carvalho Chehab } 487cb7a01acSMauro Carvalho Chehab 488cb7a01acSMauro Carvalho Chehab /* 489cb7a01acSMauro Carvalho Chehab * tvp7002_write() - Write a value to a register in TVP7002 490cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 491cb7a01acSMauro Carvalho Chehab * @addr: TVP7002 register address 492cb7a01acSMauro Carvalho Chehab * @value: value to be written to the register 493cb7a01acSMauro Carvalho Chehab * 494cb7a01acSMauro Carvalho Chehab * Write a value to a register in an TVP7002 decoder device. 495cb7a01acSMauro Carvalho Chehab * Returns zero if successful, or non-zero otherwise. 496cb7a01acSMauro Carvalho Chehab */ 497cb7a01acSMauro Carvalho Chehab static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) 498cb7a01acSMauro Carvalho Chehab { 499cb7a01acSMauro Carvalho Chehab struct i2c_client *c; 500cb7a01acSMauro Carvalho Chehab int retry; 501cb7a01acSMauro Carvalho Chehab int error; 502cb7a01acSMauro Carvalho Chehab 503cb7a01acSMauro Carvalho Chehab c = v4l2_get_subdevdata(sd); 504cb7a01acSMauro Carvalho Chehab 505cb7a01acSMauro Carvalho Chehab for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { 506cb7a01acSMauro Carvalho Chehab error = i2c_smbus_write_byte_data(c, addr, value); 507cb7a01acSMauro Carvalho Chehab 508cb7a01acSMauro Carvalho Chehab if (error >= 0) 509cb7a01acSMauro Carvalho Chehab return 0; 510cb7a01acSMauro Carvalho Chehab 511cb7a01acSMauro Carvalho Chehab v4l2_warn(sd, "Write: retry ... %d\n", retry); 512cb7a01acSMauro Carvalho Chehab msleep_interruptible(10); 513cb7a01acSMauro Carvalho Chehab } 514cb7a01acSMauro Carvalho Chehab v4l2_err(sd, "TVP7002 write error %d\n", error); 515cb7a01acSMauro Carvalho Chehab return error; 516cb7a01acSMauro Carvalho Chehab } 517cb7a01acSMauro Carvalho Chehab 518cb7a01acSMauro Carvalho Chehab /* 519cb7a01acSMauro Carvalho Chehab * tvp7002_write_err() - Write a register value with error code 520cb7a01acSMauro Carvalho Chehab * @sd: pointer to standard V4L2 sub-device structure 521cb7a01acSMauro Carvalho Chehab * @reg: destination register 522cb7a01acSMauro Carvalho Chehab * @val: value to be written 523cb7a01acSMauro Carvalho Chehab * @err: pointer to error value 524cb7a01acSMauro Carvalho Chehab * 525cb7a01acSMauro Carvalho Chehab * Write a value in a register and save error value in pointer. 526cb7a01acSMauro Carvalho Chehab * Also update the register table if successful 527cb7a01acSMauro Carvalho Chehab */ 528cb7a01acSMauro Carvalho Chehab static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, 529cb7a01acSMauro Carvalho Chehab u8 val, int *err) 530cb7a01acSMauro Carvalho Chehab { 531cb7a01acSMauro Carvalho Chehab if (!*err) 532cb7a01acSMauro Carvalho Chehab *err = tvp7002_write(sd, reg, val); 533cb7a01acSMauro Carvalho Chehab } 534cb7a01acSMauro Carvalho Chehab 535cb7a01acSMauro Carvalho Chehab /* 536cb7a01acSMauro Carvalho Chehab * tvp7002_write_inittab() - Write initialization values 537cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 538cb7a01acSMauro Carvalho Chehab * @regs: ptr to i2c_reg_value struct 539cb7a01acSMauro Carvalho Chehab * 540cb7a01acSMauro Carvalho Chehab * Write initialization values. 541cb7a01acSMauro Carvalho Chehab * Returns zero or -EINVAL if read operation fails. 542cb7a01acSMauro Carvalho Chehab */ 543cb7a01acSMauro Carvalho Chehab static int tvp7002_write_inittab(struct v4l2_subdev *sd, 544cb7a01acSMauro Carvalho Chehab const struct i2c_reg_value *regs) 545cb7a01acSMauro Carvalho Chehab { 546cb7a01acSMauro Carvalho Chehab int error = 0; 547cb7a01acSMauro Carvalho Chehab 548cb7a01acSMauro Carvalho Chehab /* Initialize the first (defined) registers */ 549cb7a01acSMauro Carvalho Chehab while (TVP7002_EOR != regs->reg) { 550cb7a01acSMauro Carvalho Chehab if (TVP7002_WRITE == regs->type) 551cb7a01acSMauro Carvalho Chehab tvp7002_write_err(sd, regs->reg, regs->value, &error); 552cb7a01acSMauro Carvalho Chehab regs++; 553cb7a01acSMauro Carvalho Chehab } 554cb7a01acSMauro Carvalho Chehab 555cb7a01acSMauro Carvalho Chehab return error; 556cb7a01acSMauro Carvalho Chehab } 557cb7a01acSMauro Carvalho Chehab 558cb7a01acSMauro Carvalho Chehab static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, 559cb7a01acSMauro Carvalho Chehab struct v4l2_dv_timings *dv_timings) 560cb7a01acSMauro Carvalho Chehab { 561cb7a01acSMauro Carvalho Chehab struct tvp7002 *device = to_tvp7002(sd); 562cb7a01acSMauro Carvalho Chehab const struct v4l2_bt_timings *bt = &dv_timings->bt; 563cb7a01acSMauro Carvalho Chehab int i; 564cb7a01acSMauro Carvalho Chehab 565cb7a01acSMauro Carvalho Chehab if (dv_timings->type != V4L2_DV_BT_656_1120) 566cb7a01acSMauro Carvalho Chehab return -EINVAL; 567f0cd015eSHans Verkuil for (i = 0; i < NUM_TIMINGS; i++) { 568f0cd015eSHans Verkuil const struct v4l2_bt_timings *t = &tvp7002_timings[i].timings.bt; 569cb7a01acSMauro Carvalho Chehab 570cb7a01acSMauro Carvalho Chehab if (!memcmp(bt, t, &bt->standards - &bt->width)) { 571f0cd015eSHans Verkuil device->current_timings = &tvp7002_timings[i]; 572f0cd015eSHans Verkuil return tvp7002_write_inittab(sd, tvp7002_timings[i].p_settings); 573cb7a01acSMauro Carvalho Chehab } 574cb7a01acSMauro Carvalho Chehab } 575cb7a01acSMauro Carvalho Chehab return -EINVAL; 576cb7a01acSMauro Carvalho Chehab } 577cb7a01acSMauro Carvalho Chehab 578cb7a01acSMauro Carvalho Chehab static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, 579cb7a01acSMauro Carvalho Chehab struct v4l2_dv_timings *dv_timings) 580cb7a01acSMauro Carvalho Chehab { 581cb7a01acSMauro Carvalho Chehab struct tvp7002 *device = to_tvp7002(sd); 582cb7a01acSMauro Carvalho Chehab 583f0cd015eSHans Verkuil *dv_timings = device->current_timings->timings; 584cb7a01acSMauro Carvalho Chehab return 0; 585cb7a01acSMauro Carvalho Chehab } 586cb7a01acSMauro Carvalho Chehab 587cb7a01acSMauro Carvalho Chehab /* 588cb7a01acSMauro Carvalho Chehab * tvp7002_s_ctrl() - Set a control 589cb7a01acSMauro Carvalho Chehab * @ctrl: ptr to v4l2_ctrl struct 590cb7a01acSMauro Carvalho Chehab * 591cb7a01acSMauro Carvalho Chehab * Set a control in TVP7002 decoder device. 592cb7a01acSMauro Carvalho Chehab * Returns zero when successful or -EINVAL if register access fails. 593cb7a01acSMauro Carvalho Chehab */ 594cb7a01acSMauro Carvalho Chehab static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) 595cb7a01acSMauro Carvalho Chehab { 596cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = to_sd(ctrl); 597cb7a01acSMauro Carvalho Chehab int error = 0; 598cb7a01acSMauro Carvalho Chehab 599cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 600cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN: 601cb7a01acSMauro Carvalho Chehab tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); 602cb7a01acSMauro Carvalho Chehab tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); 603cb7a01acSMauro Carvalho Chehab tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); 604cb7a01acSMauro Carvalho Chehab return error; 605cb7a01acSMauro Carvalho Chehab } 606cb7a01acSMauro Carvalho Chehab return -EINVAL; 607cb7a01acSMauro Carvalho Chehab } 608cb7a01acSMauro Carvalho Chehab 609cb7a01acSMauro Carvalho Chehab /* 610f0cd015eSHans Verkuil * tvp7002_query_dv() - query DV timings 611cb7a01acSMauro Carvalho Chehab * @sd: pointer to standard V4L2 sub-device structure 612f0cd015eSHans Verkuil * @index: index into the tvp7002_timings array 613cb7a01acSMauro Carvalho Chehab * 614f0cd015eSHans Verkuil * Returns the current DV timings detected by TVP7002. If no active input is 615cb7a01acSMauro Carvalho Chehab * detected, returns -EINVAL 616cb7a01acSMauro Carvalho Chehab */ 617cb7a01acSMauro Carvalho Chehab static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) 618cb7a01acSMauro Carvalho Chehab { 619f0cd015eSHans Verkuil const struct tvp7002_timings_definition *timings = tvp7002_timings; 620cb7a01acSMauro Carvalho Chehab u8 progressive; 621cb7a01acSMauro Carvalho Chehab u32 lpfr; 622cb7a01acSMauro Carvalho Chehab u32 cpln; 623cb7a01acSMauro Carvalho Chehab int error = 0; 624cb7a01acSMauro Carvalho Chehab u8 lpf_lsb; 625cb7a01acSMauro Carvalho Chehab u8 lpf_msb; 626cb7a01acSMauro Carvalho Chehab u8 cpl_lsb; 627cb7a01acSMauro Carvalho Chehab u8 cpl_msb; 628cb7a01acSMauro Carvalho Chehab 629cb7a01acSMauro Carvalho Chehab /* Return invalid index if no active input is detected */ 630f0cd015eSHans Verkuil *index = NUM_TIMINGS; 631cb7a01acSMauro Carvalho Chehab 632cb7a01acSMauro Carvalho Chehab /* Read standards from device registers */ 633cb7a01acSMauro Carvalho Chehab tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); 634cb7a01acSMauro Carvalho Chehab tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); 635cb7a01acSMauro Carvalho Chehab 636cb7a01acSMauro Carvalho Chehab if (error < 0) 637cb7a01acSMauro Carvalho Chehab return error; 638cb7a01acSMauro Carvalho Chehab 639cb7a01acSMauro Carvalho Chehab tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); 640cb7a01acSMauro Carvalho Chehab tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); 641cb7a01acSMauro Carvalho Chehab 642cb7a01acSMauro Carvalho Chehab if (error < 0) 643cb7a01acSMauro Carvalho Chehab return error; 644cb7a01acSMauro Carvalho Chehab 645cb7a01acSMauro Carvalho Chehab /* Get lines per frame, clocks per line and interlaced/progresive */ 646cb7a01acSMauro Carvalho Chehab lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); 647cb7a01acSMauro Carvalho Chehab cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); 648cb7a01acSMauro Carvalho Chehab progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; 649cb7a01acSMauro Carvalho Chehab 650cb7a01acSMauro Carvalho Chehab /* Do checking of video modes */ 651f0cd015eSHans Verkuil for (*index = 0; *index < NUM_TIMINGS; (*index)++, timings++) 652f0cd015eSHans Verkuil if (lpfr == timings->lines_per_frame && 653f0cd015eSHans Verkuil progressive == timings->progressive) { 654f0cd015eSHans Verkuil if (timings->cpl_min == 0xffff) 655cb7a01acSMauro Carvalho Chehab break; 656f0cd015eSHans Verkuil if (cpln >= timings->cpl_min && cpln <= timings->cpl_max) 657cb7a01acSMauro Carvalho Chehab break; 658cb7a01acSMauro Carvalho Chehab } 659cb7a01acSMauro Carvalho Chehab 660f0cd015eSHans Verkuil if (*index == NUM_TIMINGS) { 661cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", 662cb7a01acSMauro Carvalho Chehab lpfr, cpln); 663cb7a01acSMauro Carvalho Chehab return -ENOLINK; 664cb7a01acSMauro Carvalho Chehab } 665cb7a01acSMauro Carvalho Chehab 666cb7a01acSMauro Carvalho Chehab /* Update lines per frame and clocks per line info */ 667f0cd015eSHans Verkuil v4l2_dbg(1, debug, sd, "detected timings: %d\n", *index); 668cb7a01acSMauro Carvalho Chehab return 0; 669cb7a01acSMauro Carvalho Chehab } 670cb7a01acSMauro Carvalho Chehab 671cb7a01acSMauro Carvalho Chehab static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, 672cb7a01acSMauro Carvalho Chehab struct v4l2_dv_timings *timings) 673cb7a01acSMauro Carvalho Chehab { 674cb7a01acSMauro Carvalho Chehab int index; 675cb7a01acSMauro Carvalho Chehab int err = tvp7002_query_dv(sd, &index); 676cb7a01acSMauro Carvalho Chehab 677cb7a01acSMauro Carvalho Chehab if (err) 678cb7a01acSMauro Carvalho Chehab return err; 679f0cd015eSHans Verkuil *timings = tvp7002_timings[index].timings; 680cb7a01acSMauro Carvalho Chehab return 0; 681cb7a01acSMauro Carvalho Chehab } 682cb7a01acSMauro Carvalho Chehab 683cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 684cb7a01acSMauro Carvalho Chehab /* 685cb7a01acSMauro Carvalho Chehab * tvp7002_g_register() - Get the value of a register 686cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 687cb7a01acSMauro Carvalho Chehab * @reg: ptr to v4l2_dbg_register struct 688cb7a01acSMauro Carvalho Chehab * 689cb7a01acSMauro Carvalho Chehab * Get the value of a TVP7002 decoder device register. 690cb7a01acSMauro Carvalho Chehab * Returns zero when successful, -EINVAL if register read fails or 6917e89bd9fSLad, Prabhakar * access to I2C client fails. 692cb7a01acSMauro Carvalho Chehab */ 693cb7a01acSMauro Carvalho Chehab static int tvp7002_g_register(struct v4l2_subdev *sd, 694cb7a01acSMauro Carvalho Chehab struct v4l2_dbg_register *reg) 695cb7a01acSMauro Carvalho Chehab { 696cb7a01acSMauro Carvalho Chehab u8 val; 697cb7a01acSMauro Carvalho Chehab int ret; 698cb7a01acSMauro Carvalho Chehab 699cb7a01acSMauro Carvalho Chehab ret = tvp7002_read(sd, reg->reg & 0xff, &val); 700cb7a01acSMauro Carvalho Chehab reg->val = val; 70115c4fee3SHans Verkuil reg->size = 1; 702cb7a01acSMauro Carvalho Chehab return ret; 703cb7a01acSMauro Carvalho Chehab } 704cb7a01acSMauro Carvalho Chehab 705cb7a01acSMauro Carvalho Chehab /* 706cb7a01acSMauro Carvalho Chehab * tvp7002_s_register() - set a control 707cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 708cb7a01acSMauro Carvalho Chehab * @reg: ptr to v4l2_dbg_register struct 709cb7a01acSMauro Carvalho Chehab * 710cb7a01acSMauro Carvalho Chehab * Get the value of a TVP7002 decoder device register. 7117e89bd9fSLad, Prabhakar * Returns zero when successful, -EINVAL if register read fails. 712cb7a01acSMauro Carvalho Chehab */ 713cb7a01acSMauro Carvalho Chehab static int tvp7002_s_register(struct v4l2_subdev *sd, 714977ba3b1SHans Verkuil const struct v4l2_dbg_register *reg) 715cb7a01acSMauro Carvalho Chehab { 716cb7a01acSMauro Carvalho Chehab return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); 717cb7a01acSMauro Carvalho Chehab } 718cb7a01acSMauro Carvalho Chehab #endif 719cb7a01acSMauro Carvalho Chehab 720cb7a01acSMauro Carvalho Chehab /* 721cb7a01acSMauro Carvalho Chehab * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream 722cb7a01acSMauro Carvalho Chehab * @sd: pointer to standard V4L2 sub-device structure 723cb7a01acSMauro Carvalho Chehab * @enable: streaming enable or disable 724cb7a01acSMauro Carvalho Chehab * 725cb7a01acSMauro Carvalho Chehab * Sets streaming to enable or disable, if possible. 726cb7a01acSMauro Carvalho Chehab */ 727cb7a01acSMauro Carvalho Chehab static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) 728cb7a01acSMauro Carvalho Chehab { 729cb7a01acSMauro Carvalho Chehab struct tvp7002 *device = to_tvp7002(sd); 73018cb6503SAxel Lin int error; 731cb7a01acSMauro Carvalho Chehab 732cb7a01acSMauro Carvalho Chehab if (device->streaming == enable) 733cb7a01acSMauro Carvalho Chehab return 0; 734cb7a01acSMauro Carvalho Chehab 73518cb6503SAxel Lin /* low impedance: on, high impedance: off */ 73618cb6503SAxel Lin error = tvp7002_write(sd, TVP7002_MISC_CTL_2, enable ? 0x00 : 0x03); 73718cb6503SAxel Lin if (error) { 73818cb6503SAxel Lin v4l2_dbg(1, debug, sd, "Fail to set streaming\n"); 73918cb6503SAxel Lin return error; 740cb7a01acSMauro Carvalho Chehab } 741cb7a01acSMauro Carvalho Chehab 74218cb6503SAxel Lin device->streaming = enable; 74318cb6503SAxel Lin return 0; 744cb7a01acSMauro Carvalho Chehab } 745cb7a01acSMauro Carvalho Chehab 746cb7a01acSMauro Carvalho Chehab /* 747cb7a01acSMauro Carvalho Chehab * tvp7002_log_status() - Print information about register settings 748cb7a01acSMauro Carvalho Chehab * @sd: ptr to v4l2_subdev struct 749cb7a01acSMauro Carvalho Chehab * 750cb7a01acSMauro Carvalho Chehab * Log register values of a TVP7002 decoder device. 751cb7a01acSMauro Carvalho Chehab * Returns zero or -EINVAL if read operation fails. 752cb7a01acSMauro Carvalho Chehab */ 753cb7a01acSMauro Carvalho Chehab static int tvp7002_log_status(struct v4l2_subdev *sd) 754cb7a01acSMauro Carvalho Chehab { 755cb7a01acSMauro Carvalho Chehab struct tvp7002 *device = to_tvp7002(sd); 756f96067afSHans Verkuil const struct v4l2_bt_timings *bt; 757f96067afSHans Verkuil int detected; 758cb7a01acSMauro Carvalho Chehab 759f96067afSHans Verkuil /* Find my current timings */ 760f96067afSHans Verkuil tvp7002_query_dv(sd, &detected); 761cb7a01acSMauro Carvalho Chehab 762f96067afSHans Verkuil bt = &device->current_timings->timings.bt; 763f96067afSHans Verkuil v4l2_info(sd, "Selected DV Timings: %ux%u\n", bt->width, bt->height); 764f96067afSHans Verkuil if (detected == NUM_TIMINGS) { 765f96067afSHans Verkuil v4l2_info(sd, "Detected DV Timings: None\n"); 766cb7a01acSMauro Carvalho Chehab } else { 767f96067afSHans Verkuil bt = &tvp7002_timings[detected].timings.bt; 768f96067afSHans Verkuil v4l2_info(sd, "Detected DV Timings: %ux%u\n", 769f96067afSHans Verkuil bt->width, bt->height); 770cb7a01acSMauro Carvalho Chehab } 771cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "Streaming enabled: %s\n", 772cb7a01acSMauro Carvalho Chehab device->streaming ? "yes" : "no"); 773cb7a01acSMauro Carvalho Chehab 774cb7a01acSMauro Carvalho Chehab /* Print the current value of the gain control */ 775cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_log_status(&device->hdl, sd->name); 776cb7a01acSMauro Carvalho Chehab 777cb7a01acSMauro Carvalho Chehab return 0; 778cb7a01acSMauro Carvalho Chehab } 779cb7a01acSMauro Carvalho Chehab 780cb7a01acSMauro Carvalho Chehab static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, 781cb7a01acSMauro Carvalho Chehab struct v4l2_enum_dv_timings *timings) 782cb7a01acSMauro Carvalho Chehab { 783307d3bd4SLaurent Pinchart if (timings->pad != 0) 784307d3bd4SLaurent Pinchart return -EINVAL; 785307d3bd4SLaurent Pinchart 786cb7a01acSMauro Carvalho Chehab /* Check requested format index is within range */ 787f0cd015eSHans Verkuil if (timings->index >= NUM_TIMINGS) 788cb7a01acSMauro Carvalho Chehab return -EINVAL; 789cb7a01acSMauro Carvalho Chehab 790f0cd015eSHans Verkuil timings->timings = tvp7002_timings[timings->index].timings; 791cb7a01acSMauro Carvalho Chehab return 0; 792cb7a01acSMauro Carvalho Chehab } 793cb7a01acSMauro Carvalho Chehab 794cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { 795cb7a01acSMauro Carvalho Chehab .s_ctrl = tvp7002_s_ctrl, 796cb7a01acSMauro Carvalho Chehab }; 797cb7a01acSMauro Carvalho Chehab 79863eb2ca1SLad, Prabhakar /* 79963eb2ca1SLad, Prabhakar * tvp7002_enum_mbus_code() - Enum supported digital video format on pad 80063eb2ca1SLad, Prabhakar * @sd: pointer to standard V4L2 sub-device structure 801f7234138SHans Verkuil * @cfg: pad configuration 80263eb2ca1SLad, Prabhakar * @code: pointer to subdev enum mbus code struct 80363eb2ca1SLad, Prabhakar * 80463eb2ca1SLad, Prabhakar * Enumerate supported digital video formats for pad. 80563eb2ca1SLad, Prabhakar */ 80663eb2ca1SLad, Prabhakar static int 807f7234138SHans Verkuil tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 80863eb2ca1SLad, Prabhakar struct v4l2_subdev_mbus_code_enum *code) 80963eb2ca1SLad, Prabhakar { 81063eb2ca1SLad, Prabhakar /* Check requested format index is within range */ 81163eb2ca1SLad, Prabhakar if (code->index != 0) 81263eb2ca1SLad, Prabhakar return -EINVAL; 81363eb2ca1SLad, Prabhakar 814f5fe58fdSBoris BREZILLON code->code = MEDIA_BUS_FMT_YUYV10_1X20; 81563eb2ca1SLad, Prabhakar 81663eb2ca1SLad, Prabhakar return 0; 81763eb2ca1SLad, Prabhakar } 81863eb2ca1SLad, Prabhakar 81963eb2ca1SLad, Prabhakar /* 82063eb2ca1SLad, Prabhakar * tvp7002_get_pad_format() - get video format on pad 82163eb2ca1SLad, Prabhakar * @sd: pointer to standard V4L2 sub-device structure 822f7234138SHans Verkuil * @cfg: pad configuration 82363eb2ca1SLad, Prabhakar * @fmt: pointer to subdev format struct 82463eb2ca1SLad, Prabhakar * 82563eb2ca1SLad, Prabhakar * get video format for pad. 82663eb2ca1SLad, Prabhakar */ 82763eb2ca1SLad, Prabhakar static int 828f7234138SHans Verkuil tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 82963eb2ca1SLad, Prabhakar struct v4l2_subdev_format *fmt) 83063eb2ca1SLad, Prabhakar { 83163eb2ca1SLad, Prabhakar struct tvp7002 *tvp7002 = to_tvp7002(sd); 83263eb2ca1SLad, Prabhakar 833f5fe58fdSBoris BREZILLON fmt->format.code = MEDIA_BUS_FMT_YUYV10_1X20; 83463eb2ca1SLad, Prabhakar fmt->format.width = tvp7002->current_timings->timings.bt.width; 83563eb2ca1SLad, Prabhakar fmt->format.height = tvp7002->current_timings->timings.bt.height; 83663eb2ca1SLad, Prabhakar fmt->format.field = tvp7002->current_timings->scanmode; 83763eb2ca1SLad, Prabhakar fmt->format.colorspace = tvp7002->current_timings->color_space; 83863eb2ca1SLad, Prabhakar 83963eb2ca1SLad, Prabhakar return 0; 84063eb2ca1SLad, Prabhakar } 84163eb2ca1SLad, Prabhakar 84263eb2ca1SLad, Prabhakar /* 84363eb2ca1SLad, Prabhakar * tvp7002_set_pad_format() - set video format on pad 84463eb2ca1SLad, Prabhakar * @sd: pointer to standard V4L2 sub-device structure 845f7234138SHans Verkuil * @cfg: pad configuration 84663eb2ca1SLad, Prabhakar * @fmt: pointer to subdev format struct 84763eb2ca1SLad, Prabhakar * 84863eb2ca1SLad, Prabhakar * set video format for pad. 84963eb2ca1SLad, Prabhakar */ 85063eb2ca1SLad, Prabhakar static int 851f7234138SHans Verkuil tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 85263eb2ca1SLad, Prabhakar struct v4l2_subdev_format *fmt) 85363eb2ca1SLad, Prabhakar { 854f7234138SHans Verkuil return tvp7002_get_pad_format(sd, cfg, fmt); 85563eb2ca1SLad, Prabhakar } 85663eb2ca1SLad, Prabhakar 857cb7a01acSMauro Carvalho Chehab /* V4L2 core operation handlers */ 858cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops tvp7002_core_ops = { 859cb7a01acSMauro Carvalho Chehab .log_status = tvp7002_log_status, 860cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 861cb7a01acSMauro Carvalho Chehab .g_register = tvp7002_g_register, 862cb7a01acSMauro Carvalho Chehab .s_register = tvp7002_s_register, 863cb7a01acSMauro Carvalho Chehab #endif 864cb7a01acSMauro Carvalho Chehab }; 865cb7a01acSMauro Carvalho Chehab 866cb7a01acSMauro Carvalho Chehab /* Specific video subsystem operation handlers */ 867cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops tvp7002_video_ops = { 868cb7a01acSMauro Carvalho Chehab .g_dv_timings = tvp7002_g_dv_timings, 869cb7a01acSMauro Carvalho Chehab .s_dv_timings = tvp7002_s_dv_timings, 870cb7a01acSMauro Carvalho Chehab .query_dv_timings = tvp7002_query_dv_timings, 871cb7a01acSMauro Carvalho Chehab .s_stream = tvp7002_s_stream, 872cb7a01acSMauro Carvalho Chehab }; 873cb7a01acSMauro Carvalho Chehab 87463eb2ca1SLad, Prabhakar /* media pad related operation handlers */ 87563eb2ca1SLad, Prabhakar static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = { 87663eb2ca1SLad, Prabhakar .enum_mbus_code = tvp7002_enum_mbus_code, 87763eb2ca1SLad, Prabhakar .get_fmt = tvp7002_get_pad_format, 87863eb2ca1SLad, Prabhakar .set_fmt = tvp7002_set_pad_format, 879307d3bd4SLaurent Pinchart .enum_dv_timings = tvp7002_enum_dv_timings, 88063eb2ca1SLad, Prabhakar }; 88163eb2ca1SLad, Prabhakar 882cb7a01acSMauro Carvalho Chehab /* V4L2 top level operation handlers */ 883cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops tvp7002_ops = { 884cb7a01acSMauro Carvalho Chehab .core = &tvp7002_core_ops, 885cb7a01acSMauro Carvalho Chehab .video = &tvp7002_video_ops, 88663eb2ca1SLad, Prabhakar .pad = &tvp7002_pad_ops, 887cb7a01acSMauro Carvalho Chehab }; 888cb7a01acSMauro Carvalho Chehab 889c0d9644fSLad, Prabhakar static struct tvp7002_config * 890c0d9644fSLad, Prabhakar tvp7002_get_pdata(struct i2c_client *client) 891c0d9644fSLad, Prabhakar { 89260359a28SSakari Ailus struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 893baf40b5fSJavier Martinez Canillas struct tvp7002_config *pdata = NULL; 894c0d9644fSLad, Prabhakar struct device_node *endpoint; 895c0d9644fSLad, Prabhakar unsigned int flags; 896c0d9644fSLad, Prabhakar 897c0d9644fSLad, Prabhakar if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) 898c0d9644fSLad, Prabhakar return client->dev.platform_data; 899c0d9644fSLad, Prabhakar 900fd9fdb78SPhilipp Zabel endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL); 901c0d9644fSLad, Prabhakar if (!endpoint) 902c0d9644fSLad, Prabhakar return NULL; 903c0d9644fSLad, Prabhakar 904859969b3SSakari Ailus if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg)) 905baf40b5fSJavier Martinez Canillas goto done; 906baf40b5fSJavier Martinez Canillas 907c0d9644fSLad, Prabhakar pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); 908c0d9644fSLad, Prabhakar if (!pdata) 909c0d9644fSLad, Prabhakar goto done; 910c0d9644fSLad, Prabhakar 911c0d9644fSLad, Prabhakar flags = bus_cfg.bus.parallel.flags; 912c0d9644fSLad, Prabhakar 913c0d9644fSLad, Prabhakar if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 914c0d9644fSLad, Prabhakar pdata->hs_polarity = 1; 915c0d9644fSLad, Prabhakar 916c0d9644fSLad, Prabhakar if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) 917c0d9644fSLad, Prabhakar pdata->vs_polarity = 1; 918c0d9644fSLad, Prabhakar 919c0d9644fSLad, Prabhakar if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) 920c0d9644fSLad, Prabhakar pdata->clk_polarity = 1; 921c0d9644fSLad, Prabhakar 922c0d9644fSLad, Prabhakar if (flags & V4L2_MBUS_FIELD_EVEN_HIGH) 923c0d9644fSLad, Prabhakar pdata->fid_polarity = 1; 924c0d9644fSLad, Prabhakar 925c0d9644fSLad, Prabhakar if (flags & V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH) 926c0d9644fSLad, Prabhakar pdata->sog_polarity = 1; 927c0d9644fSLad, Prabhakar 928c0d9644fSLad, Prabhakar done: 929c0d9644fSLad, Prabhakar of_node_put(endpoint); 930c0d9644fSLad, Prabhakar return pdata; 931c0d9644fSLad, Prabhakar } 932c0d9644fSLad, Prabhakar 933cb7a01acSMauro Carvalho Chehab /* 934cb7a01acSMauro Carvalho Chehab * tvp7002_probe - Probe a TVP7002 device 935cb7a01acSMauro Carvalho Chehab * @c: ptr to i2c_client struct 936cb7a01acSMauro Carvalho Chehab * @id: ptr to i2c_device_id struct 937cb7a01acSMauro Carvalho Chehab * 938cb7a01acSMauro Carvalho Chehab * Initialize the TVP7002 device 939cb7a01acSMauro Carvalho Chehab * Returns zero when successful, -EINVAL if register read fails or 940cb7a01acSMauro Carvalho Chehab * -EIO if i2c access is not available. 941cb7a01acSMauro Carvalho Chehab */ 942cb7a01acSMauro Carvalho Chehab static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) 943cb7a01acSMauro Carvalho Chehab { 944c0d9644fSLad, Prabhakar struct tvp7002_config *pdata = tvp7002_get_pdata(c); 945cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 946cb7a01acSMauro Carvalho Chehab struct tvp7002 *device; 947f96067afSHans Verkuil struct v4l2_dv_timings timings; 948cb7a01acSMauro Carvalho Chehab int polarity_a; 949cb7a01acSMauro Carvalho Chehab int polarity_b; 950cb7a01acSMauro Carvalho Chehab u8 revision; 951cb7a01acSMauro Carvalho Chehab int error; 952cb7a01acSMauro Carvalho Chehab 953c0d9644fSLad, Prabhakar if (pdata == NULL) { 954c0d9644fSLad, Prabhakar dev_err(&c->dev, "No platform data\n"); 955c0d9644fSLad, Prabhakar return -EINVAL; 956c0d9644fSLad, Prabhakar } 957c0d9644fSLad, Prabhakar 958cb7a01acSMauro Carvalho Chehab /* Check if the adapter supports the needed features */ 959cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(c->adapter, 960cb7a01acSMauro Carvalho Chehab I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 961cb7a01acSMauro Carvalho Chehab return -EIO; 962cb7a01acSMauro Carvalho Chehab 963f3e8e4f1SLad, Prabhakar device = devm_kzalloc(&c->dev, sizeof(struct tvp7002), GFP_KERNEL); 964cb7a01acSMauro Carvalho Chehab 965cb7a01acSMauro Carvalho Chehab if (!device) 966cb7a01acSMauro Carvalho Chehab return -ENOMEM; 967cb7a01acSMauro Carvalho Chehab 968cb7a01acSMauro Carvalho Chehab sd = &device->sd; 969c0d9644fSLad, Prabhakar device->pdata = pdata; 970f0cd015eSHans Verkuil device->current_timings = tvp7002_timings; 971cb7a01acSMauro Carvalho Chehab 972cb7a01acSMauro Carvalho Chehab /* Tell v4l2 the device is ready */ 973cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); 974cb7a01acSMauro Carvalho Chehab v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", 975cb7a01acSMauro Carvalho Chehab c->addr, c->adapter->name); 976cb7a01acSMauro Carvalho Chehab 977cb7a01acSMauro Carvalho Chehab error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); 978cb7a01acSMauro Carvalho Chehab if (error < 0) 979f3e8e4f1SLad, Prabhakar return error; 980cb7a01acSMauro Carvalho Chehab 981cb7a01acSMauro Carvalho Chehab /* Get revision number */ 982cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "Rev. %02x detected.\n", revision); 983cb7a01acSMauro Carvalho Chehab if (revision != 0x02) 984cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "Unknown revision detected.\n"); 985cb7a01acSMauro Carvalho Chehab 986cb7a01acSMauro Carvalho Chehab /* Initializes TVP7002 to its default values */ 987cb7a01acSMauro Carvalho Chehab error = tvp7002_write_inittab(sd, tvp7002_init_default); 988cb7a01acSMauro Carvalho Chehab 989cb7a01acSMauro Carvalho Chehab if (error < 0) 990f3e8e4f1SLad, Prabhakar return error; 991cb7a01acSMauro Carvalho Chehab 992cb7a01acSMauro Carvalho Chehab /* Set polarity information after registers have been set */ 993cb7a01acSMauro Carvalho Chehab polarity_a = 0x20 | device->pdata->hs_polarity << 5 994cb7a01acSMauro Carvalho Chehab | device->pdata->vs_polarity << 2; 995cb7a01acSMauro Carvalho Chehab error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); 996cb7a01acSMauro Carvalho Chehab if (error < 0) 997f3e8e4f1SLad, Prabhakar return error; 998cb7a01acSMauro Carvalho Chehab 999cb7a01acSMauro Carvalho Chehab polarity_b = 0x01 | device->pdata->fid_polarity << 2 1000cb7a01acSMauro Carvalho Chehab | device->pdata->sog_polarity << 1 1001cb7a01acSMauro Carvalho Chehab | device->pdata->clk_polarity; 1002cb7a01acSMauro Carvalho Chehab error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); 1003cb7a01acSMauro Carvalho Chehab if (error < 0) 1004f3e8e4f1SLad, Prabhakar return error; 1005cb7a01acSMauro Carvalho Chehab 1006cb7a01acSMauro Carvalho Chehab /* Set registers according to default video mode */ 1007f96067afSHans Verkuil timings = device->current_timings->timings; 1008f96067afSHans Verkuil error = tvp7002_s_dv_timings(sd, &timings); 1009cb7a01acSMauro Carvalho Chehab 101063eb2ca1SLad, Prabhakar #if defined(CONFIG_MEDIA_CONTROLLER) 101163eb2ca1SLad, Prabhakar device->pad.flags = MEDIA_PAD_FL_SOURCE; 101263eb2ca1SLad, Prabhakar device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1013ca0fa5f0SHans Verkuil device->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; 101463eb2ca1SLad, Prabhakar 1015ab22e77cSMauro Carvalho Chehab error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); 101663eb2ca1SLad, Prabhakar if (error < 0) 101763eb2ca1SLad, Prabhakar return error; 101863eb2ca1SLad, Prabhakar #endif 101963eb2ca1SLad, Prabhakar 1020cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_init(&device->hdl, 1); 1021cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, 1022cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, 0, 255, 1, 0); 1023cb7a01acSMauro Carvalho Chehab sd->ctrl_handler = &device->hdl; 1024cb7a01acSMauro Carvalho Chehab if (device->hdl.error) { 102563eb2ca1SLad, Prabhakar error = device->hdl.error; 102663eb2ca1SLad, Prabhakar goto error; 1027cb7a01acSMauro Carvalho Chehab } 1028cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_setup(&device->hdl); 1029cb7a01acSMauro Carvalho Chehab 103025ba2c80SLad, Prabhakar error = v4l2_async_register_subdev(&device->sd); 103125ba2c80SLad, Prabhakar if (error) 103225ba2c80SLad, Prabhakar goto error; 103325ba2c80SLad, Prabhakar 1034f3e8e4f1SLad, Prabhakar return 0; 103563eb2ca1SLad, Prabhakar 103663eb2ca1SLad, Prabhakar error: 103763eb2ca1SLad, Prabhakar v4l2_ctrl_handler_free(&device->hdl); 103863eb2ca1SLad, Prabhakar #if defined(CONFIG_MEDIA_CONTROLLER) 103963eb2ca1SLad, Prabhakar media_entity_cleanup(&device->sd.entity); 104063eb2ca1SLad, Prabhakar #endif 104163eb2ca1SLad, Prabhakar return error; 1042cb7a01acSMauro Carvalho Chehab } 1043cb7a01acSMauro Carvalho Chehab 1044cb7a01acSMauro Carvalho Chehab /* 1045cb7a01acSMauro Carvalho Chehab * tvp7002_remove - Remove TVP7002 device support 1046cb7a01acSMauro Carvalho Chehab * @c: ptr to i2c_client struct 1047cb7a01acSMauro Carvalho Chehab * 1048cb7a01acSMauro Carvalho Chehab * Reset the TVP7002 device 1049cb7a01acSMauro Carvalho Chehab * Returns zero. 1050cb7a01acSMauro Carvalho Chehab */ 1051cb7a01acSMauro Carvalho Chehab static int tvp7002_remove(struct i2c_client *c) 1052cb7a01acSMauro Carvalho Chehab { 1053cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(c); 1054cb7a01acSMauro Carvalho Chehab struct tvp7002 *device = to_tvp7002(sd); 1055cb7a01acSMauro Carvalho Chehab 1056cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" 1057cb7a01acSMauro Carvalho Chehab "on address 0x%x\n", c->addr); 105825ba2c80SLad, Prabhakar v4l2_async_unregister_subdev(&device->sd); 105963eb2ca1SLad, Prabhakar #if defined(CONFIG_MEDIA_CONTROLLER) 106063eb2ca1SLad, Prabhakar media_entity_cleanup(&device->sd.entity); 106163eb2ca1SLad, Prabhakar #endif 1062cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&device->hdl); 1063cb7a01acSMauro Carvalho Chehab return 0; 1064cb7a01acSMauro Carvalho Chehab } 1065cb7a01acSMauro Carvalho Chehab 1066cb7a01acSMauro Carvalho Chehab /* I2C Device ID table */ 1067cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id tvp7002_id[] = { 1068cb7a01acSMauro Carvalho Chehab { "tvp7002", 0 }, 1069cb7a01acSMauro Carvalho Chehab { } 1070cb7a01acSMauro Carvalho Chehab }; 1071cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, tvp7002_id); 1072cb7a01acSMauro Carvalho Chehab 1073c0d9644fSLad, Prabhakar #if IS_ENABLED(CONFIG_OF) 1074c0d9644fSLad, Prabhakar static const struct of_device_id tvp7002_of_match[] = { 1075c0d9644fSLad, Prabhakar { .compatible = "ti,tvp7002", }, 1076c0d9644fSLad, Prabhakar { /* sentinel */ }, 1077c0d9644fSLad, Prabhakar }; 1078c0d9644fSLad, Prabhakar MODULE_DEVICE_TABLE(of, tvp7002_of_match); 1079c0d9644fSLad, Prabhakar #endif 1080c0d9644fSLad, Prabhakar 1081cb7a01acSMauro Carvalho Chehab /* I2C driver data */ 1082cb7a01acSMauro Carvalho Chehab static struct i2c_driver tvp7002_driver = { 1083cb7a01acSMauro Carvalho Chehab .driver = { 1084c0d9644fSLad, Prabhakar .of_match_table = of_match_ptr(tvp7002_of_match), 1085cb7a01acSMauro Carvalho Chehab .name = TVP7002_MODULE_NAME, 1086cb7a01acSMauro Carvalho Chehab }, 1087cb7a01acSMauro Carvalho Chehab .probe = tvp7002_probe, 1088cb7a01acSMauro Carvalho Chehab .remove = tvp7002_remove, 1089cb7a01acSMauro Carvalho Chehab .id_table = tvp7002_id, 1090cb7a01acSMauro Carvalho Chehab }; 1091cb7a01acSMauro Carvalho Chehab 1092cb7a01acSMauro Carvalho Chehab module_i2c_driver(tvp7002_driver); 1093