18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics 38c2ecf20Sopenharmony_ci * Digitizer with Horizontal PLL registers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Inc 68c2ecf20Sopenharmony_ci * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This code is partially based upon the TVP5150 driver 98c2ecf20Sopenharmony_ci * written by Mauro Carvalho Chehab <mchehab@kernel.org>, 108c2ecf20Sopenharmony_ci * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com> 118c2ecf20Sopenharmony_ci * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by 128c2ecf20Sopenharmony_ci * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 218c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 228c2ecf20Sopenharmony_ci#include <media/i2c/tvp7002.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "tvp7002_reg.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>"); 338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* I2C retry attempts */ 368c2ecf20Sopenharmony_ci#define I2C_RETRY_COUNT (5) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* End of registers */ 398c2ecf20Sopenharmony_ci#define TVP7002_EOR 0x5c 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Read write definition for registers */ 428c2ecf20Sopenharmony_ci#define TVP7002_READ 0 438c2ecf20Sopenharmony_ci#define TVP7002_WRITE 1 448c2ecf20Sopenharmony_ci#define TVP7002_RESERVED 2 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* Interlaced vs progressive mask and shift */ 478c2ecf20Sopenharmony_ci#define TVP7002_IP_SHIFT 5 488c2ecf20Sopenharmony_ci#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Shift for CPL and LPF registers */ 518c2ecf20Sopenharmony_ci#define TVP7002_CL_SHIFT 8 528c2ecf20Sopenharmony_ci#define TVP7002_CL_MASK 0x0f 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Debug functions */ 558c2ecf20Sopenharmony_cistatic bool debug; 568c2ecf20Sopenharmony_cimodule_param(debug, bool, 0644); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-2)"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Structure for register values */ 608c2ecf20Sopenharmony_cistruct i2c_reg_value { 618c2ecf20Sopenharmony_ci u8 reg; 628c2ecf20Sopenharmony_ci u8 value; 638c2ecf20Sopenharmony_ci u8 type; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * Register default values (according to tvp7002 datasheet) 688c2ecf20Sopenharmony_ci * In the case of read-only registers, the value (0xff) is 698c2ecf20Sopenharmony_ci * never written. R/W functionality is controlled by the 708c2ecf20Sopenharmony_ci * writable bit in the register struct definition. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_init_default[] = { 738c2ecf20Sopenharmony_ci { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, 748c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, 758c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, 768c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, 778c2ecf20Sopenharmony_ci { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, 788c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 798c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 808c2ecf20Sopenharmony_ci { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, 818c2ecf20Sopenharmony_ci { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, 828c2ecf20Sopenharmony_ci { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, 838c2ecf20Sopenharmony_ci { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, 848c2ecf20Sopenharmony_ci { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 858c2ecf20Sopenharmony_ci { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 868c2ecf20Sopenharmony_ci { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, 878c2ecf20Sopenharmony_ci { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, 888c2ecf20Sopenharmony_ci { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, 898c2ecf20Sopenharmony_ci { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, 908c2ecf20Sopenharmony_ci { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, 918c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, 928c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 938c2ecf20Sopenharmony_ci { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, 948c2ecf20Sopenharmony_ci { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, 958c2ecf20Sopenharmony_ci { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, 968c2ecf20Sopenharmony_ci { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, 978c2ecf20Sopenharmony_ci { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, 988c2ecf20Sopenharmony_ci { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, 998c2ecf20Sopenharmony_ci { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, 1008c2ecf20Sopenharmony_ci { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, 1018c2ecf20Sopenharmony_ci { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, 1028c2ecf20Sopenharmony_ci { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, 1038c2ecf20Sopenharmony_ci { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, 1048c2ecf20Sopenharmony_ci { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, 1058c2ecf20Sopenharmony_ci { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, 1068c2ecf20Sopenharmony_ci { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, 1078c2ecf20Sopenharmony_ci { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, 1088c2ecf20Sopenharmony_ci { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 1098c2ecf20Sopenharmony_ci { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 1108c2ecf20Sopenharmony_ci { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, 1118c2ecf20Sopenharmony_ci { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, 1128c2ecf20Sopenharmony_ci { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, 1138c2ecf20Sopenharmony_ci { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, 1148c2ecf20Sopenharmony_ci { 0x29, 0x08, TVP7002_RESERVED }, 1158c2ecf20Sopenharmony_ci { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, 1168c2ecf20Sopenharmony_ci /* PWR_CTL is controlled only by the probe and reset functions */ 1178c2ecf20Sopenharmony_ci { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, 1188c2ecf20Sopenharmony_ci { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, 1198c2ecf20Sopenharmony_ci { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, 1208c2ecf20Sopenharmony_ci { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, 1218c2ecf20Sopenharmony_ci { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, 1228c2ecf20Sopenharmony_ci { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, 1238c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 1248c2ecf20Sopenharmony_ci { 0x32, 0x18, TVP7002_RESERVED }, 1258c2ecf20Sopenharmony_ci { 0x33, 0x60, TVP7002_RESERVED }, 1268c2ecf20Sopenharmony_ci { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, 1278c2ecf20Sopenharmony_ci { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, 1288c2ecf20Sopenharmony_ci { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, 1298c2ecf20Sopenharmony_ci { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, 1308c2ecf20Sopenharmony_ci { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, 1318c2ecf20Sopenharmony_ci { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, 1328c2ecf20Sopenharmony_ci { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, 1338c2ecf20Sopenharmony_ci { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, 1348c2ecf20Sopenharmony_ci { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, 1358c2ecf20Sopenharmony_ci { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, 1368c2ecf20Sopenharmony_ci { 0x3e, 0x60, TVP7002_RESERVED }, 1378c2ecf20Sopenharmony_ci { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, 1388c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, 1398c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, 1408c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 1418c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, 1428c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 1438c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 1448c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, 1458c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 1468c2ecf20Sopenharmony_ci { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, 1478c2ecf20Sopenharmony_ci { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 1488c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, 1498c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, 1508c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, 1518c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, 1528c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, 1538c2ecf20Sopenharmony_ci { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, 1548c2ecf20Sopenharmony_ci { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, 1558c2ecf20Sopenharmony_ci { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, 1568c2ecf20Sopenharmony_ci { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, 1578c2ecf20Sopenharmony_ci { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, 1588c2ecf20Sopenharmony_ci { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, 1598c2ecf20Sopenharmony_ci { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, 1608c2ecf20Sopenharmony_ci { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, 1618c2ecf20Sopenharmony_ci { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, 1628c2ecf20Sopenharmony_ci { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, 1638c2ecf20Sopenharmony_ci { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, 1648c2ecf20Sopenharmony_ci { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, 1658c2ecf20Sopenharmony_ci { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, 1668c2ecf20Sopenharmony_ci /* This signals end of register values */ 1678c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* Register parameters for 480P */ 1718c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_480P[] = { 1728c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, 1738c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, 1748c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, 1758c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, 1768c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 1778c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, 1788c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 1798c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, 1808c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, 1818c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, 1828c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, 1838c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, 1848c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, 1858c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, 1868c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, 1878c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, 1888c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* Register parameters for 576P */ 1928c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_576P[] = { 1938c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, 1948c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, 1958c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, 1968c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, 1978c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 1988c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, 1998c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, 2008c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, 2018c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 2028c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 2038c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 2048c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, 2058c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, 2068c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, 2078c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, 2088c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, 2098c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* Register parameters for 1080I60 */ 2138c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_1080I60[] = { 2148c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, 2158c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, 2168c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 2178c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 2188c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 2198c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 2208c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 2218c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 2228c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 2238c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 2248c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 2258c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 2268c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 2278c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 2288c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 2298c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 2308c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* Register parameters for 1080P60 */ 2348c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_1080P60[] = { 2358c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, 2368c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, 2378c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, 2388c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 2398c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 2408c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 2418c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 2428c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 2438c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 2448c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 2458c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 2468c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 2478c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 2488c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 2498c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 2508c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 2518c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Register parameters for 1080I50 */ 2558c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_1080I50[] = { 2568c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, 2578c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, 2588c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 2598c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, 2608c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 2618c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, 2628c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, 2638c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, 2648c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, 2658c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, 2668c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, 2678c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 2688c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 2698c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 2708c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 2718c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 2728c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Register parameters for 720P60 */ 2768c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_720P60[] = { 2778c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, 2788c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, 2798c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, 2808c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, 2818c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 2828c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, 2838c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, 2848c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 2858c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 2868c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 2878c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 2888c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 2898c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 2908c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 2918c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, 2928c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 2938c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* Register parameters for 720P50 */ 2978c2ecf20Sopenharmony_cistatic const struct i2c_reg_value tvp7002_parms_720P50[] = { 2988c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, 2998c2ecf20Sopenharmony_ci { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, 3008c2ecf20Sopenharmony_ci { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, 3018c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, 3028c2ecf20Sopenharmony_ci { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, 3038c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, 3048c2ecf20Sopenharmony_ci { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, 3058c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, 3068c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, 3078c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, 3088c2ecf20Sopenharmony_ci { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, 3098c2ecf20Sopenharmony_ci { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, 3108c2ecf20Sopenharmony_ci { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, 3118c2ecf20Sopenharmony_ci { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, 3128c2ecf20Sopenharmony_ci { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, 3138c2ecf20Sopenharmony_ci { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, 3148c2ecf20Sopenharmony_ci { TVP7002_EOR, 0xff, TVP7002_RESERVED } 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* Timings definition for handling device operation */ 3188c2ecf20Sopenharmony_cistruct tvp7002_timings_definition { 3198c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 3208c2ecf20Sopenharmony_ci const struct i2c_reg_value *p_settings; 3218c2ecf20Sopenharmony_ci enum v4l2_colorspace color_space; 3228c2ecf20Sopenharmony_ci enum v4l2_field scanmode; 3238c2ecf20Sopenharmony_ci u16 progressive; 3248c2ecf20Sopenharmony_ci u16 lines_per_frame; 3258c2ecf20Sopenharmony_ci u16 cpl_min; 3268c2ecf20Sopenharmony_ci u16 cpl_max; 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* Struct list for digital video timings */ 3308c2ecf20Sopenharmony_cistatic const struct tvp7002_timings_definition tvp7002_timings[] = { 3318c2ecf20Sopenharmony_ci { 3328c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P60, 3338c2ecf20Sopenharmony_ci tvp7002_parms_720P60, 3348c2ecf20Sopenharmony_ci V4L2_COLORSPACE_REC709, 3358c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 3368c2ecf20Sopenharmony_ci 1, 3378c2ecf20Sopenharmony_ci 0x2EE, 3388c2ecf20Sopenharmony_ci 135, 3398c2ecf20Sopenharmony_ci 153 3408c2ecf20Sopenharmony_ci }, 3418c2ecf20Sopenharmony_ci { 3428c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080I60, 3438c2ecf20Sopenharmony_ci tvp7002_parms_1080I60, 3448c2ecf20Sopenharmony_ci V4L2_COLORSPACE_REC709, 3458c2ecf20Sopenharmony_ci V4L2_FIELD_INTERLACED, 3468c2ecf20Sopenharmony_ci 0, 3478c2ecf20Sopenharmony_ci 0x465, 3488c2ecf20Sopenharmony_ci 181, 3498c2ecf20Sopenharmony_ci 205 3508c2ecf20Sopenharmony_ci }, 3518c2ecf20Sopenharmony_ci { 3528c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080I50, 3538c2ecf20Sopenharmony_ci tvp7002_parms_1080I50, 3548c2ecf20Sopenharmony_ci V4L2_COLORSPACE_REC709, 3558c2ecf20Sopenharmony_ci V4L2_FIELD_INTERLACED, 3568c2ecf20Sopenharmony_ci 0, 3578c2ecf20Sopenharmony_ci 0x465, 3588c2ecf20Sopenharmony_ci 217, 3598c2ecf20Sopenharmony_ci 245 3608c2ecf20Sopenharmony_ci }, 3618c2ecf20Sopenharmony_ci { 3628c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P50, 3638c2ecf20Sopenharmony_ci tvp7002_parms_720P50, 3648c2ecf20Sopenharmony_ci V4L2_COLORSPACE_REC709, 3658c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 3668c2ecf20Sopenharmony_ci 1, 3678c2ecf20Sopenharmony_ci 0x2EE, 3688c2ecf20Sopenharmony_ci 163, 3698c2ecf20Sopenharmony_ci 183 3708c2ecf20Sopenharmony_ci }, 3718c2ecf20Sopenharmony_ci { 3728c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P60, 3738c2ecf20Sopenharmony_ci tvp7002_parms_1080P60, 3748c2ecf20Sopenharmony_ci V4L2_COLORSPACE_REC709, 3758c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 3768c2ecf20Sopenharmony_ci 1, 3778c2ecf20Sopenharmony_ci 0x465, 3788c2ecf20Sopenharmony_ci 90, 3798c2ecf20Sopenharmony_ci 102 3808c2ecf20Sopenharmony_ci }, 3818c2ecf20Sopenharmony_ci { 3828c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_720X480P59_94, 3838c2ecf20Sopenharmony_ci tvp7002_parms_480P, 3848c2ecf20Sopenharmony_ci V4L2_COLORSPACE_SMPTE170M, 3858c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 3868c2ecf20Sopenharmony_ci 1, 3878c2ecf20Sopenharmony_ci 0x20D, 3888c2ecf20Sopenharmony_ci 0xffff, 3898c2ecf20Sopenharmony_ci 0xffff 3908c2ecf20Sopenharmony_ci }, 3918c2ecf20Sopenharmony_ci { 3928c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_720X576P50, 3938c2ecf20Sopenharmony_ci tvp7002_parms_576P, 3948c2ecf20Sopenharmony_ci V4L2_COLORSPACE_SMPTE170M, 3958c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 3968c2ecf20Sopenharmony_ci 1, 3978c2ecf20Sopenharmony_ci 0x271, 3988c2ecf20Sopenharmony_ci 0xffff, 3998c2ecf20Sopenharmony_ci 0xffff 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci#define NUM_TIMINGS ARRAY_SIZE(tvp7002_timings) 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* Device definition */ 4068c2ecf20Sopenharmony_cistruct tvp7002 { 4078c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 4088c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 4098c2ecf20Sopenharmony_ci const struct tvp7002_config *pdata; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci int ver; 4128c2ecf20Sopenharmony_ci int streaming; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci const struct tvp7002_timings_definition *current_timings; 4158c2ecf20Sopenharmony_ci struct media_pad pad; 4168c2ecf20Sopenharmony_ci}; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* 4198c2ecf20Sopenharmony_ci * to_tvp7002 - Obtain device handler TVP7002 4208c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Returns device handler tvp7002. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci return container_of(sd, struct tvp7002, sd); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* 4358c2ecf20Sopenharmony_ci * tvp7002_read - Read a value from a register in an TVP7002 4368c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 4378c2ecf20Sopenharmony_ci * @addr: TVP7002 register address 4388c2ecf20Sopenharmony_ci * @dst: pointer to 8-bit destination 4398c2ecf20Sopenharmony_ci * 4408c2ecf20Sopenharmony_ci * Returns value read if successful, or non-zero (-1) otherwise. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(sd); 4458c2ecf20Sopenharmony_ci int retry; 4468c2ecf20Sopenharmony_ci int error; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { 4498c2ecf20Sopenharmony_ci error = i2c_smbus_read_byte_data(c, addr); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (error >= 0) { 4528c2ecf20Sopenharmony_ci *dst = (u8)error; 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci msleep_interruptible(10); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci v4l2_err(sd, "TVP7002 read error %d\n", error); 4598c2ecf20Sopenharmony_ci return error; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* 4638c2ecf20Sopenharmony_ci * tvp7002_read_err() - Read a register value with error code 4648c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 4658c2ecf20Sopenharmony_ci * @reg: destination register 4668c2ecf20Sopenharmony_ci * @val: value to be read 4678c2ecf20Sopenharmony_ci * @err: pointer to error value 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Read a value in a register and save error value in pointer. 4708c2ecf20Sopenharmony_ci * Also update the register table if successful 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_cistatic inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, 4738c2ecf20Sopenharmony_ci u8 *dst, int *err) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci if (!*err) 4768c2ecf20Sopenharmony_ci *err = tvp7002_read(sd, reg, dst); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * tvp7002_write() - Write a value to a register in TVP7002 4818c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 4828c2ecf20Sopenharmony_ci * @addr: TVP7002 register address 4838c2ecf20Sopenharmony_ci * @value: value to be written to the register 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * Write a value to a register in an TVP7002 decoder device. 4868c2ecf20Sopenharmony_ci * Returns zero if successful, or non-zero otherwise. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_cistatic int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct i2c_client *c; 4918c2ecf20Sopenharmony_ci int retry; 4928c2ecf20Sopenharmony_ci int error; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci c = v4l2_get_subdevdata(sd); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { 4978c2ecf20Sopenharmony_ci error = i2c_smbus_write_byte_data(c, addr, value); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (error >= 0) 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci v4l2_warn(sd, "Write: retry ... %d\n", retry); 5038c2ecf20Sopenharmony_ci msleep_interruptible(10); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci v4l2_err(sd, "TVP7002 write error %d\n", error); 5068c2ecf20Sopenharmony_ci return error; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/* 5108c2ecf20Sopenharmony_ci * tvp7002_write_err() - Write a register value with error code 5118c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 5128c2ecf20Sopenharmony_ci * @reg: destination register 5138c2ecf20Sopenharmony_ci * @val: value to be written 5148c2ecf20Sopenharmony_ci * @err: pointer to error value 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Write a value in a register and save error value in pointer. 5178c2ecf20Sopenharmony_ci * Also update the register table if successful 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cistatic inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, 5208c2ecf20Sopenharmony_ci u8 val, int *err) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci if (!*err) 5238c2ecf20Sopenharmony_ci *err = tvp7002_write(sd, reg, val); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/* 5278c2ecf20Sopenharmony_ci * tvp7002_write_inittab() - Write initialization values 5288c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 5298c2ecf20Sopenharmony_ci * @regs: ptr to i2c_reg_value struct 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * Write initialization values. 5328c2ecf20Sopenharmony_ci * Returns zero or -EINVAL if read operation fails. 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_cistatic int tvp7002_write_inittab(struct v4l2_subdev *sd, 5358c2ecf20Sopenharmony_ci const struct i2c_reg_value *regs) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci int error = 0; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Initialize the first (defined) registers */ 5408c2ecf20Sopenharmony_ci while (TVP7002_EOR != regs->reg) { 5418c2ecf20Sopenharmony_ci if (TVP7002_WRITE == regs->type) 5428c2ecf20Sopenharmony_ci tvp7002_write_err(sd, regs->reg, regs->value, &error); 5438c2ecf20Sopenharmony_ci regs++; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return error; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int tvp7002_s_dv_timings(struct v4l2_subdev *sd, 5508c2ecf20Sopenharmony_ci struct v4l2_dv_timings *dv_timings) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct tvp7002 *device = to_tvp7002(sd); 5538c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt = &dv_timings->bt; 5548c2ecf20Sopenharmony_ci int i; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (dv_timings->type != V4L2_DV_BT_656_1120) 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci for (i = 0; i < NUM_TIMINGS; i++) { 5598c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *t = &tvp7002_timings[i].timings.bt; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!memcmp(bt, t, &bt->standards - &bt->width)) { 5628c2ecf20Sopenharmony_ci device->current_timings = &tvp7002_timings[i]; 5638c2ecf20Sopenharmony_ci return tvp7002_write_inittab(sd, tvp7002_timings[i].p_settings); 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci return -EINVAL; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int tvp7002_g_dv_timings(struct v4l2_subdev *sd, 5708c2ecf20Sopenharmony_ci struct v4l2_dv_timings *dv_timings) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct tvp7002 *device = to_tvp7002(sd); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci *dv_timings = device->current_timings->timings; 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* 5798c2ecf20Sopenharmony_ci * tvp7002_s_ctrl() - Set a control 5808c2ecf20Sopenharmony_ci * @ctrl: ptr to v4l2_ctrl struct 5818c2ecf20Sopenharmony_ci * 5828c2ecf20Sopenharmony_ci * Set a control in TVP7002 decoder device. 5838c2ecf20Sopenharmony_ci * Returns zero when successful or -EINVAL if register access fails. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_cistatic int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 5888c2ecf20Sopenharmony_ci int error = 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci switch (ctrl->id) { 5918c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 5928c2ecf20Sopenharmony_ci tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); 5938c2ecf20Sopenharmony_ci tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); 5948c2ecf20Sopenharmony_ci tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); 5958c2ecf20Sopenharmony_ci return error; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci return -EINVAL; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/* 6018c2ecf20Sopenharmony_ci * tvp7002_query_dv() - query DV timings 6028c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 6038c2ecf20Sopenharmony_ci * @index: index into the tvp7002_timings array 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * Returns the current DV timings detected by TVP7002. If no active input is 6068c2ecf20Sopenharmony_ci * detected, returns -EINVAL 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_cistatic int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci const struct tvp7002_timings_definition *timings = tvp7002_timings; 6118c2ecf20Sopenharmony_ci u8 progressive; 6128c2ecf20Sopenharmony_ci u32 lpfr; 6138c2ecf20Sopenharmony_ci u32 cpln; 6148c2ecf20Sopenharmony_ci int error = 0; 6158c2ecf20Sopenharmony_ci u8 lpf_lsb; 6168c2ecf20Sopenharmony_ci u8 lpf_msb; 6178c2ecf20Sopenharmony_ci u8 cpl_lsb; 6188c2ecf20Sopenharmony_ci u8 cpl_msb; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Return invalid index if no active input is detected */ 6218c2ecf20Sopenharmony_ci *index = NUM_TIMINGS; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* Read standards from device registers */ 6248c2ecf20Sopenharmony_ci tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); 6258c2ecf20Sopenharmony_ci tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (error < 0) 6288c2ecf20Sopenharmony_ci return error; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); 6318c2ecf20Sopenharmony_ci tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (error < 0) 6348c2ecf20Sopenharmony_ci return error; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Get lines per frame, clocks per line and interlaced/progresive */ 6378c2ecf20Sopenharmony_ci lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); 6388c2ecf20Sopenharmony_ci cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); 6398c2ecf20Sopenharmony_ci progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Do checking of video modes */ 6428c2ecf20Sopenharmony_ci for (*index = 0; *index < NUM_TIMINGS; (*index)++, timings++) 6438c2ecf20Sopenharmony_ci if (lpfr == timings->lines_per_frame && 6448c2ecf20Sopenharmony_ci progressive == timings->progressive) { 6458c2ecf20Sopenharmony_ci if (timings->cpl_min == 0xffff) 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci if (cpln >= timings->cpl_min && cpln <= timings->cpl_max) 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (*index == NUM_TIMINGS) { 6528c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", 6538c2ecf20Sopenharmony_ci lpfr, cpln); 6548c2ecf20Sopenharmony_ci return -ENOLINK; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Update lines per frame and clocks per line info */ 6588c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "detected timings: %d\n", *index); 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int tvp7002_query_dv_timings(struct v4l2_subdev *sd, 6638c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci int index; 6668c2ecf20Sopenharmony_ci int err = tvp7002_query_dv(sd, &index); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (err) 6698c2ecf20Sopenharmony_ci return err; 6708c2ecf20Sopenharmony_ci *timings = tvp7002_timings[index].timings; 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 6758c2ecf20Sopenharmony_ci/* 6768c2ecf20Sopenharmony_ci * tvp7002_g_register() - Get the value of a register 6778c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 6788c2ecf20Sopenharmony_ci * @reg: ptr to v4l2_dbg_register struct 6798c2ecf20Sopenharmony_ci * 6808c2ecf20Sopenharmony_ci * Get the value of a TVP7002 decoder device register. 6818c2ecf20Sopenharmony_ci * Returns zero when successful, -EINVAL if register read fails or 6828c2ecf20Sopenharmony_ci * access to I2C client fails. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic int tvp7002_g_register(struct v4l2_subdev *sd, 6858c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci u8 val; 6888c2ecf20Sopenharmony_ci int ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci ret = tvp7002_read(sd, reg->reg & 0xff, &val); 6918c2ecf20Sopenharmony_ci if (ret < 0) 6928c2ecf20Sopenharmony_ci return ret; 6938c2ecf20Sopenharmony_ci reg->val = val; 6948c2ecf20Sopenharmony_ci reg->size = 1; 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* 6998c2ecf20Sopenharmony_ci * tvp7002_s_register() - set a control 7008c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 7018c2ecf20Sopenharmony_ci * @reg: ptr to v4l2_dbg_register struct 7028c2ecf20Sopenharmony_ci * 7038c2ecf20Sopenharmony_ci * Get the value of a TVP7002 decoder device register. 7048c2ecf20Sopenharmony_ci * Returns zero when successful, -EINVAL if register read fails. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic int tvp7002_s_register(struct v4l2_subdev *sd, 7078c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci#endif 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* 7148c2ecf20Sopenharmony_ci * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream 7158c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 7168c2ecf20Sopenharmony_ci * @enable: streaming enable or disable 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * Sets streaming to enable or disable, if possible. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_cistatic int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct tvp7002 *device = to_tvp7002(sd); 7238c2ecf20Sopenharmony_ci int error; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (device->streaming == enable) 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* low impedance: on, high impedance: off */ 7298c2ecf20Sopenharmony_ci error = tvp7002_write(sd, TVP7002_MISC_CTL_2, enable ? 0x00 : 0x03); 7308c2ecf20Sopenharmony_ci if (error) { 7318c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "Fail to set streaming\n"); 7328c2ecf20Sopenharmony_ci return error; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci device->streaming = enable; 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* 7408c2ecf20Sopenharmony_ci * tvp7002_log_status() - Print information about register settings 7418c2ecf20Sopenharmony_ci * @sd: ptr to v4l2_subdev struct 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * Log register values of a TVP7002 decoder device. 7448c2ecf20Sopenharmony_ci * Returns zero or -EINVAL if read operation fails. 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_cistatic int tvp7002_log_status(struct v4l2_subdev *sd) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct tvp7002 *device = to_tvp7002(sd); 7498c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt; 7508c2ecf20Sopenharmony_ci int detected; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Find my current timings */ 7538c2ecf20Sopenharmony_ci tvp7002_query_dv(sd, &detected); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci bt = &device->current_timings->timings.bt; 7568c2ecf20Sopenharmony_ci v4l2_info(sd, "Selected DV Timings: %ux%u\n", bt->width, bt->height); 7578c2ecf20Sopenharmony_ci if (detected == NUM_TIMINGS) { 7588c2ecf20Sopenharmony_ci v4l2_info(sd, "Detected DV Timings: None\n"); 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci bt = &tvp7002_timings[detected].timings.bt; 7618c2ecf20Sopenharmony_ci v4l2_info(sd, "Detected DV Timings: %ux%u\n", 7628c2ecf20Sopenharmony_ci bt->width, bt->height); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci v4l2_info(sd, "Streaming enabled: %s\n", 7658c2ecf20Sopenharmony_ci device->streaming ? "yes" : "no"); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Print the current value of the gain control */ 7688c2ecf20Sopenharmony_ci v4l2_ctrl_handler_log_status(&device->hdl, sd->name); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, 7748c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci if (timings->pad != 0) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* Check requested format index is within range */ 7808c2ecf20Sopenharmony_ci if (timings->index >= NUM_TIMINGS) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci timings->timings = tvp7002_timings[timings->index].timings; 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { 7888c2ecf20Sopenharmony_ci .s_ctrl = tvp7002_s_ctrl, 7898c2ecf20Sopenharmony_ci}; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci/* 7928c2ecf20Sopenharmony_ci * tvp7002_enum_mbus_code() - Enum supported digital video format on pad 7938c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 7948c2ecf20Sopenharmony_ci * @cfg: pad configuration 7958c2ecf20Sopenharmony_ci * @code: pointer to subdev enum mbus code struct 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Enumerate supported digital video formats for pad. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_cistatic int 8008c2ecf20Sopenharmony_citvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 8018c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci /* Check requested format index is within range */ 8048c2ecf20Sopenharmony_ci if (code->index != 0) 8058c2ecf20Sopenharmony_ci return -EINVAL; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_YUYV10_1X20; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci/* 8138c2ecf20Sopenharmony_ci * tvp7002_get_pad_format() - get video format on pad 8148c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 8158c2ecf20Sopenharmony_ci * @cfg: pad configuration 8168c2ecf20Sopenharmony_ci * @fmt: pointer to subdev format struct 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci * get video format for pad. 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_cistatic int 8218c2ecf20Sopenharmony_citvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 8228c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct tvp7002 *tvp7002 = to_tvp7002(sd); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci fmt->format.code = MEDIA_BUS_FMT_YUYV10_1X20; 8278c2ecf20Sopenharmony_ci fmt->format.width = tvp7002->current_timings->timings.bt.width; 8288c2ecf20Sopenharmony_ci fmt->format.height = tvp7002->current_timings->timings.bt.height; 8298c2ecf20Sopenharmony_ci fmt->format.field = tvp7002->current_timings->scanmode; 8308c2ecf20Sopenharmony_ci fmt->format.colorspace = tvp7002->current_timings->color_space; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci/* 8368c2ecf20Sopenharmony_ci * tvp7002_set_pad_format() - set video format on pad 8378c2ecf20Sopenharmony_ci * @sd: pointer to standard V4L2 sub-device structure 8388c2ecf20Sopenharmony_ci * @cfg: pad configuration 8398c2ecf20Sopenharmony_ci * @fmt: pointer to subdev format struct 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * set video format for pad. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_cistatic int 8448c2ecf20Sopenharmony_citvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 8458c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci return tvp7002_get_pad_format(sd, cfg, fmt); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci/* V4L2 core operation handlers */ 8518c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops tvp7002_core_ops = { 8528c2ecf20Sopenharmony_ci .log_status = tvp7002_log_status, 8538c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 8548c2ecf20Sopenharmony_ci .g_register = tvp7002_g_register, 8558c2ecf20Sopenharmony_ci .s_register = tvp7002_s_register, 8568c2ecf20Sopenharmony_ci#endif 8578c2ecf20Sopenharmony_ci}; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* Specific video subsystem operation handlers */ 8608c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops tvp7002_video_ops = { 8618c2ecf20Sopenharmony_ci .g_dv_timings = tvp7002_g_dv_timings, 8628c2ecf20Sopenharmony_ci .s_dv_timings = tvp7002_s_dv_timings, 8638c2ecf20Sopenharmony_ci .query_dv_timings = tvp7002_query_dv_timings, 8648c2ecf20Sopenharmony_ci .s_stream = tvp7002_s_stream, 8658c2ecf20Sopenharmony_ci}; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* media pad related operation handlers */ 8688c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops tvp7002_pad_ops = { 8698c2ecf20Sopenharmony_ci .enum_mbus_code = tvp7002_enum_mbus_code, 8708c2ecf20Sopenharmony_ci .get_fmt = tvp7002_get_pad_format, 8718c2ecf20Sopenharmony_ci .set_fmt = tvp7002_set_pad_format, 8728c2ecf20Sopenharmony_ci .enum_dv_timings = tvp7002_enum_dv_timings, 8738c2ecf20Sopenharmony_ci}; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/* V4L2 top level operation handlers */ 8768c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops tvp7002_ops = { 8778c2ecf20Sopenharmony_ci .core = &tvp7002_core_ops, 8788c2ecf20Sopenharmony_ci .video = &tvp7002_video_ops, 8798c2ecf20Sopenharmony_ci .pad = &tvp7002_pad_ops, 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic struct tvp7002_config * 8838c2ecf20Sopenharmony_citvp7002_get_pdata(struct i2c_client *client) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 8868c2ecf20Sopenharmony_ci struct tvp7002_config *pdata = NULL; 8878c2ecf20Sopenharmony_ci struct device_node *endpoint; 8888c2ecf20Sopenharmony_ci unsigned int flags; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) 8918c2ecf20Sopenharmony_ci return client->dev.platform_data; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL); 8948c2ecf20Sopenharmony_ci if (!endpoint) 8958c2ecf20Sopenharmony_ci return NULL; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg)) 8988c2ecf20Sopenharmony_ci goto done; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); 9018c2ecf20Sopenharmony_ci if (!pdata) 9028c2ecf20Sopenharmony_ci goto done; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci flags = bus_cfg.bus.parallel.flags; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 9078c2ecf20Sopenharmony_ci pdata->hs_polarity = 1; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) 9108c2ecf20Sopenharmony_ci pdata->vs_polarity = 1; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) 9138c2ecf20Sopenharmony_ci pdata->clk_polarity = 1; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_FIELD_EVEN_HIGH) 9168c2ecf20Sopenharmony_ci pdata->fid_polarity = 1; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH) 9198c2ecf20Sopenharmony_ci pdata->sog_polarity = 1; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cidone: 9228c2ecf20Sopenharmony_ci of_node_put(endpoint); 9238c2ecf20Sopenharmony_ci return pdata; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* 9278c2ecf20Sopenharmony_ci * tvp7002_probe - Probe a TVP7002 device 9288c2ecf20Sopenharmony_ci * @c: ptr to i2c_client struct 9298c2ecf20Sopenharmony_ci * @id: ptr to i2c_device_id struct 9308c2ecf20Sopenharmony_ci * 9318c2ecf20Sopenharmony_ci * Initialize the TVP7002 device 9328c2ecf20Sopenharmony_ci * Returns zero when successful, -EINVAL if register read fails or 9338c2ecf20Sopenharmony_ci * -EIO if i2c access is not available. 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_cistatic int tvp7002_probe(struct i2c_client *c) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct tvp7002_config *pdata = tvp7002_get_pdata(c); 9388c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 9398c2ecf20Sopenharmony_ci struct tvp7002 *device; 9408c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 9418c2ecf20Sopenharmony_ci int polarity_a; 9428c2ecf20Sopenharmony_ci int polarity_b; 9438c2ecf20Sopenharmony_ci u8 revision; 9448c2ecf20Sopenharmony_ci int error; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (pdata == NULL) { 9478c2ecf20Sopenharmony_ci dev_err(&c->dev, "No platform data\n"); 9488c2ecf20Sopenharmony_ci return -EINVAL; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 9528c2ecf20Sopenharmony_ci if (!i2c_check_functionality(c->adapter, 9538c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 9548c2ecf20Sopenharmony_ci return -EIO; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci device = devm_kzalloc(&c->dev, sizeof(struct tvp7002), GFP_KERNEL); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!device) 9598c2ecf20Sopenharmony_ci return -ENOMEM; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci sd = &device->sd; 9628c2ecf20Sopenharmony_ci device->pdata = pdata; 9638c2ecf20Sopenharmony_ci device->current_timings = tvp7002_timings; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Tell v4l2 the device is ready */ 9668c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); 9678c2ecf20Sopenharmony_ci v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", 9688c2ecf20Sopenharmony_ci c->addr, c->adapter->name); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); 9718c2ecf20Sopenharmony_ci if (error < 0) 9728c2ecf20Sopenharmony_ci return error; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Get revision number */ 9758c2ecf20Sopenharmony_ci v4l2_info(sd, "Rev. %02x detected.\n", revision); 9768c2ecf20Sopenharmony_ci if (revision != 0x02) 9778c2ecf20Sopenharmony_ci v4l2_info(sd, "Unknown revision detected.\n"); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* Initializes TVP7002 to its default values */ 9808c2ecf20Sopenharmony_ci error = tvp7002_write_inittab(sd, tvp7002_init_default); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (error < 0) 9838c2ecf20Sopenharmony_ci return error; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Set polarity information after registers have been set */ 9868c2ecf20Sopenharmony_ci polarity_a = 0x20 | device->pdata->hs_polarity << 5 9878c2ecf20Sopenharmony_ci | device->pdata->vs_polarity << 2; 9888c2ecf20Sopenharmony_ci error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); 9898c2ecf20Sopenharmony_ci if (error < 0) 9908c2ecf20Sopenharmony_ci return error; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci polarity_b = 0x01 | device->pdata->fid_polarity << 2 9938c2ecf20Sopenharmony_ci | device->pdata->sog_polarity << 1 9948c2ecf20Sopenharmony_ci | device->pdata->clk_polarity; 9958c2ecf20Sopenharmony_ci error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); 9968c2ecf20Sopenharmony_ci if (error < 0) 9978c2ecf20Sopenharmony_ci return error; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* Set registers according to default video mode */ 10008c2ecf20Sopenharmony_ci timings = device->current_timings->timings; 10018c2ecf20Sopenharmony_ci error = tvp7002_s_dv_timings(sd, &timings); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER) 10048c2ecf20Sopenharmony_ci device->pad.flags = MEDIA_PAD_FL_SOURCE; 10058c2ecf20Sopenharmony_ci device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 10068c2ecf20Sopenharmony_ci device->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); 10098c2ecf20Sopenharmony_ci if (error < 0) 10108c2ecf20Sopenharmony_ci return error; 10118c2ecf20Sopenharmony_ci#endif 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&device->hdl, 1); 10148c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, 10158c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 255, 1, 0); 10168c2ecf20Sopenharmony_ci sd->ctrl_handler = &device->hdl; 10178c2ecf20Sopenharmony_ci if (device->hdl.error) { 10188c2ecf20Sopenharmony_ci error = device->hdl.error; 10198c2ecf20Sopenharmony_ci goto error; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&device->hdl); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci error = v4l2_async_register_subdev(&device->sd); 10248c2ecf20Sopenharmony_ci if (error) 10258c2ecf20Sopenharmony_ci goto error; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cierror: 10308c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&device->hdl); 10318c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER) 10328c2ecf20Sopenharmony_ci media_entity_cleanup(&device->sd.entity); 10338c2ecf20Sopenharmony_ci#endif 10348c2ecf20Sopenharmony_ci return error; 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci/* 10388c2ecf20Sopenharmony_ci * tvp7002_remove - Remove TVP7002 device support 10398c2ecf20Sopenharmony_ci * @c: ptr to i2c_client struct 10408c2ecf20Sopenharmony_ci * 10418c2ecf20Sopenharmony_ci * Reset the TVP7002 device 10428c2ecf20Sopenharmony_ci * Returns zero. 10438c2ecf20Sopenharmony_ci */ 10448c2ecf20Sopenharmony_cistatic int tvp7002_remove(struct i2c_client *c) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(c); 10478c2ecf20Sopenharmony_ci struct tvp7002 *device = to_tvp7002(sd); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" 10508c2ecf20Sopenharmony_ci "on address 0x%x\n", c->addr); 10518c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&device->sd); 10528c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER) 10538c2ecf20Sopenharmony_ci media_entity_cleanup(&device->sd.entity); 10548c2ecf20Sopenharmony_ci#endif 10558c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&device->hdl); 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/* I2C Device ID table */ 10608c2ecf20Sopenharmony_cistatic const struct i2c_device_id tvp7002_id[] = { 10618c2ecf20Sopenharmony_ci { "tvp7002", 0 }, 10628c2ecf20Sopenharmony_ci { } 10638c2ecf20Sopenharmony_ci}; 10648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tvp7002_id); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 10678c2ecf20Sopenharmony_cistatic const struct of_device_id tvp7002_of_match[] = { 10688c2ecf20Sopenharmony_ci { .compatible = "ti,tvp7002", }, 10698c2ecf20Sopenharmony_ci { /* sentinel */ }, 10708c2ecf20Sopenharmony_ci}; 10718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tvp7002_of_match); 10728c2ecf20Sopenharmony_ci#endif 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/* I2C driver data */ 10758c2ecf20Sopenharmony_cistatic struct i2c_driver tvp7002_driver = { 10768c2ecf20Sopenharmony_ci .driver = { 10778c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(tvp7002_of_match), 10788c2ecf20Sopenharmony_ci .name = TVP7002_MODULE_NAME, 10798c2ecf20Sopenharmony_ci }, 10808c2ecf20Sopenharmony_ci .probe_new = tvp7002_probe, 10818c2ecf20Sopenharmony_ci .remove = tvp7002_remove, 10828c2ecf20Sopenharmony_ci .id_table = tvp7002_id, 10838c2ecf20Sopenharmony_ci}; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cimodule_i2c_driver(tvp7002_driver); 1086