18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * tc358767 eDP bridge driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 CogentEmbedded Inc
68c2ecf20Sopenharmony_ci * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2016 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Copyright (C) 2016 Zodiac Inflight Innovations
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Initially based on: drivers/gpu/drm/i2c/tda998x_drv.c
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments
158c2ecf20Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
198c2ecf20Sopenharmony_ci#include <linux/clk.h>
208c2ecf20Sopenharmony_ci#include <linux/device.h>
218c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <linux/regmap.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
298c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
308c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h>
318c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
328c2ecf20Sopenharmony_ci#include <drm/drm_of.h>
338c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
348c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
358c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Registers */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Display Parallel Interface */
408c2ecf20Sopenharmony_ci#define DPIPXLFMT		0x0440
418c2ecf20Sopenharmony_ci#define VS_POL_ACTIVE_LOW		(1 << 10)
428c2ecf20Sopenharmony_ci#define HS_POL_ACTIVE_LOW		(1 << 9)
438c2ecf20Sopenharmony_ci#define DE_POL_ACTIVE_HIGH		(0 << 8)
448c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG1		(0 << 2) /* LSB aligned */
458c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG2		(1 << 2) /* Loosely Packed */
468c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG3		(2 << 2) /* LSB aligned 8-bit */
478c2ecf20Sopenharmony_ci#define DPI_BPP_RGB888			(0 << 0)
488c2ecf20Sopenharmony_ci#define DPI_BPP_RGB666			(1 << 0)
498c2ecf20Sopenharmony_ci#define DPI_BPP_RGB565			(2 << 0)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Video Path */
528c2ecf20Sopenharmony_ci#define VPCTRL0			0x0450
538c2ecf20Sopenharmony_ci#define VSDELAY			GENMASK(31, 20)
548c2ecf20Sopenharmony_ci#define OPXLFMT_RGB666			(0 << 8)
558c2ecf20Sopenharmony_ci#define OPXLFMT_RGB888			(1 << 8)
568c2ecf20Sopenharmony_ci#define FRMSYNC_DISABLED		(0 << 4) /* Video Timing Gen Disabled */
578c2ecf20Sopenharmony_ci#define FRMSYNC_ENABLED			(1 << 4) /* Video Timing Gen Enabled */
588c2ecf20Sopenharmony_ci#define MSF_DISABLED			(0 << 0) /* Magic Square FRC disabled */
598c2ecf20Sopenharmony_ci#define MSF_ENABLED			(1 << 0) /* Magic Square FRC enabled */
608c2ecf20Sopenharmony_ci#define HTIM01			0x0454
618c2ecf20Sopenharmony_ci#define HPW			GENMASK(8, 0)
628c2ecf20Sopenharmony_ci#define HBPR			GENMASK(24, 16)
638c2ecf20Sopenharmony_ci#define HTIM02			0x0458
648c2ecf20Sopenharmony_ci#define HDISPR			GENMASK(10, 0)
658c2ecf20Sopenharmony_ci#define HFPR			GENMASK(24, 16)
668c2ecf20Sopenharmony_ci#define VTIM01			0x045c
678c2ecf20Sopenharmony_ci#define VSPR			GENMASK(7, 0)
688c2ecf20Sopenharmony_ci#define VBPR			GENMASK(23, 16)
698c2ecf20Sopenharmony_ci#define VTIM02			0x0460
708c2ecf20Sopenharmony_ci#define VFPR			GENMASK(23, 16)
718c2ecf20Sopenharmony_ci#define VDISPR			GENMASK(10, 0)
728c2ecf20Sopenharmony_ci#define VFUEN0			0x0464
738c2ecf20Sopenharmony_ci#define VFUEN				BIT(0)   /* Video Frame Timing Upload */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* System */
768c2ecf20Sopenharmony_ci#define TC_IDREG		0x0500
778c2ecf20Sopenharmony_ci#define SYSSTAT			0x0508
788c2ecf20Sopenharmony_ci#define SYSCTRL			0x0510
798c2ecf20Sopenharmony_ci#define DP0_AUDSRC_NO_INPUT		(0 << 3)
808c2ecf20Sopenharmony_ci#define DP0_AUDSRC_I2S_RX		(1 << 3)
818c2ecf20Sopenharmony_ci#define DP0_VIDSRC_NO_INPUT		(0 << 0)
828c2ecf20Sopenharmony_ci#define DP0_VIDSRC_DSI_RX		(1 << 0)
838c2ecf20Sopenharmony_ci#define DP0_VIDSRC_DPI_RX		(2 << 0)
848c2ecf20Sopenharmony_ci#define DP0_VIDSRC_COLOR_BAR		(3 << 0)
858c2ecf20Sopenharmony_ci#define SYSRSTENB		0x050c
868c2ecf20Sopenharmony_ci#define ENBI2C				(1 << 0)
878c2ecf20Sopenharmony_ci#define ENBLCD0				(1 << 2)
888c2ecf20Sopenharmony_ci#define ENBBM				(1 << 3)
898c2ecf20Sopenharmony_ci#define ENBDSIRX			(1 << 4)
908c2ecf20Sopenharmony_ci#define ENBREG				(1 << 5)
918c2ecf20Sopenharmony_ci#define ENBHDCP				(1 << 8)
928c2ecf20Sopenharmony_ci#define GPIOM			0x0540
938c2ecf20Sopenharmony_ci#define GPIOC			0x0544
948c2ecf20Sopenharmony_ci#define GPIOO			0x0548
958c2ecf20Sopenharmony_ci#define GPIOI			0x054c
968c2ecf20Sopenharmony_ci#define INTCTL_G		0x0560
978c2ecf20Sopenharmony_ci#define INTSTS_G		0x0564
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define INT_SYSERR		BIT(16)
1008c2ecf20Sopenharmony_ci#define INT_GPIO_H(x)		(1 << (x == 0 ? 2 : 10))
1018c2ecf20Sopenharmony_ci#define INT_GPIO_LC(x)		(1 << (x == 0 ? 3 : 11))
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define INT_GP0_LCNT		0x0584
1048c2ecf20Sopenharmony_ci#define INT_GP1_LCNT		0x0588
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Control */
1078c2ecf20Sopenharmony_ci#define DP0CTL			0x0600
1088c2ecf20Sopenharmony_ci#define VID_MN_GEN			BIT(6)   /* Auto-generate M/N values */
1098c2ecf20Sopenharmony_ci#define EF_EN				BIT(5)   /* Enable Enhanced Framing */
1108c2ecf20Sopenharmony_ci#define VID_EN				BIT(1)   /* Video transmission enable */
1118c2ecf20Sopenharmony_ci#define DP_EN				BIT(0)   /* Enable DPTX function */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Clocks */
1148c2ecf20Sopenharmony_ci#define DP0_VIDMNGEN0		0x0610
1158c2ecf20Sopenharmony_ci#define DP0_VIDMNGEN1		0x0614
1168c2ecf20Sopenharmony_ci#define DP0_VMNGENSTATUS	0x0618
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Main Channel */
1198c2ecf20Sopenharmony_ci#define DP0_SECSAMPLE		0x0640
1208c2ecf20Sopenharmony_ci#define DP0_VIDSYNCDELAY	0x0644
1218c2ecf20Sopenharmony_ci#define VID_SYNC_DLY		GENMASK(15, 0)
1228c2ecf20Sopenharmony_ci#define THRESH_DLY		GENMASK(31, 16)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define DP0_TOTALVAL		0x0648
1258c2ecf20Sopenharmony_ci#define H_TOTAL			GENMASK(15, 0)
1268c2ecf20Sopenharmony_ci#define V_TOTAL			GENMASK(31, 16)
1278c2ecf20Sopenharmony_ci#define DP0_STARTVAL		0x064c
1288c2ecf20Sopenharmony_ci#define H_START			GENMASK(15, 0)
1298c2ecf20Sopenharmony_ci#define V_START			GENMASK(31, 16)
1308c2ecf20Sopenharmony_ci#define DP0_ACTIVEVAL		0x0650
1318c2ecf20Sopenharmony_ci#define H_ACT			GENMASK(15, 0)
1328c2ecf20Sopenharmony_ci#define V_ACT			GENMASK(31, 16)
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#define DP0_SYNCVAL		0x0654
1358c2ecf20Sopenharmony_ci#define VS_WIDTH		GENMASK(30, 16)
1368c2ecf20Sopenharmony_ci#define HS_WIDTH		GENMASK(14, 0)
1378c2ecf20Sopenharmony_ci#define SYNCVAL_HS_POL_ACTIVE_LOW	(1 << 15)
1388c2ecf20Sopenharmony_ci#define SYNCVAL_VS_POL_ACTIVE_LOW	(1 << 31)
1398c2ecf20Sopenharmony_ci#define DP0_MISC		0x0658
1408c2ecf20Sopenharmony_ci#define TU_SIZE_RECOMMENDED		(63) /* LSCLK cycles per TU */
1418c2ecf20Sopenharmony_ci#define MAX_TU_SYMBOL		GENMASK(28, 23)
1428c2ecf20Sopenharmony_ci#define TU_SIZE			GENMASK(21, 16)
1438c2ecf20Sopenharmony_ci#define BPC_6				(0 << 5)
1448c2ecf20Sopenharmony_ci#define BPC_8				(1 << 5)
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* AUX channel */
1478c2ecf20Sopenharmony_ci#define DP0_AUXCFG0		0x0660
1488c2ecf20Sopenharmony_ci#define DP0_AUXCFG0_BSIZE	GENMASK(11, 8)
1498c2ecf20Sopenharmony_ci#define DP0_AUXCFG0_ADDR_ONLY	BIT(4)
1508c2ecf20Sopenharmony_ci#define DP0_AUXCFG1		0x0664
1518c2ecf20Sopenharmony_ci#define AUX_RX_FILTER_EN		BIT(16)
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#define DP0_AUXADDR		0x0668
1548c2ecf20Sopenharmony_ci#define DP0_AUXWDATA(i)		(0x066c + (i) * 4)
1558c2ecf20Sopenharmony_ci#define DP0_AUXRDATA(i)		(0x067c + (i) * 4)
1568c2ecf20Sopenharmony_ci#define DP0_AUXSTATUS		0x068c
1578c2ecf20Sopenharmony_ci#define AUX_BYTES		GENMASK(15, 8)
1588c2ecf20Sopenharmony_ci#define AUX_STATUS		GENMASK(7, 4)
1598c2ecf20Sopenharmony_ci#define AUX_TIMEOUT		BIT(1)
1608c2ecf20Sopenharmony_ci#define AUX_BUSY		BIT(0)
1618c2ecf20Sopenharmony_ci#define DP0_AUXI2CADR		0x0698
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* Link Training */
1648c2ecf20Sopenharmony_ci#define DP0_SRCCTRL		0x06a0
1658c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_SCRMBLDIS		BIT(13)
1668c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_EN810B		BIT(12)
1678c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_NOTP		(0 << 8)
1688c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_TP1			(1 << 8)
1698c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_TP2			(2 << 8)
1708c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANESKEW		BIT(7)
1718c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_SSCG		BIT(3)
1728c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANES_1		(0 << 2)
1738c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANES_2		(1 << 2)
1748c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_BW27		(1 << 1)
1758c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_BW162		(0 << 1)
1768c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_AUTOCORRECT		BIT(0)
1778c2ecf20Sopenharmony_ci#define DP0_LTSTAT		0x06d0
1788c2ecf20Sopenharmony_ci#define LT_LOOPDONE			BIT(13)
1798c2ecf20Sopenharmony_ci#define LT_STATUS_MASK			(0x1f << 8)
1808c2ecf20Sopenharmony_ci#define LT_CHANNEL1_EQ_BITS		(DP_CHANNEL_EQ_BITS << 4)
1818c2ecf20Sopenharmony_ci#define LT_INTERLANE_ALIGN_DONE		BIT(3)
1828c2ecf20Sopenharmony_ci#define LT_CHANNEL0_EQ_BITS		(DP_CHANNEL_EQ_BITS)
1838c2ecf20Sopenharmony_ci#define DP0_SNKLTCHGREQ		0x06d4
1848c2ecf20Sopenharmony_ci#define DP0_LTLOOPCTRL		0x06d8
1858c2ecf20Sopenharmony_ci#define DP0_SNKLTCTRL		0x06e4
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci#define DP1_SRCCTRL		0x07a0
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/* PHY */
1908c2ecf20Sopenharmony_ci#define DP_PHY_CTRL		0x0800
1918c2ecf20Sopenharmony_ci#define DP_PHY_RST			BIT(28)  /* DP PHY Global Soft Reset */
1928c2ecf20Sopenharmony_ci#define BGREN				BIT(25)  /* AUX PHY BGR Enable */
1938c2ecf20Sopenharmony_ci#define PWR_SW_EN			BIT(24)  /* PHY Power Switch Enable */
1948c2ecf20Sopenharmony_ci#define PHY_M1_RST			BIT(12)  /* Reset PHY1 Main Channel */
1958c2ecf20Sopenharmony_ci#define PHY_RDY				BIT(16)  /* PHY Main Channels Ready */
1968c2ecf20Sopenharmony_ci#define PHY_M0_RST			BIT(8)   /* Reset PHY0 Main Channel */
1978c2ecf20Sopenharmony_ci#define PHY_2LANE			BIT(2)   /* PHY Enable 2 lanes */
1988c2ecf20Sopenharmony_ci#define PHY_A0_EN			BIT(1)   /* PHY Aux Channel0 Enable */
1998c2ecf20Sopenharmony_ci#define PHY_M0_EN			BIT(0)   /* PHY Main Channel0 Enable */
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* PLL */
2028c2ecf20Sopenharmony_ci#define DP0_PLLCTRL		0x0900
2038c2ecf20Sopenharmony_ci#define DP1_PLLCTRL		0x0904	/* not defined in DS */
2048c2ecf20Sopenharmony_ci#define PXL_PLLCTRL		0x0908
2058c2ecf20Sopenharmony_ci#define PLLUPDATE			BIT(2)
2068c2ecf20Sopenharmony_ci#define PLLBYP				BIT(1)
2078c2ecf20Sopenharmony_ci#define PLLEN				BIT(0)
2088c2ecf20Sopenharmony_ci#define PXL_PLLPARAM		0x0914
2098c2ecf20Sopenharmony_ci#define IN_SEL_REFCLK			(0 << 14)
2108c2ecf20Sopenharmony_ci#define SYS_PLLPARAM		0x0918
2118c2ecf20Sopenharmony_ci#define REF_FREQ_38M4			(0 << 8) /* 38.4 MHz */
2128c2ecf20Sopenharmony_ci#define REF_FREQ_19M2			(1 << 8) /* 19.2 MHz */
2138c2ecf20Sopenharmony_ci#define REF_FREQ_26M			(2 << 8) /* 26 MHz */
2148c2ecf20Sopenharmony_ci#define REF_FREQ_13M			(3 << 8) /* 13 MHz */
2158c2ecf20Sopenharmony_ci#define SYSCLK_SEL_LSCLK		(0 << 4)
2168c2ecf20Sopenharmony_ci#define LSCLK_DIV_1			(0 << 0)
2178c2ecf20Sopenharmony_ci#define LSCLK_DIV_2			(1 << 0)
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* Test & Debug */
2208c2ecf20Sopenharmony_ci#define TSTCTL			0x0a00
2218c2ecf20Sopenharmony_ci#define COLOR_R			GENMASK(31, 24)
2228c2ecf20Sopenharmony_ci#define COLOR_G			GENMASK(23, 16)
2238c2ecf20Sopenharmony_ci#define COLOR_B			GENMASK(15, 8)
2248c2ecf20Sopenharmony_ci#define ENI2CFILTER		BIT(4)
2258c2ecf20Sopenharmony_ci#define COLOR_BAR_MODE		GENMASK(1, 0)
2268c2ecf20Sopenharmony_ci#define COLOR_BAR_MODE_BARS	2
2278c2ecf20Sopenharmony_ci#define PLL_DBG			0x0a04
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic bool tc_test_pattern;
2308c2ecf20Sopenharmony_cimodule_param_named(test, tc_test_pattern, bool, 0644);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistruct tc_edp_link {
2338c2ecf20Sopenharmony_ci	u8			dpcd[DP_RECEIVER_CAP_SIZE];
2348c2ecf20Sopenharmony_ci	unsigned int		rate;
2358c2ecf20Sopenharmony_ci	u8			num_lanes;
2368c2ecf20Sopenharmony_ci	u8			assr;
2378c2ecf20Sopenharmony_ci	bool			scrambler_dis;
2388c2ecf20Sopenharmony_ci	bool			spread;
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistruct tc_data {
2428c2ecf20Sopenharmony_ci	struct device		*dev;
2438c2ecf20Sopenharmony_ci	struct regmap		*regmap;
2448c2ecf20Sopenharmony_ci	struct drm_dp_aux	aux;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	struct drm_bridge	bridge;
2478c2ecf20Sopenharmony_ci	struct drm_bridge	*panel_bridge;
2488c2ecf20Sopenharmony_ci	struct drm_connector	connector;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* link settings */
2518c2ecf20Sopenharmony_ci	struct tc_edp_link	link;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* current mode */
2548c2ecf20Sopenharmony_ci	struct drm_display_mode	mode;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	u32			rev;
2578c2ecf20Sopenharmony_ci	u8			assr;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	struct gpio_desc	*sd_gpio;
2608c2ecf20Sopenharmony_ci	struct gpio_desc	*reset_gpio;
2618c2ecf20Sopenharmony_ci	struct clk		*refclk;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* do we have IRQ */
2648c2ecf20Sopenharmony_ci	bool			have_irq;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* HPD pin number (0 or 1) or -ENODEV */
2678c2ecf20Sopenharmony_ci	int			hpd_pin;
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic inline struct tc_data *aux_to_tc(struct drm_dp_aux *a)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return container_of(a, struct tc_data, aux);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic inline struct tc_data *bridge_to_tc(struct drm_bridge *b)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	return container_of(b, struct tc_data, bridge);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic inline struct tc_data *connector_to_tc(struct drm_connector *c)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return container_of(c, struct tc_data, connector);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic inline int tc_poll_timeout(struct tc_data *tc, unsigned int addr,
2868c2ecf20Sopenharmony_ci				  unsigned int cond_mask,
2878c2ecf20Sopenharmony_ci				  unsigned int cond_value,
2888c2ecf20Sopenharmony_ci				  unsigned long sleep_us, u64 timeout_us)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	unsigned int val;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return regmap_read_poll_timeout(tc->regmap, addr, val,
2938c2ecf20Sopenharmony_ci					(val & cond_mask) == cond_value,
2948c2ecf20Sopenharmony_ci					sleep_us, timeout_us);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int tc_aux_wait_busy(struct tc_data *tc)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	return tc_poll_timeout(tc, DP0_AUXSTATUS, AUX_BUSY, 0, 100, 100000);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int tc_aux_write_data(struct tc_data *tc, const void *data,
3038c2ecf20Sopenharmony_ci			     size_t size)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	u32 auxwdata[DP_AUX_MAX_PAYLOAD_BYTES / sizeof(u32)] = { 0 };
3068c2ecf20Sopenharmony_ci	int ret, count = ALIGN(size, sizeof(u32));
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	memcpy(auxwdata, data, size);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	ret = regmap_raw_write(tc->regmap, DP0_AUXWDATA(0), auxwdata, count);
3118c2ecf20Sopenharmony_ci	if (ret)
3128c2ecf20Sopenharmony_ci		return ret;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return size;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int tc_aux_read_data(struct tc_data *tc, void *data, size_t size)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	u32 auxrdata[DP_AUX_MAX_PAYLOAD_BYTES / sizeof(u32)];
3208c2ecf20Sopenharmony_ci	int ret, count = ALIGN(size, sizeof(u32));
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	ret = regmap_raw_read(tc->regmap, DP0_AUXRDATA(0), auxrdata, count);
3238c2ecf20Sopenharmony_ci	if (ret)
3248c2ecf20Sopenharmony_ci		return ret;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	memcpy(data, auxrdata, size);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return size;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic u32 tc_auxcfg0(struct drm_dp_aux_msg *msg, size_t size)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	u32 auxcfg0 = msg->request;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (size)
3368c2ecf20Sopenharmony_ci		auxcfg0 |= FIELD_PREP(DP0_AUXCFG0_BSIZE, size - 1);
3378c2ecf20Sopenharmony_ci	else
3388c2ecf20Sopenharmony_ci		auxcfg0 |= DP0_AUXCFG0_ADDR_ONLY;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return auxcfg0;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic ssize_t tc_aux_transfer(struct drm_dp_aux *aux,
3448c2ecf20Sopenharmony_ci			       struct drm_dp_aux_msg *msg)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct tc_data *tc = aux_to_tc(aux);
3478c2ecf20Sopenharmony_ci	size_t size = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES - 1, msg->size);
3488c2ecf20Sopenharmony_ci	u8 request = msg->request & ~DP_AUX_I2C_MOT;
3498c2ecf20Sopenharmony_ci	u32 auxstatus;
3508c2ecf20Sopenharmony_ci	int ret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	ret = tc_aux_wait_busy(tc);
3538c2ecf20Sopenharmony_ci	if (ret)
3548c2ecf20Sopenharmony_ci		return ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	switch (request) {
3578c2ecf20Sopenharmony_ci	case DP_AUX_NATIVE_READ:
3588c2ecf20Sopenharmony_ci	case DP_AUX_I2C_READ:
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	case DP_AUX_NATIVE_WRITE:
3618c2ecf20Sopenharmony_ci	case DP_AUX_I2C_WRITE:
3628c2ecf20Sopenharmony_ci		if (size) {
3638c2ecf20Sopenharmony_ci			ret = tc_aux_write_data(tc, msg->buffer, size);
3648c2ecf20Sopenharmony_ci			if (ret < 0)
3658c2ecf20Sopenharmony_ci				return ret;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	default:
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Store address */
3738c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_AUXADDR, msg->address);
3748c2ecf20Sopenharmony_ci	if (ret)
3758c2ecf20Sopenharmony_ci		return ret;
3768c2ecf20Sopenharmony_ci	/* Start transfer */
3778c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_AUXCFG0, tc_auxcfg0(msg, size));
3788c2ecf20Sopenharmony_ci	if (ret)
3798c2ecf20Sopenharmony_ci		return ret;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = tc_aux_wait_busy(tc);
3828c2ecf20Sopenharmony_ci	if (ret)
3838c2ecf20Sopenharmony_ci		return ret;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &auxstatus);
3868c2ecf20Sopenharmony_ci	if (ret)
3878c2ecf20Sopenharmony_ci		return ret;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (auxstatus & AUX_TIMEOUT)
3908c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3918c2ecf20Sopenharmony_ci	/*
3928c2ecf20Sopenharmony_ci	 * For some reason address-only DP_AUX_I2C_WRITE (MOT), still
3938c2ecf20Sopenharmony_ci	 * reports 1 byte transferred in its status. To deal we that
3948c2ecf20Sopenharmony_ci	 * we ignore aux_bytes field if we know that this was an
3958c2ecf20Sopenharmony_ci	 * address-only transfer
3968c2ecf20Sopenharmony_ci	 */
3978c2ecf20Sopenharmony_ci	if (size)
3988c2ecf20Sopenharmony_ci		size = FIELD_GET(AUX_BYTES, auxstatus);
3998c2ecf20Sopenharmony_ci	msg->reply = FIELD_GET(AUX_STATUS, auxstatus);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	switch (request) {
4028c2ecf20Sopenharmony_ci	case DP_AUX_NATIVE_READ:
4038c2ecf20Sopenharmony_ci	case DP_AUX_I2C_READ:
4048c2ecf20Sopenharmony_ci		if (size)
4058c2ecf20Sopenharmony_ci			return tc_aux_read_data(tc, msg->buffer, size);
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return size;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic const char * const training_pattern1_errors[] = {
4138c2ecf20Sopenharmony_ci	"No errors",
4148c2ecf20Sopenharmony_ci	"Aux write error",
4158c2ecf20Sopenharmony_ci	"Aux read error",
4168c2ecf20Sopenharmony_ci	"Max voltage reached error",
4178c2ecf20Sopenharmony_ci	"Loop counter expired error",
4188c2ecf20Sopenharmony_ci	"res", "res", "res"
4198c2ecf20Sopenharmony_ci};
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic const char * const training_pattern2_errors[] = {
4228c2ecf20Sopenharmony_ci	"No errors",
4238c2ecf20Sopenharmony_ci	"Aux write error",
4248c2ecf20Sopenharmony_ci	"Aux read error",
4258c2ecf20Sopenharmony_ci	"Clock recovery failed error",
4268c2ecf20Sopenharmony_ci	"Loop counter expired error",
4278c2ecf20Sopenharmony_ci	"res", "res", "res"
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic u32 tc_srcctrl(struct tc_data *tc)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	/*
4338c2ecf20Sopenharmony_ci	 * No training pattern, skew lane 1 data by two LSCLK cycles with
4348c2ecf20Sopenharmony_ci	 * respect to lane 0 data, AutoCorrect Mode = 0
4358c2ecf20Sopenharmony_ci	 */
4368c2ecf20Sopenharmony_ci	u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_EN810B;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (tc->link.scrambler_dis)
4398c2ecf20Sopenharmony_ci		reg |= DP0_SRCCTRL_SCRMBLDIS;	/* Scrambler Disabled */
4408c2ecf20Sopenharmony_ci	if (tc->link.spread)
4418c2ecf20Sopenharmony_ci		reg |= DP0_SRCCTRL_SSCG;	/* Spread Spectrum Enable */
4428c2ecf20Sopenharmony_ci	if (tc->link.num_lanes == 2)
4438c2ecf20Sopenharmony_ci		reg |= DP0_SRCCTRL_LANES_2;	/* Two Main Channel Lanes */
4448c2ecf20Sopenharmony_ci	if (tc->link.rate != 162000)
4458c2ecf20Sopenharmony_ci		reg |= DP0_SRCCTRL_BW27;	/* 2.7 Gbps link */
4468c2ecf20Sopenharmony_ci	return reg;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int tc_pllupdate(struct tc_data *tc, unsigned int pllctrl)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	int ret;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, pllctrl, PLLUPDATE | PLLEN);
4548c2ecf20Sopenharmony_ci	if (ret)
4558c2ecf20Sopenharmony_ci		return ret;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Wait for PLL to lock: up to 2.09 ms, depending on refclk */
4588c2ecf20Sopenharmony_ci	usleep_range(3000, 6000);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return 0;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	int ret;
4668c2ecf20Sopenharmony_ci	int i_pre, best_pre = 1;
4678c2ecf20Sopenharmony_ci	int i_post, best_post = 1;
4688c2ecf20Sopenharmony_ci	int div, best_div = 1;
4698c2ecf20Sopenharmony_ci	int mul, best_mul = 1;
4708c2ecf20Sopenharmony_ci	int delta, best_delta;
4718c2ecf20Sopenharmony_ci	int ext_div[] = {1, 2, 3, 5, 7};
4728c2ecf20Sopenharmony_ci	int best_pixelclock = 0;
4738c2ecf20Sopenharmony_ci	int vco_hi = 0;
4748c2ecf20Sopenharmony_ci	u32 pxl_pllparam;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock,
4778c2ecf20Sopenharmony_ci		refclk);
4788c2ecf20Sopenharmony_ci	best_delta = pixelclock;
4798c2ecf20Sopenharmony_ci	/* Loop over all possible ext_divs, skipping invalid configurations */
4808c2ecf20Sopenharmony_ci	for (i_pre = 0; i_pre < ARRAY_SIZE(ext_div); i_pre++) {
4818c2ecf20Sopenharmony_ci		/*
4828c2ecf20Sopenharmony_ci		 * refclk / ext_pre_div should be in the 1 to 200 MHz range.
4838c2ecf20Sopenharmony_ci		 * We don't allow any refclk > 200 MHz, only check lower bounds.
4848c2ecf20Sopenharmony_ci		 */
4858c2ecf20Sopenharmony_ci		if (refclk / ext_div[i_pre] < 1000000)
4868c2ecf20Sopenharmony_ci			continue;
4878c2ecf20Sopenharmony_ci		for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) {
4888c2ecf20Sopenharmony_ci			for (div = 1; div <= 16; div++) {
4898c2ecf20Sopenharmony_ci				u32 clk;
4908c2ecf20Sopenharmony_ci				u64 tmp;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci				tmp = pixelclock * ext_div[i_pre] *
4938c2ecf20Sopenharmony_ci				      ext_div[i_post] * div;
4948c2ecf20Sopenharmony_ci				do_div(tmp, refclk);
4958c2ecf20Sopenharmony_ci				mul = tmp;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci				/* Check limits */
4988c2ecf20Sopenharmony_ci				if ((mul < 1) || (mul > 128))
4998c2ecf20Sopenharmony_ci					continue;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci				clk = (refclk / ext_div[i_pre] / div) * mul;
5028c2ecf20Sopenharmony_ci				/*
5038c2ecf20Sopenharmony_ci				 * refclk * mul / (ext_pre_div * pre_div)
5048c2ecf20Sopenharmony_ci				 * should be in the 150 to 650 MHz range
5058c2ecf20Sopenharmony_ci				 */
5068c2ecf20Sopenharmony_ci				if ((clk > 650000000) || (clk < 150000000))
5078c2ecf20Sopenharmony_ci					continue;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci				clk = clk / ext_div[i_post];
5108c2ecf20Sopenharmony_ci				delta = clk - pixelclock;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci				if (abs(delta) < abs(best_delta)) {
5138c2ecf20Sopenharmony_ci					best_pre = i_pre;
5148c2ecf20Sopenharmony_ci					best_post = i_post;
5158c2ecf20Sopenharmony_ci					best_div = div;
5168c2ecf20Sopenharmony_ci					best_mul = mul;
5178c2ecf20Sopenharmony_ci					best_delta = delta;
5188c2ecf20Sopenharmony_ci					best_pixelclock = clk;
5198c2ecf20Sopenharmony_ci				}
5208c2ecf20Sopenharmony_ci			}
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	if (best_pixelclock == 0) {
5248c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Failed to calc clock for %d pixelclock\n",
5258c2ecf20Sopenharmony_ci			pixelclock);
5268c2ecf20Sopenharmony_ci		return -EINVAL;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock,
5308c2ecf20Sopenharmony_ci		best_delta);
5318c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "PLL: %d / %d / %d * %d / %d\n", refclk,
5328c2ecf20Sopenharmony_ci		ext_div[best_pre], best_div, best_mul, ext_div[best_post]);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* if VCO >= 300 MHz */
5358c2ecf20Sopenharmony_ci	if (refclk / ext_div[best_pre] / best_div * best_mul >= 300000000)
5368c2ecf20Sopenharmony_ci		vco_hi = 1;
5378c2ecf20Sopenharmony_ci	/* see DS */
5388c2ecf20Sopenharmony_ci	if (best_div == 16)
5398c2ecf20Sopenharmony_ci		best_div = 0;
5408c2ecf20Sopenharmony_ci	if (best_mul == 128)
5418c2ecf20Sopenharmony_ci		best_mul = 0;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* Power up PLL and switch to bypass */
5448c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP | PLLEN);
5458c2ecf20Sopenharmony_ci	if (ret)
5468c2ecf20Sopenharmony_ci		return ret;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	pxl_pllparam  = vco_hi << 24; /* For PLL VCO >= 300 MHz = 1 */
5498c2ecf20Sopenharmony_ci	pxl_pllparam |= ext_div[best_pre] << 20; /* External Pre-divider */
5508c2ecf20Sopenharmony_ci	pxl_pllparam |= ext_div[best_post] << 16; /* External Post-divider */
5518c2ecf20Sopenharmony_ci	pxl_pllparam |= IN_SEL_REFCLK; /* Use RefClk as PLL input */
5528c2ecf20Sopenharmony_ci	pxl_pllparam |= best_div << 8; /* Divider for PLL RefClk */
5538c2ecf20Sopenharmony_ci	pxl_pllparam |= best_mul; /* Multiplier for PLL */
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, PXL_PLLPARAM, pxl_pllparam);
5568c2ecf20Sopenharmony_ci	if (ret)
5578c2ecf20Sopenharmony_ci		return ret;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* Force PLL parameter update and disable bypass */
5608c2ecf20Sopenharmony_ci	return tc_pllupdate(tc, PXL_PLLCTRL);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int tc_pxl_pll_dis(struct tc_data *tc)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	/* Enable PLL bypass, power down PLL */
5668c2ecf20Sopenharmony_ci	return regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic int tc_stream_clock_calc(struct tc_data *tc)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	/*
5728c2ecf20Sopenharmony_ci	 * If the Stream clock and Link Symbol clock are
5738c2ecf20Sopenharmony_ci	 * asynchronous with each other, the value of M changes over
5748c2ecf20Sopenharmony_ci	 * time. This way of generating link clock and stream
5758c2ecf20Sopenharmony_ci	 * clock is called Asynchronous Clock mode. The value M
5768c2ecf20Sopenharmony_ci	 * must change while the value N stays constant. The
5778c2ecf20Sopenharmony_ci	 * value of N in this Asynchronous Clock mode must be set
5788c2ecf20Sopenharmony_ci	 * to 2^15 or 32,768.
5798c2ecf20Sopenharmony_ci	 *
5808c2ecf20Sopenharmony_ci	 * LSCLK = 1/10 of high speed link clock
5818c2ecf20Sopenharmony_ci	 *
5828c2ecf20Sopenharmony_ci	 * f_STRMCLK = M/N * f_LSCLK
5838c2ecf20Sopenharmony_ci	 * M/N = f_STRMCLK / f_LSCLK
5848c2ecf20Sopenharmony_ci	 *
5858c2ecf20Sopenharmony_ci	 */
5868c2ecf20Sopenharmony_ci	return regmap_write(tc->regmap, DP0_VIDMNGEN1, 32768);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int tc_set_syspllparam(struct tc_data *tc)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	unsigned long rate;
5928c2ecf20Sopenharmony_ci	u32 pllparam = SYSCLK_SEL_LSCLK | LSCLK_DIV_2;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	rate = clk_get_rate(tc->refclk);
5958c2ecf20Sopenharmony_ci	switch (rate) {
5968c2ecf20Sopenharmony_ci	case 38400000:
5978c2ecf20Sopenharmony_ci		pllparam |= REF_FREQ_38M4;
5988c2ecf20Sopenharmony_ci		break;
5998c2ecf20Sopenharmony_ci	case 26000000:
6008c2ecf20Sopenharmony_ci		pllparam |= REF_FREQ_26M;
6018c2ecf20Sopenharmony_ci		break;
6028c2ecf20Sopenharmony_ci	case 19200000:
6038c2ecf20Sopenharmony_ci		pllparam |= REF_FREQ_19M2;
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci	case 13000000:
6068c2ecf20Sopenharmony_ci		pllparam |= REF_FREQ_13M;
6078c2ecf20Sopenharmony_ci		break;
6088c2ecf20Sopenharmony_ci	default:
6098c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Invalid refclk rate: %lu Hz\n", rate);
6108c2ecf20Sopenharmony_ci		return -EINVAL;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return regmap_write(tc->regmap, SYS_PLLPARAM, pllparam);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int tc_aux_link_setup(struct tc_data *tc)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	int ret;
6198c2ecf20Sopenharmony_ci	u32 dp0_auxcfg1;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* Setup DP-PHY / PLL */
6228c2ecf20Sopenharmony_ci	ret = tc_set_syspllparam(tc);
6238c2ecf20Sopenharmony_ci	if (ret)
6248c2ecf20Sopenharmony_ci		goto err;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP_PHY_CTRL,
6278c2ecf20Sopenharmony_ci			   BGREN | PWR_SW_EN | PHY_A0_EN);
6288c2ecf20Sopenharmony_ci	if (ret)
6298c2ecf20Sopenharmony_ci		goto err;
6308c2ecf20Sopenharmony_ci	/*
6318c2ecf20Sopenharmony_ci	 * Initially PLLs are in bypass. Force PLL parameter update,
6328c2ecf20Sopenharmony_ci	 * disable PLL bypass, enable PLL
6338c2ecf20Sopenharmony_ci	 */
6348c2ecf20Sopenharmony_ci	ret = tc_pllupdate(tc, DP0_PLLCTRL);
6358c2ecf20Sopenharmony_ci	if (ret)
6368c2ecf20Sopenharmony_ci		goto err;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	ret = tc_pllupdate(tc, DP1_PLLCTRL);
6398c2ecf20Sopenharmony_ci	if (ret)
6408c2ecf20Sopenharmony_ci		goto err;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	ret = tc_poll_timeout(tc, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 100, 100000);
6438c2ecf20Sopenharmony_ci	if (ret == -ETIMEDOUT) {
6448c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Timeout waiting for PHY to become ready");
6458c2ecf20Sopenharmony_ci		return ret;
6468c2ecf20Sopenharmony_ci	} else if (ret) {
6478c2ecf20Sopenharmony_ci		goto err;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* Setup AUX link */
6518c2ecf20Sopenharmony_ci	dp0_auxcfg1  = AUX_RX_FILTER_EN;
6528c2ecf20Sopenharmony_ci	dp0_auxcfg1 |= 0x06 << 8; /* Aux Bit Period Calculator Threshold */
6538c2ecf20Sopenharmony_ci	dp0_auxcfg1 |= 0x3f << 0; /* Aux Response Timeout Timer */
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_AUXCFG1, dp0_auxcfg1);
6568c2ecf20Sopenharmony_ci	if (ret)
6578c2ecf20Sopenharmony_ci		goto err;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return 0;
6608c2ecf20Sopenharmony_cierr:
6618c2ecf20Sopenharmony_ci	dev_err(tc->dev, "tc_aux_link_setup failed: %d\n", ret);
6628c2ecf20Sopenharmony_ci	return ret;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic int tc_get_display_props(struct tc_data *tc)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	u8 revision, num_lanes;
6688c2ecf20Sopenharmony_ci	unsigned int rate;
6698c2ecf20Sopenharmony_ci	int ret;
6708c2ecf20Sopenharmony_ci	u8 reg;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* Read DP Rx Link Capability */
6738c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_read(&tc->aux, DP_DPCD_REV, tc->link.dpcd,
6748c2ecf20Sopenharmony_ci			       DP_RECEIVER_CAP_SIZE);
6758c2ecf20Sopenharmony_ci	if (ret < 0)
6768c2ecf20Sopenharmony_ci		goto err_dpcd_read;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	revision = tc->link.dpcd[DP_DPCD_REV];
6798c2ecf20Sopenharmony_ci	rate = drm_dp_max_link_rate(tc->link.dpcd);
6808c2ecf20Sopenharmony_ci	num_lanes = drm_dp_max_lane_count(tc->link.dpcd);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (rate != 162000 && rate != 270000) {
6838c2ecf20Sopenharmony_ci		dev_dbg(tc->dev, "Falling to 2.7 Gbps rate\n");
6848c2ecf20Sopenharmony_ci		rate = 270000;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	tc->link.rate = rate;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (num_lanes > 2) {
6908c2ecf20Sopenharmony_ci		dev_dbg(tc->dev, "Falling to 2 lanes\n");
6918c2ecf20Sopenharmony_ci		num_lanes = 2;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	tc->link.num_lanes = num_lanes;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, &reg);
6978c2ecf20Sopenharmony_ci	if (ret < 0)
6988c2ecf20Sopenharmony_ci		goto err_dpcd_read;
6998c2ecf20Sopenharmony_ci	tc->link.spread = reg & DP_MAX_DOWNSPREAD_0_5;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAIN_LINK_CHANNEL_CODING, &reg);
7028c2ecf20Sopenharmony_ci	if (ret < 0)
7038c2ecf20Sopenharmony_ci		goto err_dpcd_read;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	tc->link.scrambler_dis = false;
7068c2ecf20Sopenharmony_ci	/* read assr */
7078c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_readb(&tc->aux, DP_EDP_CONFIGURATION_SET, &reg);
7088c2ecf20Sopenharmony_ci	if (ret < 0)
7098c2ecf20Sopenharmony_ci		goto err_dpcd_read;
7108c2ecf20Sopenharmony_ci	tc->link.assr = reg & DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n",
7138c2ecf20Sopenharmony_ci		revision >> 4, revision & 0x0f,
7148c2ecf20Sopenharmony_ci		(tc->link.rate == 162000) ? "1.62Gbps" : "2.7Gbps",
7158c2ecf20Sopenharmony_ci		tc->link.num_lanes,
7168c2ecf20Sopenharmony_ci		drm_dp_enhanced_frame_cap(tc->link.dpcd) ?
7178c2ecf20Sopenharmony_ci		"enhanced" : "default");
7188c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "Downspread: %s, scrambler: %s\n",
7198c2ecf20Sopenharmony_ci		tc->link.spread ? "0.5%" : "0.0%",
7208c2ecf20Sopenharmony_ci		tc->link.scrambler_dis ? "disabled" : "enabled");
7218c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n",
7228c2ecf20Sopenharmony_ci		tc->link.assr, tc->assr);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	return 0;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cierr_dpcd_read:
7278c2ecf20Sopenharmony_ci	dev_err(tc->dev, "failed to read DPCD: %d\n", ret);
7288c2ecf20Sopenharmony_ci	return ret;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic int tc_set_video_mode(struct tc_data *tc,
7328c2ecf20Sopenharmony_ci			     const struct drm_display_mode *mode)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	int ret;
7358c2ecf20Sopenharmony_ci	int vid_sync_dly;
7368c2ecf20Sopenharmony_ci	int max_tu_symbol;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	int left_margin = mode->htotal - mode->hsync_end;
7398c2ecf20Sopenharmony_ci	int right_margin = mode->hsync_start - mode->hdisplay;
7408c2ecf20Sopenharmony_ci	int hsync_len = mode->hsync_end - mode->hsync_start;
7418c2ecf20Sopenharmony_ci	int upper_margin = mode->vtotal - mode->vsync_end;
7428c2ecf20Sopenharmony_ci	int lower_margin = mode->vsync_start - mode->vdisplay;
7438c2ecf20Sopenharmony_ci	int vsync_len = mode->vsync_end - mode->vsync_start;
7448c2ecf20Sopenharmony_ci	u32 dp0_syncval;
7458c2ecf20Sopenharmony_ci	u32 bits_per_pixel = 24;
7468c2ecf20Sopenharmony_ci	u32 in_bw, out_bw;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/*
7498c2ecf20Sopenharmony_ci	 * Recommended maximum number of symbols transferred in a transfer unit:
7508c2ecf20Sopenharmony_ci	 * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size,
7518c2ecf20Sopenharmony_ci	 *              (output active video bandwidth in bytes))
7528c2ecf20Sopenharmony_ci	 * Must be less than tu_size.
7538c2ecf20Sopenharmony_ci	 */
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	in_bw = mode->clock * bits_per_pixel / 8;
7568c2ecf20Sopenharmony_ci	out_bw = tc->link.num_lanes * tc->link.rate;
7578c2ecf20Sopenharmony_ci	max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "set mode %dx%d\n",
7608c2ecf20Sopenharmony_ci		mode->hdisplay, mode->vdisplay);
7618c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "H margin %d,%d sync %d\n",
7628c2ecf20Sopenharmony_ci		left_margin, right_margin, hsync_len);
7638c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "V margin %d,%d sync %d\n",
7648c2ecf20Sopenharmony_ci		upper_margin, lower_margin, vsync_len);
7658c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/*
7698c2ecf20Sopenharmony_ci	 * LCD Ctl Frame Size
7708c2ecf20Sopenharmony_ci	 * datasheet is not clear of vsdelay in case of DPI
7718c2ecf20Sopenharmony_ci	 * assume we do not need any delay when DPI is a source of
7728c2ecf20Sopenharmony_ci	 * sync signals
7738c2ecf20Sopenharmony_ci	 */
7748c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, VPCTRL0,
7758c2ecf20Sopenharmony_ci			   FIELD_PREP(VSDELAY, 0) |
7768c2ecf20Sopenharmony_ci			   OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED);
7778c2ecf20Sopenharmony_ci	if (ret)
7788c2ecf20Sopenharmony_ci		return ret;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, HTIM01,
7818c2ecf20Sopenharmony_ci			   FIELD_PREP(HBPR, ALIGN(left_margin, 2)) |
7828c2ecf20Sopenharmony_ci			   FIELD_PREP(HPW, ALIGN(hsync_len, 2)));
7838c2ecf20Sopenharmony_ci	if (ret)
7848c2ecf20Sopenharmony_ci		return ret;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, HTIM02,
7878c2ecf20Sopenharmony_ci			   FIELD_PREP(HDISPR, ALIGN(mode->hdisplay, 2)) |
7888c2ecf20Sopenharmony_ci			   FIELD_PREP(HFPR, ALIGN(right_margin, 2)));
7898c2ecf20Sopenharmony_ci	if (ret)
7908c2ecf20Sopenharmony_ci		return ret;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, VTIM01,
7938c2ecf20Sopenharmony_ci			   FIELD_PREP(VBPR, upper_margin) |
7948c2ecf20Sopenharmony_ci			   FIELD_PREP(VSPR, vsync_len));
7958c2ecf20Sopenharmony_ci	if (ret)
7968c2ecf20Sopenharmony_ci		return ret;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, VTIM02,
7998c2ecf20Sopenharmony_ci			   FIELD_PREP(VFPR, lower_margin) |
8008c2ecf20Sopenharmony_ci			   FIELD_PREP(VDISPR, mode->vdisplay));
8018c2ecf20Sopenharmony_ci	if (ret)
8028c2ecf20Sopenharmony_ci		return ret;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, VFUEN0, VFUEN); /* update settings */
8058c2ecf20Sopenharmony_ci	if (ret)
8068c2ecf20Sopenharmony_ci		return ret;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* Test pattern settings */
8098c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, TSTCTL,
8108c2ecf20Sopenharmony_ci			   FIELD_PREP(COLOR_R, 120) |
8118c2ecf20Sopenharmony_ci			   FIELD_PREP(COLOR_G, 20) |
8128c2ecf20Sopenharmony_ci			   FIELD_PREP(COLOR_B, 99) |
8138c2ecf20Sopenharmony_ci			   ENI2CFILTER |
8148c2ecf20Sopenharmony_ci			   FIELD_PREP(COLOR_BAR_MODE, COLOR_BAR_MODE_BARS));
8158c2ecf20Sopenharmony_ci	if (ret)
8168c2ecf20Sopenharmony_ci		return ret;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* DP Main Stream Attributes */
8198c2ecf20Sopenharmony_ci	vid_sync_dly = hsync_len + left_margin + mode->hdisplay;
8208c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_VIDSYNCDELAY,
8218c2ecf20Sopenharmony_ci		 FIELD_PREP(THRESH_DLY, max_tu_symbol) |
8228c2ecf20Sopenharmony_ci		 FIELD_PREP(VID_SYNC_DLY, vid_sync_dly));
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_TOTALVAL,
8258c2ecf20Sopenharmony_ci			   FIELD_PREP(H_TOTAL, mode->htotal) |
8268c2ecf20Sopenharmony_ci			   FIELD_PREP(V_TOTAL, mode->vtotal));
8278c2ecf20Sopenharmony_ci	if (ret)
8288c2ecf20Sopenharmony_ci		return ret;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_STARTVAL,
8318c2ecf20Sopenharmony_ci			   FIELD_PREP(H_START, left_margin + hsync_len) |
8328c2ecf20Sopenharmony_ci			   FIELD_PREP(V_START, upper_margin + vsync_len));
8338c2ecf20Sopenharmony_ci	if (ret)
8348c2ecf20Sopenharmony_ci		return ret;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_ACTIVEVAL,
8378c2ecf20Sopenharmony_ci			   FIELD_PREP(V_ACT, mode->vdisplay) |
8388c2ecf20Sopenharmony_ci			   FIELD_PREP(H_ACT, mode->hdisplay));
8398c2ecf20Sopenharmony_ci	if (ret)
8408c2ecf20Sopenharmony_ci		return ret;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	dp0_syncval = FIELD_PREP(VS_WIDTH, vsync_len) |
8438c2ecf20Sopenharmony_ci		      FIELD_PREP(HS_WIDTH, hsync_len);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
8468c2ecf20Sopenharmony_ci		dp0_syncval |= SYNCVAL_VS_POL_ACTIVE_LOW;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
8498c2ecf20Sopenharmony_ci		dp0_syncval |= SYNCVAL_HS_POL_ACTIVE_LOW;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SYNCVAL, dp0_syncval);
8528c2ecf20Sopenharmony_ci	if (ret)
8538c2ecf20Sopenharmony_ci		return ret;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DPIPXLFMT,
8568c2ecf20Sopenharmony_ci			   VS_POL_ACTIVE_LOW | HS_POL_ACTIVE_LOW |
8578c2ecf20Sopenharmony_ci			   DE_POL_ACTIVE_HIGH | SUB_CFG_TYPE_CONFIG1 |
8588c2ecf20Sopenharmony_ci			   DPI_BPP_RGB888);
8598c2ecf20Sopenharmony_ci	if (ret)
8608c2ecf20Sopenharmony_ci		return ret;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_MISC,
8638c2ecf20Sopenharmony_ci			   FIELD_PREP(MAX_TU_SYMBOL, max_tu_symbol) |
8648c2ecf20Sopenharmony_ci			   FIELD_PREP(TU_SIZE, TU_SIZE_RECOMMENDED) |
8658c2ecf20Sopenharmony_ci			   BPC_8);
8668c2ecf20Sopenharmony_ci	if (ret)
8678c2ecf20Sopenharmony_ci		return ret;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return 0;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic int tc_wait_link_training(struct tc_data *tc)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	u32 value;
8758c2ecf20Sopenharmony_ci	int ret;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	ret = tc_poll_timeout(tc, DP0_LTSTAT, LT_LOOPDONE,
8788c2ecf20Sopenharmony_ci			      LT_LOOPDONE, 500, 100000);
8798c2ecf20Sopenharmony_ci	if (ret) {
8808c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Link training timeout waiting for LT_LOOPDONE!\n");
8818c2ecf20Sopenharmony_ci		return ret;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	ret = regmap_read(tc->regmap, DP0_LTSTAT, &value);
8858c2ecf20Sopenharmony_ci	if (ret)
8868c2ecf20Sopenharmony_ci		return ret;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	return (value >> 8) & 0x7;
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic int tc_main_link_enable(struct tc_data *tc)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	struct drm_dp_aux *aux = &tc->aux;
8948c2ecf20Sopenharmony_ci	struct device *dev = tc->dev;
8958c2ecf20Sopenharmony_ci	u32 dp_phy_ctrl;
8968c2ecf20Sopenharmony_ci	u32 value;
8978c2ecf20Sopenharmony_ci	int ret;
8988c2ecf20Sopenharmony_ci	u8 tmp[DP_LINK_STATUS_SIZE];
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "link enable\n");
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	ret = regmap_read(tc->regmap, DP0CTL, &value);
9038c2ecf20Sopenharmony_ci	if (ret)
9048c2ecf20Sopenharmony_ci		return ret;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (WARN_ON(value & DP_EN)) {
9078c2ecf20Sopenharmony_ci		ret = regmap_write(tc->regmap, DP0CTL, 0);
9088c2ecf20Sopenharmony_ci		if (ret)
9098c2ecf20Sopenharmony_ci			return ret;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc));
9138c2ecf20Sopenharmony_ci	if (ret)
9148c2ecf20Sopenharmony_ci		return ret;
9158c2ecf20Sopenharmony_ci	/* SSCG and BW27 on DP1 must be set to the same as on DP0 */
9168c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP1_SRCCTRL,
9178c2ecf20Sopenharmony_ci		 (tc->link.spread ? DP0_SRCCTRL_SSCG : 0) |
9188c2ecf20Sopenharmony_ci		 ((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0));
9198c2ecf20Sopenharmony_ci	if (ret)
9208c2ecf20Sopenharmony_ci		return ret;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	ret = tc_set_syspllparam(tc);
9238c2ecf20Sopenharmony_ci	if (ret)
9248c2ecf20Sopenharmony_ci		return ret;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* Setup Main Link */
9278c2ecf20Sopenharmony_ci	dp_phy_ctrl = BGREN | PWR_SW_EN | PHY_A0_EN | PHY_M0_EN;
9288c2ecf20Sopenharmony_ci	if (tc->link.num_lanes == 2)
9298c2ecf20Sopenharmony_ci		dp_phy_ctrl |= PHY_2LANE;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl);
9328c2ecf20Sopenharmony_ci	if (ret)
9338c2ecf20Sopenharmony_ci		return ret;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* PLL setup */
9368c2ecf20Sopenharmony_ci	ret = tc_pllupdate(tc, DP0_PLLCTRL);
9378c2ecf20Sopenharmony_ci	if (ret)
9388c2ecf20Sopenharmony_ci		return ret;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	ret = tc_pllupdate(tc, DP1_PLLCTRL);
9418c2ecf20Sopenharmony_ci	if (ret)
9428c2ecf20Sopenharmony_ci		return ret;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/* Reset/Enable Main Links */
9458c2ecf20Sopenharmony_ci	dp_phy_ctrl |= DP_PHY_RST | PHY_M1_RST | PHY_M0_RST;
9468c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl);
9478c2ecf20Sopenharmony_ci	usleep_range(100, 200);
9488c2ecf20Sopenharmony_ci	dp_phy_ctrl &= ~(DP_PHY_RST | PHY_M1_RST | PHY_M0_RST);
9498c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	ret = tc_poll_timeout(tc, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 500, 100000);
9528c2ecf20Sopenharmony_ci	if (ret) {
9538c2ecf20Sopenharmony_ci		dev_err(dev, "timeout waiting for phy become ready");
9548c2ecf20Sopenharmony_ci		return ret;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* Set misc: 8 bits per color */
9588c2ecf20Sopenharmony_ci	ret = regmap_update_bits(tc->regmap, DP0_MISC, BPC_8, BPC_8);
9598c2ecf20Sopenharmony_ci	if (ret)
9608c2ecf20Sopenharmony_ci		return ret;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/*
9638c2ecf20Sopenharmony_ci	 * ASSR mode
9648c2ecf20Sopenharmony_ci	 * on TC358767 side ASSR configured through strap pin
9658c2ecf20Sopenharmony_ci	 * seems there is no way to change this setting from SW
9668c2ecf20Sopenharmony_ci	 *
9678c2ecf20Sopenharmony_ci	 * check is tc configured for same mode
9688c2ecf20Sopenharmony_ci	 */
9698c2ecf20Sopenharmony_ci	if (tc->assr != tc->link.assr) {
9708c2ecf20Sopenharmony_ci		dev_dbg(dev, "Trying to set display to ASSR: %d\n",
9718c2ecf20Sopenharmony_ci			tc->assr);
9728c2ecf20Sopenharmony_ci		/* try to set ASSR on display side */
9738c2ecf20Sopenharmony_ci		tmp[0] = tc->assr;
9748c2ecf20Sopenharmony_ci		ret = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, tmp[0]);
9758c2ecf20Sopenharmony_ci		if (ret < 0)
9768c2ecf20Sopenharmony_ci			goto err_dpcd_read;
9778c2ecf20Sopenharmony_ci		/* read back */
9788c2ecf20Sopenharmony_ci		ret = drm_dp_dpcd_readb(aux, DP_EDP_CONFIGURATION_SET, tmp);
9798c2ecf20Sopenharmony_ci		if (ret < 0)
9808c2ecf20Sopenharmony_ci			goto err_dpcd_read;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		if (tmp[0] != tc->assr) {
9838c2ecf20Sopenharmony_ci			dev_dbg(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n",
9848c2ecf20Sopenharmony_ci				tc->assr);
9858c2ecf20Sopenharmony_ci			/* trying with disabled scrambler */
9868c2ecf20Sopenharmony_ci			tc->link.scrambler_dis = true;
9878c2ecf20Sopenharmony_ci		}
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* Setup Link & DPRx Config for Training */
9918c2ecf20Sopenharmony_ci	tmp[0] = drm_dp_link_rate_to_bw_code(tc->link.rate);
9928c2ecf20Sopenharmony_ci	tmp[1] = tc->link.num_lanes;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
9958c2ecf20Sopenharmony_ci		tmp[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, tmp, 2);
9988c2ecf20Sopenharmony_ci	if (ret < 0)
9998c2ecf20Sopenharmony_ci		goto err_dpcd_write;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* DOWNSPREAD_CTRL */
10028c2ecf20Sopenharmony_ci	tmp[0] = tc->link.spread ? DP_SPREAD_AMP_0_5 : 0x00;
10038c2ecf20Sopenharmony_ci	/* MAIN_LINK_CHANNEL_CODING_SET */
10048c2ecf20Sopenharmony_ci	tmp[1] =  DP_SET_ANSI_8B10B;
10058c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_write(aux, DP_DOWNSPREAD_CTRL, tmp, 2);
10068c2ecf20Sopenharmony_ci	if (ret < 0)
10078c2ecf20Sopenharmony_ci		goto err_dpcd_write;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* Reset voltage-swing & pre-emphasis */
10108c2ecf20Sopenharmony_ci	tmp[0] = tmp[1] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 |
10118c2ecf20Sopenharmony_ci			  DP_TRAIN_PRE_EMPH_LEVEL_0;
10128c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, tmp, 2);
10138c2ecf20Sopenharmony_ci	if (ret < 0)
10148c2ecf20Sopenharmony_ci		goto err_dpcd_write;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	/* Clock-Recovery */
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* Set DPCD 0x102 for Training Pattern 1 */
10198c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SNKLTCTRL,
10208c2ecf20Sopenharmony_ci			   DP_LINK_SCRAMBLING_DISABLE |
10218c2ecf20Sopenharmony_ci			   DP_TRAINING_PATTERN_1);
10228c2ecf20Sopenharmony_ci	if (ret)
10238c2ecf20Sopenharmony_ci		return ret;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_LTLOOPCTRL,
10268c2ecf20Sopenharmony_ci			   (15 << 28) |	/* Defer Iteration Count */
10278c2ecf20Sopenharmony_ci			   (15 << 24) |	/* Loop Iteration Count */
10288c2ecf20Sopenharmony_ci			   (0xd << 0));	/* Loop Timer Delay */
10298c2ecf20Sopenharmony_ci	if (ret)
10308c2ecf20Sopenharmony_ci		return ret;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SRCCTRL,
10338c2ecf20Sopenharmony_ci			   tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS |
10348c2ecf20Sopenharmony_ci			   DP0_SRCCTRL_AUTOCORRECT |
10358c2ecf20Sopenharmony_ci			   DP0_SRCCTRL_TP1);
10368c2ecf20Sopenharmony_ci	if (ret)
10378c2ecf20Sopenharmony_ci		return ret;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* Enable DP0 to start Link Training */
10408c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0CTL,
10418c2ecf20Sopenharmony_ci			   (drm_dp_enhanced_frame_cap(tc->link.dpcd) ?
10428c2ecf20Sopenharmony_ci				EF_EN : 0) | DP_EN);
10438c2ecf20Sopenharmony_ci	if (ret)
10448c2ecf20Sopenharmony_ci		return ret;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	/* wait */
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ret = tc_wait_link_training(tc);
10498c2ecf20Sopenharmony_ci	if (ret < 0)
10508c2ecf20Sopenharmony_ci		return ret;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (ret) {
10538c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Link training phase 1 failed: %s\n",
10548c2ecf20Sopenharmony_ci			training_pattern1_errors[ret]);
10558c2ecf20Sopenharmony_ci		return -ENODEV;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* Channel Equalization */
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	/* Set DPCD 0x102 for Training Pattern 2 */
10618c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SNKLTCTRL,
10628c2ecf20Sopenharmony_ci			   DP_LINK_SCRAMBLING_DISABLE |
10638c2ecf20Sopenharmony_ci			   DP_TRAINING_PATTERN_2);
10648c2ecf20Sopenharmony_ci	if (ret)
10658c2ecf20Sopenharmony_ci		return ret;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SRCCTRL,
10688c2ecf20Sopenharmony_ci			   tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS |
10698c2ecf20Sopenharmony_ci			   DP0_SRCCTRL_AUTOCORRECT |
10708c2ecf20Sopenharmony_ci			   DP0_SRCCTRL_TP2);
10718c2ecf20Sopenharmony_ci	if (ret)
10728c2ecf20Sopenharmony_ci		return ret;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/* wait */
10758c2ecf20Sopenharmony_ci	ret = tc_wait_link_training(tc);
10768c2ecf20Sopenharmony_ci	if (ret < 0)
10778c2ecf20Sopenharmony_ci		return ret;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (ret) {
10808c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Link training phase 2 failed: %s\n",
10818c2ecf20Sopenharmony_ci			training_pattern2_errors[ret]);
10828c2ecf20Sopenharmony_ci		return -ENODEV;
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	/*
10868c2ecf20Sopenharmony_ci	 * Toshiba's documentation suggests to first clear DPCD 0x102, then
10878c2ecf20Sopenharmony_ci	 * clear the training pattern bit in DP0_SRCCTRL. Testing shows
10888c2ecf20Sopenharmony_ci	 * that the link sometimes drops if those steps are done in that order,
10898c2ecf20Sopenharmony_ci	 * but if the steps are done in reverse order, the link stays up.
10908c2ecf20Sopenharmony_ci	 *
10918c2ecf20Sopenharmony_ci	 * So we do the steps differently than documented here.
10928c2ecf20Sopenharmony_ci	 */
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	/* Clear Training Pattern, set AutoCorrect Mode = 1 */
10958c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) |
10968c2ecf20Sopenharmony_ci			   DP0_SRCCTRL_AUTOCORRECT);
10978c2ecf20Sopenharmony_ci	if (ret)
10988c2ecf20Sopenharmony_ci		return ret;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	/* Clear DPCD 0x102 */
11018c2ecf20Sopenharmony_ci	/* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */
11028c2ecf20Sopenharmony_ci	tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00;
11038c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, tmp[0]);
11048c2ecf20Sopenharmony_ci	if (ret < 0)
11058c2ecf20Sopenharmony_ci		goto err_dpcd_write;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* Check link status */
11088c2ecf20Sopenharmony_ci	ret = drm_dp_dpcd_read_link_status(aux, tmp);
11098c2ecf20Sopenharmony_ci	if (ret < 0)
11108c2ecf20Sopenharmony_ci		goto err_dpcd_read;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	ret = 0;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	value = tmp[0] & DP_CHANNEL_EQ_BITS;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (value != DP_CHANNEL_EQ_BITS) {
11178c2ecf20Sopenharmony_ci		dev_err(tc->dev, "Lane 0 failed: %x\n", value);
11188c2ecf20Sopenharmony_ci		ret = -ENODEV;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (tc->link.num_lanes == 2) {
11228c2ecf20Sopenharmony_ci		value = (tmp[0] >> 4) & DP_CHANNEL_EQ_BITS;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		if (value != DP_CHANNEL_EQ_BITS) {
11258c2ecf20Sopenharmony_ci			dev_err(tc->dev, "Lane 1 failed: %x\n", value);
11268c2ecf20Sopenharmony_ci			ret = -ENODEV;
11278c2ecf20Sopenharmony_ci		}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci		if (!(tmp[2] & DP_INTERLANE_ALIGN_DONE)) {
11308c2ecf20Sopenharmony_ci			dev_err(tc->dev, "Interlane align failed\n");
11318c2ecf20Sopenharmony_ci			ret = -ENODEV;
11328c2ecf20Sopenharmony_ci		}
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (ret) {
11368c2ecf20Sopenharmony_ci		dev_err(dev, "0x0202 LANE0_1_STATUS:            0x%02x\n", tmp[0]);
11378c2ecf20Sopenharmony_ci		dev_err(dev, "0x0203 LANE2_3_STATUS             0x%02x\n", tmp[1]);
11388c2ecf20Sopenharmony_ci		dev_err(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n", tmp[2]);
11398c2ecf20Sopenharmony_ci		dev_err(dev, "0x0205 SINK_STATUS:               0x%02x\n", tmp[3]);
11408c2ecf20Sopenharmony_ci		dev_err(dev, "0x0206 ADJUST_REQUEST_LANE0_1:    0x%02x\n", tmp[4]);
11418c2ecf20Sopenharmony_ci		dev_err(dev, "0x0207 ADJUST_REQUEST_LANE2_3:    0x%02x\n", tmp[5]);
11428c2ecf20Sopenharmony_ci		return ret;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	return 0;
11468c2ecf20Sopenharmony_cierr_dpcd_read:
11478c2ecf20Sopenharmony_ci	dev_err(tc->dev, "Failed to read DPCD: %d\n", ret);
11488c2ecf20Sopenharmony_ci	return ret;
11498c2ecf20Sopenharmony_cierr_dpcd_write:
11508c2ecf20Sopenharmony_ci	dev_err(tc->dev, "Failed to write DPCD: %d\n", ret);
11518c2ecf20Sopenharmony_ci	return ret;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistatic int tc_main_link_disable(struct tc_data *tc)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	int ret;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "link disable\n");
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0_SRCCTRL, 0);
11618c2ecf20Sopenharmony_ci	if (ret)
11628c2ecf20Sopenharmony_ci		return ret;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	return regmap_write(tc->regmap, DP0CTL, 0);
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic int tc_stream_enable(struct tc_data *tc)
11688c2ecf20Sopenharmony_ci{
11698c2ecf20Sopenharmony_ci	int ret;
11708c2ecf20Sopenharmony_ci	u32 value;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "enable video stream\n");
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* PXL PLL setup */
11758c2ecf20Sopenharmony_ci	if (tc_test_pattern) {
11768c2ecf20Sopenharmony_ci		ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
11778c2ecf20Sopenharmony_ci				    1000 * tc->mode.clock);
11788c2ecf20Sopenharmony_ci		if (ret)
11798c2ecf20Sopenharmony_ci			return ret;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	ret = tc_set_video_mode(tc, &tc->mode);
11838c2ecf20Sopenharmony_ci	if (ret)
11848c2ecf20Sopenharmony_ci		return ret;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	/* Set M/N */
11878c2ecf20Sopenharmony_ci	ret = tc_stream_clock_calc(tc);
11888c2ecf20Sopenharmony_ci	if (ret)
11898c2ecf20Sopenharmony_ci		return ret;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	value = VID_MN_GEN | DP_EN;
11928c2ecf20Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
11938c2ecf20Sopenharmony_ci		value |= EF_EN;
11948c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0CTL, value);
11958c2ecf20Sopenharmony_ci	if (ret)
11968c2ecf20Sopenharmony_ci		return ret;
11978c2ecf20Sopenharmony_ci	/*
11988c2ecf20Sopenharmony_ci	 * VID_EN assertion should be delayed by at least N * LSCLK
11998c2ecf20Sopenharmony_ci	 * cycles from the time VID_MN_GEN is enabled in order to
12008c2ecf20Sopenharmony_ci	 * generate stable values for VID_M. LSCLK is 270 MHz or
12018c2ecf20Sopenharmony_ci	 * 162 MHz, VID_N is set to 32768 in  tc_stream_clock_calc(),
12028c2ecf20Sopenharmony_ci	 * so a delay of at least 203 us should suffice.
12038c2ecf20Sopenharmony_ci	 */
12048c2ecf20Sopenharmony_ci	usleep_range(500, 1000);
12058c2ecf20Sopenharmony_ci	value |= VID_EN;
12068c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, DP0CTL, value);
12078c2ecf20Sopenharmony_ci	if (ret)
12088c2ecf20Sopenharmony_ci		return ret;
12098c2ecf20Sopenharmony_ci	/* Set input interface */
12108c2ecf20Sopenharmony_ci	value = DP0_AUDSRC_NO_INPUT;
12118c2ecf20Sopenharmony_ci	if (tc_test_pattern)
12128c2ecf20Sopenharmony_ci		value |= DP0_VIDSRC_COLOR_BAR;
12138c2ecf20Sopenharmony_ci	else
12148c2ecf20Sopenharmony_ci		value |= DP0_VIDSRC_DPI_RX;
12158c2ecf20Sopenharmony_ci	ret = regmap_write(tc->regmap, SYSCTRL, value);
12168c2ecf20Sopenharmony_ci	if (ret)
12178c2ecf20Sopenharmony_ci		return ret;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	return 0;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic int tc_stream_disable(struct tc_data *tc)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	int ret;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	dev_dbg(tc->dev, "disable video stream\n");
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	ret = regmap_update_bits(tc->regmap, DP0CTL, VID_EN, 0);
12298c2ecf20Sopenharmony_ci	if (ret)
12308c2ecf20Sopenharmony_ci		return ret;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	tc_pxl_pll_dis(tc);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	return 0;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic void tc_bridge_enable(struct drm_bridge *bridge)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
12408c2ecf20Sopenharmony_ci	int ret;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	ret = tc_get_display_props(tc);
12438c2ecf20Sopenharmony_ci	if (ret < 0) {
12448c2ecf20Sopenharmony_ci		dev_err(tc->dev, "failed to read display props: %d\n", ret);
12458c2ecf20Sopenharmony_ci		return;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	ret = tc_main_link_enable(tc);
12498c2ecf20Sopenharmony_ci	if (ret < 0) {
12508c2ecf20Sopenharmony_ci		dev_err(tc->dev, "main link enable error: %d\n", ret);
12518c2ecf20Sopenharmony_ci		return;
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	ret = tc_stream_enable(tc);
12558c2ecf20Sopenharmony_ci	if (ret < 0) {
12568c2ecf20Sopenharmony_ci		dev_err(tc->dev, "main link stream start error: %d\n", ret);
12578c2ecf20Sopenharmony_ci		tc_main_link_disable(tc);
12588c2ecf20Sopenharmony_ci		return;
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cistatic void tc_bridge_disable(struct drm_bridge *bridge)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
12658c2ecf20Sopenharmony_ci	int ret;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	ret = tc_stream_disable(tc);
12688c2ecf20Sopenharmony_ci	if (ret < 0)
12698c2ecf20Sopenharmony_ci		dev_err(tc->dev, "main link stream stop error: %d\n", ret);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	ret = tc_main_link_disable(tc);
12728c2ecf20Sopenharmony_ci	if (ret < 0)
12738c2ecf20Sopenharmony_ci		dev_err(tc->dev, "main link disable error: %d\n", ret);
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
12778c2ecf20Sopenharmony_ci				 const struct drm_display_mode *mode,
12788c2ecf20Sopenharmony_ci				 struct drm_display_mode *adj)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	/* Fixup sync polarities, both hsync and vsync are active low */
12818c2ecf20Sopenharmony_ci	adj->flags = mode->flags;
12828c2ecf20Sopenharmony_ci	adj->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
12838c2ecf20Sopenharmony_ci	adj->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	return true;
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge,
12898c2ecf20Sopenharmony_ci					  const struct drm_display_info *info,
12908c2ecf20Sopenharmony_ci					  const struct drm_display_mode *mode)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
12938c2ecf20Sopenharmony_ci	u32 req, avail;
12948c2ecf20Sopenharmony_ci	u32 bits_per_pixel = 24;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	/* DPI interface clock limitation: upto 154 MHz */
12978c2ecf20Sopenharmony_ci	if (mode->clock > 154000)
12988c2ecf20Sopenharmony_ci		return MODE_CLOCK_HIGH;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	req = mode->clock * bits_per_pixel / 8;
13018c2ecf20Sopenharmony_ci	avail = tc->link.num_lanes * tc->link.rate;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	if (req > avail)
13048c2ecf20Sopenharmony_ci		return MODE_BAD;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	return MODE_OK;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_cistatic void tc_bridge_mode_set(struct drm_bridge *bridge,
13108c2ecf20Sopenharmony_ci			       const struct drm_display_mode *mode,
13118c2ecf20Sopenharmony_ci			       const struct drm_display_mode *adj)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	tc->mode = *mode;
13168c2ecf20Sopenharmony_ci}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cistatic struct edid *tc_get_edid(struct drm_bridge *bridge,
13198c2ecf20Sopenharmony_ci				struct drm_connector *connector)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	return drm_get_edid(connector, &tc->aux.ddc);
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_cistatic int tc_connector_get_modes(struct drm_connector *connector)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct tc_data *tc = connector_to_tc(connector);
13298c2ecf20Sopenharmony_ci	int num_modes;
13308c2ecf20Sopenharmony_ci	struct edid *edid;
13318c2ecf20Sopenharmony_ci	int ret;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	ret = tc_get_display_props(tc);
13348c2ecf20Sopenharmony_ci	if (ret < 0) {
13358c2ecf20Sopenharmony_ci		dev_err(tc->dev, "failed to read display props: %d\n", ret);
13368c2ecf20Sopenharmony_ci		return 0;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	if (tc->panel_bridge) {
13408c2ecf20Sopenharmony_ci		num_modes = drm_bridge_get_modes(tc->panel_bridge, connector);
13418c2ecf20Sopenharmony_ci		if (num_modes > 0)
13428c2ecf20Sopenharmony_ci			return num_modes;
13438c2ecf20Sopenharmony_ci	}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	edid = tc_get_edid(&tc->bridge, connector);
13468c2ecf20Sopenharmony_ci	num_modes = drm_add_edid_modes(connector, edid);
13478c2ecf20Sopenharmony_ci	kfree(edid);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	return num_modes;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs tc_connector_helper_funcs = {
13538c2ecf20Sopenharmony_ci	.get_modes = tc_connector_get_modes,
13548c2ecf20Sopenharmony_ci};
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_cistatic enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge)
13578c2ecf20Sopenharmony_ci{
13588c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
13598c2ecf20Sopenharmony_ci	bool conn;
13608c2ecf20Sopenharmony_ci	u32 val;
13618c2ecf20Sopenharmony_ci	int ret;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	ret = regmap_read(tc->regmap, GPIOI, &val);
13648c2ecf20Sopenharmony_ci	if (ret)
13658c2ecf20Sopenharmony_ci		return connector_status_unknown;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	conn = val & BIT(tc->hpd_pin);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (conn)
13708c2ecf20Sopenharmony_ci		return connector_status_connected;
13718c2ecf20Sopenharmony_ci	else
13728c2ecf20Sopenharmony_ci		return connector_status_disconnected;
13738c2ecf20Sopenharmony_ci}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_cistatic enum drm_connector_status
13768c2ecf20Sopenharmony_citc_connector_detect(struct drm_connector *connector, bool force)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	struct tc_data *tc = connector_to_tc(connector);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	if (tc->hpd_pin >= 0)
13818c2ecf20Sopenharmony_ci		return tc_bridge_detect(&tc->bridge);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (tc->panel_bridge)
13848c2ecf20Sopenharmony_ci		return connector_status_connected;
13858c2ecf20Sopenharmony_ci	else
13868c2ecf20Sopenharmony_ci		return connector_status_unknown;
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs tc_connector_funcs = {
13908c2ecf20Sopenharmony_ci	.detect = tc_connector_detect,
13918c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
13928c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
13938c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
13948c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
13958c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
13968c2ecf20Sopenharmony_ci};
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_cistatic int tc_bridge_attach(struct drm_bridge *bridge,
13998c2ecf20Sopenharmony_ci			    enum drm_bridge_attach_flags flags)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
14028c2ecf20Sopenharmony_ci	struct tc_data *tc = bridge_to_tc(bridge);
14038c2ecf20Sopenharmony_ci	struct drm_device *drm = bridge->dev;
14048c2ecf20Sopenharmony_ci	int ret;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	if (tc->panel_bridge) {
14078c2ecf20Sopenharmony_ci		/* If a connector is required then this driver shall create it */
14088c2ecf20Sopenharmony_ci		ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
14098c2ecf20Sopenharmony_ci					&tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
14108c2ecf20Sopenharmony_ci		if (ret)
14118c2ecf20Sopenharmony_ci			return ret;
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
14158c2ecf20Sopenharmony_ci		return 0;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* Create DP/eDP connector */
14188c2ecf20Sopenharmony_ci	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
14198c2ecf20Sopenharmony_ci	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, tc->bridge.type);
14208c2ecf20Sopenharmony_ci	if (ret)
14218c2ecf20Sopenharmony_ci		return ret;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* Don't poll if don't have HPD connected */
14248c2ecf20Sopenharmony_ci	if (tc->hpd_pin >= 0) {
14258c2ecf20Sopenharmony_ci		if (tc->have_irq)
14268c2ecf20Sopenharmony_ci			tc->connector.polled = DRM_CONNECTOR_POLL_HPD;
14278c2ecf20Sopenharmony_ci		else
14288c2ecf20Sopenharmony_ci			tc->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
14298c2ecf20Sopenharmony_ci					       DRM_CONNECTOR_POLL_DISCONNECT;
14308c2ecf20Sopenharmony_ci	}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	drm_display_info_set_bus_formats(&tc->connector.display_info,
14338c2ecf20Sopenharmony_ci					 &bus_format, 1);
14348c2ecf20Sopenharmony_ci	tc->connector.display_info.bus_flags =
14358c2ecf20Sopenharmony_ci		DRM_BUS_FLAG_DE_HIGH |
14368c2ecf20Sopenharmony_ci		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE |
14378c2ecf20Sopenharmony_ci		DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
14388c2ecf20Sopenharmony_ci	drm_connector_attach_encoder(&tc->connector, tc->bridge.encoder);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	return 0;
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs tc_bridge_funcs = {
14448c2ecf20Sopenharmony_ci	.attach = tc_bridge_attach,
14458c2ecf20Sopenharmony_ci	.mode_valid = tc_mode_valid,
14468c2ecf20Sopenharmony_ci	.mode_set = tc_bridge_mode_set,
14478c2ecf20Sopenharmony_ci	.enable = tc_bridge_enable,
14488c2ecf20Sopenharmony_ci	.disable = tc_bridge_disable,
14498c2ecf20Sopenharmony_ci	.mode_fixup = tc_bridge_mode_fixup,
14508c2ecf20Sopenharmony_ci	.detect = tc_bridge_detect,
14518c2ecf20Sopenharmony_ci	.get_edid = tc_get_edid,
14528c2ecf20Sopenharmony_ci};
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic bool tc_readable_reg(struct device *dev, unsigned int reg)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	return reg != SYSCTRL;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic const struct regmap_range tc_volatile_ranges[] = {
14608c2ecf20Sopenharmony_ci	regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS),
14618c2ecf20Sopenharmony_ci	regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ),
14628c2ecf20Sopenharmony_ci	regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL),
14638c2ecf20Sopenharmony_ci	regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL),
14648c2ecf20Sopenharmony_ci	regmap_reg_range(VFUEN0, VFUEN0),
14658c2ecf20Sopenharmony_ci	regmap_reg_range(INTSTS_G, INTSTS_G),
14668c2ecf20Sopenharmony_ci	regmap_reg_range(GPIOI, GPIOI),
14678c2ecf20Sopenharmony_ci};
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_cistatic const struct regmap_access_table tc_volatile_table = {
14708c2ecf20Sopenharmony_ci	.yes_ranges = tc_volatile_ranges,
14718c2ecf20Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges),
14728c2ecf20Sopenharmony_ci};
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic bool tc_writeable_reg(struct device *dev, unsigned int reg)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	return (reg != TC_IDREG) &&
14778c2ecf20Sopenharmony_ci	       (reg != DP0_LTSTAT) &&
14788c2ecf20Sopenharmony_ci	       (reg != DP0_SNKLTCHGREQ);
14798c2ecf20Sopenharmony_ci}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cistatic const struct regmap_config tc_regmap_config = {
14828c2ecf20Sopenharmony_ci	.name = "tc358767",
14838c2ecf20Sopenharmony_ci	.reg_bits = 16,
14848c2ecf20Sopenharmony_ci	.val_bits = 32,
14858c2ecf20Sopenharmony_ci	.reg_stride = 4,
14868c2ecf20Sopenharmony_ci	.max_register = PLL_DBG,
14878c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
14888c2ecf20Sopenharmony_ci	.readable_reg = tc_readable_reg,
14898c2ecf20Sopenharmony_ci	.volatile_table = &tc_volatile_table,
14908c2ecf20Sopenharmony_ci	.writeable_reg = tc_writeable_reg,
14918c2ecf20Sopenharmony_ci	.reg_format_endian = REGMAP_ENDIAN_BIG,
14928c2ecf20Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
14938c2ecf20Sopenharmony_ci};
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic irqreturn_t tc_irq_handler(int irq, void *arg)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct tc_data *tc = arg;
14988c2ecf20Sopenharmony_ci	u32 val;
14998c2ecf20Sopenharmony_ci	int r;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	r = regmap_read(tc->regmap, INTSTS_G, &val);
15028c2ecf20Sopenharmony_ci	if (r)
15038c2ecf20Sopenharmony_ci		return IRQ_NONE;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (!val)
15068c2ecf20Sopenharmony_ci		return IRQ_NONE;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	if (val & INT_SYSERR) {
15098c2ecf20Sopenharmony_ci		u32 stat = 0;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci		regmap_read(tc->regmap, SYSSTAT, &stat);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci		dev_err(tc->dev, "syserr %x\n", stat);
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	if (tc->hpd_pin >= 0 && tc->bridge.dev) {
15178c2ecf20Sopenharmony_ci		/*
15188c2ecf20Sopenharmony_ci		 * H is triggered when the GPIO goes high.
15198c2ecf20Sopenharmony_ci		 *
15208c2ecf20Sopenharmony_ci		 * LC is triggered when the GPIO goes low and stays low for
15218c2ecf20Sopenharmony_ci		 * the duration of LCNT
15228c2ecf20Sopenharmony_ci		 */
15238c2ecf20Sopenharmony_ci		bool h = val & INT_GPIO_H(tc->hpd_pin);
15248c2ecf20Sopenharmony_ci		bool lc = val & INT_GPIO_LC(tc->hpd_pin);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin,
15278c2ecf20Sopenharmony_ci			h ? "H" : "", lc ? "LC" : "");
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci		if (h || lc)
15308c2ecf20Sopenharmony_ci			drm_kms_helper_hotplug_event(tc->bridge.dev);
15318c2ecf20Sopenharmony_ci	}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	regmap_write(tc->regmap, INTSTS_G, val);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct device *dev = tc->dev;
15418c2ecf20Sopenharmony_ci	struct drm_panel *panel;
15428c2ecf20Sopenharmony_ci	int ret;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	/* port@2 is the output port */
15458c2ecf20Sopenharmony_ci	ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
15468c2ecf20Sopenharmony_ci	if (ret && ret != -ENODEV)
15478c2ecf20Sopenharmony_ci		return ret;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	if (panel) {
15508c2ecf20Sopenharmony_ci		struct drm_bridge *panel_bridge;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci		panel_bridge = devm_drm_panel_bridge_add(dev, panel);
15538c2ecf20Sopenharmony_ci		if (IS_ERR(panel_bridge))
15548c2ecf20Sopenharmony_ci			return PTR_ERR(panel_bridge);
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		tc->panel_bridge = panel_bridge;
15578c2ecf20Sopenharmony_ci		tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
15588c2ecf20Sopenharmony_ci	} else {
15598c2ecf20Sopenharmony_ci		tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	return 0;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic void tc_clk_disable(void *data)
15668c2ecf20Sopenharmony_ci{
15678c2ecf20Sopenharmony_ci	struct clk *refclk = data;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	clk_disable_unprepare(refclk);
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
15758c2ecf20Sopenharmony_ci	struct tc_data *tc;
15768c2ecf20Sopenharmony_ci	int ret;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL);
15798c2ecf20Sopenharmony_ci	if (!tc)
15808c2ecf20Sopenharmony_ci		return -ENOMEM;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	tc->dev = dev;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	ret = tc_probe_edp_bridge_endpoint(tc);
15858c2ecf20Sopenharmony_ci	if (ret)
15868c2ecf20Sopenharmony_ci		return ret;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	tc->refclk = devm_clk_get(dev, "ref");
15898c2ecf20Sopenharmony_ci	if (IS_ERR(tc->refclk)) {
15908c2ecf20Sopenharmony_ci		ret = PTR_ERR(tc->refclk);
15918c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get refclk: %d\n", ret);
15928c2ecf20Sopenharmony_ci		return ret;
15938c2ecf20Sopenharmony_ci	}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(tc->refclk);
15968c2ecf20Sopenharmony_ci	if (ret)
15978c2ecf20Sopenharmony_ci		return ret;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk);
16008c2ecf20Sopenharmony_ci	if (ret)
16018c2ecf20Sopenharmony_ci		return ret;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	/* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */
16048c2ecf20Sopenharmony_ci	usleep_range(10, 15);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* Shut down GPIO is optional */
16078c2ecf20Sopenharmony_ci	tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
16088c2ecf20Sopenharmony_ci	if (IS_ERR(tc->sd_gpio))
16098c2ecf20Sopenharmony_ci		return PTR_ERR(tc->sd_gpio);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	if (tc->sd_gpio) {
16128c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(tc->sd_gpio, 0);
16138c2ecf20Sopenharmony_ci		usleep_range(5000, 10000);
16148c2ecf20Sopenharmony_ci	}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	/* Reset GPIO is optional */
16178c2ecf20Sopenharmony_ci	tc->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
16188c2ecf20Sopenharmony_ci	if (IS_ERR(tc->reset_gpio))
16198c2ecf20Sopenharmony_ci		return PTR_ERR(tc->reset_gpio);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	if (tc->reset_gpio) {
16228c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(tc->reset_gpio, 1);
16238c2ecf20Sopenharmony_ci		usleep_range(5000, 10000);
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config);
16278c2ecf20Sopenharmony_ci	if (IS_ERR(tc->regmap)) {
16288c2ecf20Sopenharmony_ci		ret = PTR_ERR(tc->regmap);
16298c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to initialize regmap: %d\n", ret);
16308c2ecf20Sopenharmony_ci		return ret;
16318c2ecf20Sopenharmony_ci	}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "toshiba,hpd-pin",
16348c2ecf20Sopenharmony_ci				   &tc->hpd_pin);
16358c2ecf20Sopenharmony_ci	if (ret) {
16368c2ecf20Sopenharmony_ci		tc->hpd_pin = -ENODEV;
16378c2ecf20Sopenharmony_ci	} else {
16388c2ecf20Sopenharmony_ci		if (tc->hpd_pin < 0 || tc->hpd_pin > 1) {
16398c2ecf20Sopenharmony_ci			dev_err(dev, "failed to parse HPD number\n");
16408c2ecf20Sopenharmony_ci			return -EINVAL;
16418c2ecf20Sopenharmony_ci		}
16428c2ecf20Sopenharmony_ci	}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	if (client->irq > 0) {
16458c2ecf20Sopenharmony_ci		/* enable SysErr */
16468c2ecf20Sopenharmony_ci		regmap_write(tc->regmap, INTCTL_G, INT_SYSERR);
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(dev, client->irq,
16498c2ecf20Sopenharmony_ci						NULL, tc_irq_handler,
16508c2ecf20Sopenharmony_ci						IRQF_ONESHOT,
16518c2ecf20Sopenharmony_ci						"tc358767-irq", tc);
16528c2ecf20Sopenharmony_ci		if (ret) {
16538c2ecf20Sopenharmony_ci			dev_err(dev, "failed to register dp interrupt\n");
16548c2ecf20Sopenharmony_ci			return ret;
16558c2ecf20Sopenharmony_ci		}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci		tc->have_irq = true;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev);
16618c2ecf20Sopenharmony_ci	if (ret) {
16628c2ecf20Sopenharmony_ci		dev_err(tc->dev, "can not read device ID: %d\n", ret);
16638c2ecf20Sopenharmony_ci		return ret;
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	if ((tc->rev != 0x6601) && (tc->rev != 0x6603)) {
16678c2ecf20Sopenharmony_ci		dev_err(tc->dev, "invalid device ID: 0x%08x\n", tc->rev);
16688c2ecf20Sopenharmony_ci		return -EINVAL;
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	if (!tc->reset_gpio) {
16748c2ecf20Sopenharmony_ci		/*
16758c2ecf20Sopenharmony_ci		 * If the reset pin isn't present, do a software reset. It isn't
16768c2ecf20Sopenharmony_ci		 * as thorough as the hardware reset, as we can't reset the I2C
16778c2ecf20Sopenharmony_ci		 * communication block for obvious reasons, but it's getting the
16788c2ecf20Sopenharmony_ci		 * chip into a defined state.
16798c2ecf20Sopenharmony_ci		 */
16808c2ecf20Sopenharmony_ci		regmap_update_bits(tc->regmap, SYSRSTENB,
16818c2ecf20Sopenharmony_ci				ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP,
16828c2ecf20Sopenharmony_ci				0);
16838c2ecf20Sopenharmony_ci		regmap_update_bits(tc->regmap, SYSRSTENB,
16848c2ecf20Sopenharmony_ci				ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP,
16858c2ecf20Sopenharmony_ci				ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP);
16868c2ecf20Sopenharmony_ci		usleep_range(5000, 10000);
16878c2ecf20Sopenharmony_ci	}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	if (tc->hpd_pin >= 0) {
16908c2ecf20Sopenharmony_ci		u32 lcnt_reg = tc->hpd_pin == 0 ? INT_GP0_LCNT : INT_GP1_LCNT;
16918c2ecf20Sopenharmony_ci		u32 h_lc = INT_GPIO_H(tc->hpd_pin) | INT_GPIO_LC(tc->hpd_pin);
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci		/* Set LCNT to 2ms */
16948c2ecf20Sopenharmony_ci		regmap_write(tc->regmap, lcnt_reg,
16958c2ecf20Sopenharmony_ci			     clk_get_rate(tc->refclk) * 2 / 1000);
16968c2ecf20Sopenharmony_ci		/* We need the "alternate" mode for HPD */
16978c2ecf20Sopenharmony_ci		regmap_write(tc->regmap, GPIOM, BIT(tc->hpd_pin));
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci		if (tc->have_irq) {
17008c2ecf20Sopenharmony_ci			/* enable H & LC */
17018c2ecf20Sopenharmony_ci			regmap_update_bits(tc->regmap, INTCTL_G, h_lc, h_lc);
17028c2ecf20Sopenharmony_ci		}
17038c2ecf20Sopenharmony_ci	}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	ret = tc_aux_link_setup(tc);
17068c2ecf20Sopenharmony_ci	if (ret)
17078c2ecf20Sopenharmony_ci		return ret;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	/* Register DP AUX channel */
17108c2ecf20Sopenharmony_ci	tc->aux.name = "TC358767 AUX i2c adapter";
17118c2ecf20Sopenharmony_ci	tc->aux.dev = tc->dev;
17128c2ecf20Sopenharmony_ci	tc->aux.transfer = tc_aux_transfer;
17138c2ecf20Sopenharmony_ci	ret = drm_dp_aux_register(&tc->aux);
17148c2ecf20Sopenharmony_ci	if (ret)
17158c2ecf20Sopenharmony_ci		return ret;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	tc->bridge.funcs = &tc_bridge_funcs;
17188c2ecf20Sopenharmony_ci	if (tc->hpd_pin >= 0)
17198c2ecf20Sopenharmony_ci		tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
17208c2ecf20Sopenharmony_ci	tc->bridge.ops |= DRM_BRIDGE_OP_EDID;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	tc->bridge.of_node = dev->of_node;
17238c2ecf20Sopenharmony_ci	drm_bridge_add(&tc->bridge);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, tc);
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	return 0;
17288c2ecf20Sopenharmony_ci}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_cistatic int tc_remove(struct i2c_client *client)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	struct tc_data *tc = i2c_get_clientdata(client);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	drm_bridge_remove(&tc->bridge);
17358c2ecf20Sopenharmony_ci	drm_dp_aux_unregister(&tc->aux);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	return 0;
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_cistatic const struct i2c_device_id tc358767_i2c_ids[] = {
17418c2ecf20Sopenharmony_ci	{ "tc358767", 0 },
17428c2ecf20Sopenharmony_ci	{ }
17438c2ecf20Sopenharmony_ci};
17448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_cistatic const struct of_device_id tc358767_of_ids[] = {
17478c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc358767", },
17488c2ecf20Sopenharmony_ci	{ }
17498c2ecf20Sopenharmony_ci};
17508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tc358767_of_ids);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic struct i2c_driver tc358767_driver = {
17538c2ecf20Sopenharmony_ci	.driver = {
17548c2ecf20Sopenharmony_ci		.name = "tc358767",
17558c2ecf20Sopenharmony_ci		.of_match_table = tc358767_of_ids,
17568c2ecf20Sopenharmony_ci	},
17578c2ecf20Sopenharmony_ci	.id_table = tc358767_i2c_ids,
17588c2ecf20Sopenharmony_ci	.probe = tc_probe,
17598c2ecf20Sopenharmony_ci	.remove	= tc_remove,
17608c2ecf20Sopenharmony_ci};
17618c2ecf20Sopenharmony_cimodule_i2c_driver(tc358767_driver);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com>");
17648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("tc358767 eDP encoder driver");
17658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1766