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