18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * ths8200 - Texas Instruments THS8200 video encoder driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify
78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
88c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
118c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
128c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This program is distributed .as is. WITHOUT ANY WARRANTY of any
158c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
168c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178c2ecf20Sopenharmony_ci * GNU General Public License for more details.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/i2c.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h>
268c2ecf20Sopenharmony_ci#include <media/v4l2-async.h>
278c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "ths8200_regs.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int debug;
328c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-2)");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments THS8200 video encoder driver");
368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>");
378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Bugge <martin.bugge@cisco.com>");
388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct ths8200_state {
418c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
428c2ecf20Sopenharmony_ci	uint8_t chip_version;
438c2ecf20Sopenharmony_ci	/* Is the ths8200 powered on? */
448c2ecf20Sopenharmony_ci	bool power_on;
458c2ecf20Sopenharmony_ci	struct v4l2_dv_timings dv_timings;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap ths8200_timings_cap = {
498c2ecf20Sopenharmony_ci	.type = V4L2_DV_BT_656_1120,
508c2ecf20Sopenharmony_ci	/* keep this initialization for compatibility with GCC < 4.4.6 */
518c2ecf20Sopenharmony_ci	.reserved = { 0 },
528c2ecf20Sopenharmony_ci	V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1080, 25000000, 148500000,
538c2ecf20Sopenharmony_ci		V4L2_DV_BT_STD_CEA861, V4L2_DV_BT_CAP_PROGRESSIVE)
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return container_of(sd, struct ths8200_state, sd);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline unsigned htotal(const struct v4l2_bt_timings *t)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	return V4L2_DV_BT_FRAME_WIDTH(t);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic inline unsigned vtotal(const struct v4l2_bt_timings *t)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	return V4L2_DV_BT_FRAME_HEIGHT(t);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int ths8200_read(struct v4l2_subdev *sd, u8 reg)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return i2c_smbus_read_byte_data(client, reg);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int ths8200_write(struct v4l2_subdev *sd, u8 reg, u8 val)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci	int i;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
858c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client, reg, val);
868c2ecf20Sopenharmony_ci		if (ret == 0)
878c2ecf20Sopenharmony_ci			return 0;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	v4l2_err(sd, "I2C Write Problem\n");
908c2ecf20Sopenharmony_ci	return ret;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* To set specific bits in the register, a clear-mask is given (to be AND-ed),
948c2ecf20Sopenharmony_ci * and then the value-mask (to be OR-ed).
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic inline void
978c2ecf20Sopenharmony_ciths8200_write_and_or(struct v4l2_subdev *sd, u8 reg,
988c2ecf20Sopenharmony_ci		     uint8_t clr_mask, uint8_t val_mask)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	ths8200_write(sd, reg, (ths8200_read(sd, reg) & clr_mask) | val_mask);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int ths8200_g_register(struct v4l2_subdev *sd,
1068c2ecf20Sopenharmony_ci			      struct v4l2_dbg_register *reg)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	reg->val = ths8200_read(sd, reg->reg & 0xff);
1098c2ecf20Sopenharmony_ci	reg->size = 1;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int ths8200_s_register(struct v4l2_subdev *sd,
1158c2ecf20Sopenharmony_ci			      const struct v4l2_dbg_register *reg)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	ths8200_write(sd, reg->reg & 0xff, reg->val & 0xff);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci#endif
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int ths8200_log_status(struct v4l2_subdev *sd)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct ths8200_state *state = to_state(sd);
1268c2ecf20Sopenharmony_ci	uint8_t reg_03 = ths8200_read(sd, THS8200_CHIP_CTL);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	v4l2_info(sd, "----- Chip status -----\n");
1298c2ecf20Sopenharmony_ci	v4l2_info(sd, "version: %u\n", state->chip_version);
1308c2ecf20Sopenharmony_ci	v4l2_info(sd, "power: %s\n", (reg_03 & 0x0c) ? "off" : "on");
1318c2ecf20Sopenharmony_ci	v4l2_info(sd, "reset: %s\n", (reg_03 & 0x01) ? "off" : "on");
1328c2ecf20Sopenharmony_ci	v4l2_info(sd, "test pattern: %s\n",
1338c2ecf20Sopenharmony_ci		  (reg_03 & 0x20) ? "enabled" : "disabled");
1348c2ecf20Sopenharmony_ci	v4l2_info(sd, "format: %ux%u\n",
1358c2ecf20Sopenharmony_ci		  ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_MSB) * 256 +
1368c2ecf20Sopenharmony_ci		  ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_LSB),
1378c2ecf20Sopenharmony_ci		  (ths8200_read(sd, THS8200_DTG2_LINE_CNT_MSB) & 0x07) * 256 +
1388c2ecf20Sopenharmony_ci		  ths8200_read(sd, THS8200_DTG2_LINE_CNT_LSB));
1398c2ecf20Sopenharmony_ci	v4l2_print_dv_timings(sd->name, "Configured format:",
1408c2ecf20Sopenharmony_ci			      &state->dv_timings, true);
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Power up/down ths8200 */
1458c2ecf20Sopenharmony_cistatic int ths8200_s_power(struct v4l2_subdev *sd, int on)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct ths8200_state *state = to_state(sd);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off");
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	state->power_on = on;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* Power up/down - leave in reset state until input video is present */
1548c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0xf2, (on ? 0x00 : 0x0c));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ths8200_core_ops = {
1608c2ecf20Sopenharmony_ci	.log_status = ths8200_log_status,
1618c2ecf20Sopenharmony_ci	.s_power = ths8200_s_power,
1628c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
1638c2ecf20Sopenharmony_ci	.g_register = ths8200_g_register,
1648c2ecf20Sopenharmony_ci	.s_register = ths8200_s_register,
1658c2ecf20Sopenharmony_ci#endif
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
1698c2ecf20Sopenharmony_ci * V4L2 subdev video operations
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int ths8200_s_stream(struct v4l2_subdev *sd, int enable)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct ths8200_state *state = to_state(sd);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (enable && !state->power_on)
1778c2ecf20Sopenharmony_ci		ths8200_s_power(sd, true);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0xfe,
1808c2ecf20Sopenharmony_ci			     (enable ? 0x01 : 0x00));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: %sable\n",
1838c2ecf20Sopenharmony_ci		 __func__, (enable ? "en" : "dis"));
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void ths8200_core_init(struct v4l2_subdev *sd)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	/* setup clocks */
1918c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0x3f, 0xc0);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/**** Data path control (DATA) ****/
1948c2ecf20Sopenharmony_ci	/* Set FSADJ 700 mV,
1958c2ecf20Sopenharmony_ci	 * bypass 422-444 interpolation,
1968c2ecf20Sopenharmony_ci	 * input format 30 bit RGB444
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DATA_CNTL, 0x70);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* DTG Mode (Video blocked during blanking
2018c2ecf20Sopenharmony_ci	 * VESA slave
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_MODE, 0x87);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/**** Display Timing Generator Control, Part 1 (DTG1). ****/
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Disable embedded syncs on the output by setting
2088c2ecf20Sopenharmony_ci	 * the amplitude to zero for all channels.
2098c2ecf20Sopenharmony_ci	 */
2108c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x00);
2118c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x00);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	uint8_t polarity = 0;
2178c2ecf20Sopenharmony_ci	uint16_t line_start_active_video = (bt->vsync + bt->vbackporch);
2188c2ecf20Sopenharmony_ci	uint16_t line_start_front_porch  = (vtotal(bt) - bt->vfrontporch);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*** System ****/
2218c2ecf20Sopenharmony_ci	/* Set chip in reset while it is configured */
2228c2ecf20Sopenharmony_ci	ths8200_s_stream(sd, false);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* configure video output timings */
2258c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_A, bt->hsync);
2268c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_B, bt->hfrontporch);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Zero for progressive scan formats.*/
2298c2ecf20Sopenharmony_ci	if (!bt->interlaced)
2308c2ecf20Sopenharmony_ci		ths8200_write(sd, THS8200_DTG1_SPEC_C, 0x00);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Distance from leading edge of h sync to start of active video.
2338c2ecf20Sopenharmony_ci	 * MSB in 0x2b
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_D_LSB,
2368c2ecf20Sopenharmony_ci		      (bt->hbackporch + bt->hsync) & 0xff);
2378c2ecf20Sopenharmony_ci	/* Zero for SDTV-mode. MSB in 0x2b */
2388c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_E_LSB, 0x00);
2398c2ecf20Sopenharmony_ci	/*
2408c2ecf20Sopenharmony_ci	 * MSB for dtg1_spec(d/e/h). See comment for
2418c2ecf20Sopenharmony_ci	 * corresponding LSB registers.
2428c2ecf20Sopenharmony_ci	 */
2438c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_DEH_MSB,
2448c2ecf20Sopenharmony_ci		      ((bt->hbackporch + bt->hsync) & 0x100) >> 1);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* h front porch */
2478c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_K_LSB, (bt->hfrontporch) & 0xff);
2488c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_K_MSB,
2498c2ecf20Sopenharmony_ci		      ((bt->hfrontporch) & 0x700) >> 8);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Half the line length. Used to calculate SDTV line types. */
2528c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_G_LSB, (htotal(bt)/2) & 0xff);
2538c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_SPEC_G_MSB,
2548c2ecf20Sopenharmony_ci		      ((htotal(bt)/2) >> 8) & 0x0f);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* Total pixels per line (ex. 720p: 1650) */
2578c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_TOT_PIXELS_MSB, htotal(bt) >> 8);
2588c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_TOT_PIXELS_LSB, htotal(bt) & 0xff);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* Frame height and field height */
2618c2ecf20Sopenharmony_ci	/* Field height should be programmed higher than frame_size for
2628c2ecf20Sopenharmony_ci	 * progressive scan formats
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_FRAME_FIELD_SZ_MSB,
2658c2ecf20Sopenharmony_ci		      ((vtotal(bt) >> 4) & 0xf0) + 0x7);
2668c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG1_FRAME_SZ_LSB, vtotal(bt) & 0xff);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* Should be programmed higher than frame_size
2698c2ecf20Sopenharmony_ci	 * for progressive formats
2708c2ecf20Sopenharmony_ci	 */
2718c2ecf20Sopenharmony_ci	if (!bt->interlaced)
2728c2ecf20Sopenharmony_ci		ths8200_write(sd, THS8200_DTG1_FIELD_SZ_LSB, 0xff);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/**** Display Timing Generator Control, Part 2 (DTG2). ****/
2758c2ecf20Sopenharmony_ci	/* Set breakpoint line numbers and types
2768c2ecf20Sopenharmony_ci	 * THS8200 generates line types with different properties. A line type
2778c2ecf20Sopenharmony_ci	 * that sets all the RGB-outputs to zero is used in the blanking areas,
2788c2ecf20Sopenharmony_ci	 * while a line type that enable the RGB-outputs is used in active video
2798c2ecf20Sopenharmony_ci	 * area. The line numbers for start of active video, start of front
2808c2ecf20Sopenharmony_ci	 * porch and after the last line in the frame must be set with the
2818c2ecf20Sopenharmony_ci	 * corresponding line types.
2828c2ecf20Sopenharmony_ci	 *
2838c2ecf20Sopenharmony_ci	 * Line types:
2848c2ecf20Sopenharmony_ci	 * 0x9 - Full normal sync pulse: Blocks data when dtg1_pass is off.
2858c2ecf20Sopenharmony_ci	 *       Used in blanking area.
2868c2ecf20Sopenharmony_ci	 * 0x0 - Active video: Video data is always passed. Used in active
2878c2ecf20Sopenharmony_ci	 *       video area.
2888c2ecf20Sopenharmony_ci	 */
2898c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_DTG2_BP1_2_MSB, 0x88,
2908c2ecf20Sopenharmony_ci			     ((line_start_active_video >> 4) & 0x70) +
2918c2ecf20Sopenharmony_ci			     ((line_start_front_porch >> 8) & 0x07));
2928c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_BP3_4_MSB, ((vtotal(bt)) >> 4) & 0x70);
2938c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_BP1_LSB, line_start_active_video & 0xff);
2948c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_BP2_LSB, line_start_front_porch & 0xff);
2958c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_BP3_LSB, (vtotal(bt)) & 0xff);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* line types */
2988c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_LINETYPE1, 0x90);
2998c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_LINETYPE2, 0x90);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* h sync width transmitted */
3028c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_HLENGTH_LSB, bt->hsync & 0xff);
3038c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB, 0x3f,
3048c2ecf20Sopenharmony_ci			     (bt->hsync >> 2) & 0xc0);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* The pixel value h sync is asserted on */
3078c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB, 0xe0,
3088c2ecf20Sopenharmony_ci			     (htotal(bt) >> 8) & 0x1f);
3098c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, htotal(bt));
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* v sync width transmitted (must add 1 to get correct output) */
3128c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync + 1) & 0xff);
3138c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0x3f,
3148c2ecf20Sopenharmony_ci			     ((bt->vsync + 1) >> 2) & 0xc0);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* The pixel value v sync is asserted on (must add 1 to get correct output) */
3178c2ecf20Sopenharmony_ci	ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0xf8,
3188c2ecf20Sopenharmony_ci			     ((vtotal(bt) + 1) >> 8) & 0x7);
3198c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt) + 1);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* For progressive video vlength2 must be set to all 0 and vdly2 must
3228c2ecf20Sopenharmony_ci	 * be set to all 1.
3238c2ecf20Sopenharmony_ci	 */
3248c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VLENGTH2_LSB, 0x00);
3258c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VLENGTH2_MSB_VDLY2_MSB, 0x07);
3268c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VDLY2_LSB, 0xff);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* Internal delay factors to synchronize the sync pulses and the data */
3298c2ecf20Sopenharmony_ci	/* Experimental values delays (hor 0, ver 0) */
3308c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, 0);
3318c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, 0);
3328c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_MSB, 0);
3338c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 0);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Polarity of received and transmitted sync signals */
3368c2ecf20Sopenharmony_ci	if (bt->polarities & V4L2_DV_HSYNC_POS_POL) {
3378c2ecf20Sopenharmony_ci		polarity |= 0x01; /* HS_IN */
3388c2ecf20Sopenharmony_ci		polarity |= 0x08; /* HS_OUT */
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	if (bt->polarities & V4L2_DV_VSYNC_POS_POL) {
3418c2ecf20Sopenharmony_ci		polarity |= 0x02; /* VS_IN */
3428c2ecf20Sopenharmony_ci		polarity |= 0x10; /* VS_OUT */
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* RGB mode, no embedded timings */
3468c2ecf20Sopenharmony_ci	/* Timing of video input bus is derived from HS, VS, and FID dedicated
3478c2ecf20Sopenharmony_ci	 * inputs
3488c2ecf20Sopenharmony_ci	 */
3498c2ecf20Sopenharmony_ci	ths8200_write(sd, THS8200_DTG2_CNTL, 0x44 | polarity);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* leave reset */
3528c2ecf20Sopenharmony_ci	ths8200_s_stream(sd, true);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: frame %dx%d, polarity %d\n"
3558c2ecf20Sopenharmony_ci		 "horizontal: front porch %d, back porch %d, sync %d\n"
3568c2ecf20Sopenharmony_ci		 "vertical: sync %d\n", __func__, htotal(bt), vtotal(bt),
3578c2ecf20Sopenharmony_ci		 polarity, bt->hfrontporch, bt->hbackporch,
3588c2ecf20Sopenharmony_ci		 bt->hsync, bt->vsync);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int ths8200_s_dv_timings(struct v4l2_subdev *sd,
3628c2ecf20Sopenharmony_ci				struct v4l2_dv_timings *timings)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct ths8200_state *state = to_state(sd);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s:\n", __func__);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap,
3698c2ecf20Sopenharmony_ci				NULL, NULL))
3708c2ecf20Sopenharmony_ci		return -EINVAL;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10,
3738c2ecf20Sopenharmony_ci				NULL, NULL)) {
3748c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "Unsupported format\n");
3758c2ecf20Sopenharmony_ci		return -EINVAL;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* save timings */
3818c2ecf20Sopenharmony_ci	state->dv_timings = *timings;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	ths8200_setup(sd, &timings->bt);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int ths8200_g_dv_timings(struct v4l2_subdev *sd,
3898c2ecf20Sopenharmony_ci				struct v4l2_dv_timings *timings)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct ths8200_state *state = to_state(sd);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s:\n", __func__);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	*timings = state->dv_timings;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
4018c2ecf20Sopenharmony_ci				   struct v4l2_enum_dv_timings *timings)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	if (timings->pad != 0)
4048c2ecf20Sopenharmony_ci		return -EINVAL;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap,
4078c2ecf20Sopenharmony_ci			NULL, NULL);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
4118c2ecf20Sopenharmony_ci				  struct v4l2_dv_timings_cap *cap)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	if (cap->pad != 0)
4148c2ecf20Sopenharmony_ci		return -EINVAL;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	*cap = ths8200_timings_cap;
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci/* Specific video subsystem operation handlers */
4218c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ths8200_video_ops = {
4228c2ecf20Sopenharmony_ci	.s_stream = ths8200_s_stream,
4238c2ecf20Sopenharmony_ci	.s_dv_timings = ths8200_s_dv_timings,
4248c2ecf20Sopenharmony_ci	.g_dv_timings = ths8200_g_dv_timings,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
4288c2ecf20Sopenharmony_ci	.enum_dv_timings = ths8200_enum_dv_timings,
4298c2ecf20Sopenharmony_ci	.dv_timings_cap = ths8200_dv_timings_cap,
4308c2ecf20Sopenharmony_ci};
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/* V4L2 top level operation handlers */
4338c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ths8200_ops = {
4348c2ecf20Sopenharmony_ci	.core  = &ths8200_core_ops,
4358c2ecf20Sopenharmony_ci	.video = &ths8200_video_ops,
4368c2ecf20Sopenharmony_ci	.pad = &ths8200_pad_ops,
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int ths8200_probe(struct i2c_client *client)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct ths8200_state *state;
4428c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
4438c2ecf20Sopenharmony_ci	int error;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* Check if the adapter supports the needed features */
4468c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
4478c2ecf20Sopenharmony_ci		return -EIO;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
4508c2ecf20Sopenharmony_ci	if (!state)
4518c2ecf20Sopenharmony_ci		return -ENOMEM;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	sd = &state->sd;
4548c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &ths8200_ops);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	state->chip_version = ths8200_read(sd, THS8200_VERSION);
4578c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "chip version 0x%x\n", state->chip_version);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	ths8200_core_init(sd);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	error = v4l2_async_register_subdev(&state->sd);
4628c2ecf20Sopenharmony_ci	if (error)
4638c2ecf20Sopenharmony_ci		return error;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
4668c2ecf20Sopenharmony_ci		  client->addr << 1, client->adapter->name);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int ths8200_remove(struct i2c_client *client)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
4748c2ecf20Sopenharmony_ci	struct ths8200_state *decoder = to_state(sd);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
4778c2ecf20Sopenharmony_ci		 client->addr << 1, client->adapter->name);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	ths8200_s_power(sd, false);
4808c2ecf20Sopenharmony_ci	v4l2_async_unregister_subdev(&decoder->sd);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic const struct i2c_device_id ths8200_id[] = {
4868c2ecf20Sopenharmony_ci	{ "ths8200", 0 },
4878c2ecf20Sopenharmony_ci	{},
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ths8200_id);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
4928c2ecf20Sopenharmony_cistatic const struct of_device_id ths8200_of_match[] = {
4938c2ecf20Sopenharmony_ci	{ .compatible = "ti,ths8200", },
4948c2ecf20Sopenharmony_ci	{ /* sentinel */ },
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ths8200_of_match);
4978c2ecf20Sopenharmony_ci#endif
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic struct i2c_driver ths8200_driver = {
5008c2ecf20Sopenharmony_ci	.driver = {
5018c2ecf20Sopenharmony_ci		.name = "ths8200",
5028c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(ths8200_of_match),
5038c2ecf20Sopenharmony_ci	},
5048c2ecf20Sopenharmony_ci	.probe_new = ths8200_probe,
5058c2ecf20Sopenharmony_ci	.remove = ths8200_remove,
5068c2ecf20Sopenharmony_ci	.id_table = ths8200_id,
5078c2ecf20Sopenharmony_ci};
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cimodule_i2c_driver(ths8200_driver);
510