xref: /openbmc/linux/drivers/gpu/drm/bridge/tc358767.c (revision 7caff0fc4296eba5e2e473e6719726c65f1b7c31)
1*7caff0fcSAndrey Gusakov /*
2*7caff0fcSAndrey Gusakov  * tc358767 eDP bridge driver
3*7caff0fcSAndrey Gusakov  *
4*7caff0fcSAndrey Gusakov  * Copyright (C) 2016 CogentEmbedded Inc
5*7caff0fcSAndrey Gusakov  * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
6*7caff0fcSAndrey Gusakov  *
7*7caff0fcSAndrey Gusakov  * Copyright (C) 2016 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de>
8*7caff0fcSAndrey Gusakov  *
9*7caff0fcSAndrey Gusakov  * Initially based on: drivers/gpu/drm/i2c/tda998x_drv.c
10*7caff0fcSAndrey Gusakov  *
11*7caff0fcSAndrey Gusakov  * Copyright (C) 2012 Texas Instruments
12*7caff0fcSAndrey Gusakov  * Author: Rob Clark <robdclark@gmail.com>
13*7caff0fcSAndrey Gusakov  *
14*7caff0fcSAndrey Gusakov  * This program is free software; you can redistribute it and/or modify
15*7caff0fcSAndrey Gusakov  * it under the terms of the GNU General Public License as published by
16*7caff0fcSAndrey Gusakov  * the Free Software Foundation; either version 2 of the License, or
17*7caff0fcSAndrey Gusakov  * (at your option) any later version.
18*7caff0fcSAndrey Gusakov  *
19*7caff0fcSAndrey Gusakov  * This program is distributed in the hope that it will be useful,
20*7caff0fcSAndrey Gusakov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21*7caff0fcSAndrey Gusakov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22*7caff0fcSAndrey Gusakov  * GNU General Public License for more details.
23*7caff0fcSAndrey Gusakov  */
24*7caff0fcSAndrey Gusakov 
25*7caff0fcSAndrey Gusakov #include <linux/clk.h>
26*7caff0fcSAndrey Gusakov #include <linux/device.h>
27*7caff0fcSAndrey Gusakov #include <linux/gpio/consumer.h>
28*7caff0fcSAndrey Gusakov #include <linux/i2c.h>
29*7caff0fcSAndrey Gusakov #include <linux/kernel.h>
30*7caff0fcSAndrey Gusakov #include <linux/module.h>
31*7caff0fcSAndrey Gusakov #include <linux/regmap.h>
32*7caff0fcSAndrey Gusakov #include <linux/slab.h>
33*7caff0fcSAndrey Gusakov 
34*7caff0fcSAndrey Gusakov #include <drm/drm_atomic_helper.h>
35*7caff0fcSAndrey Gusakov #include <drm/drm_crtc_helper.h>
36*7caff0fcSAndrey Gusakov #include <drm/drm_dp_helper.h>
37*7caff0fcSAndrey Gusakov #include <drm/drm_edid.h>
38*7caff0fcSAndrey Gusakov #include <drm/drm_of.h>
39*7caff0fcSAndrey Gusakov #include <drm/drm_panel.h>
40*7caff0fcSAndrey Gusakov 
41*7caff0fcSAndrey Gusakov /* Registers */
42*7caff0fcSAndrey Gusakov 
43*7caff0fcSAndrey Gusakov /* Display Parallel Interface */
44*7caff0fcSAndrey Gusakov #define DPIPXLFMT		0x0440
45*7caff0fcSAndrey Gusakov #define VS_POL_ACTIVE_LOW		(1 << 10)
46*7caff0fcSAndrey Gusakov #define HS_POL_ACTIVE_LOW		(1 << 9)
47*7caff0fcSAndrey Gusakov #define DE_POL_ACTIVE_HIGH		(0 << 8)
48*7caff0fcSAndrey Gusakov #define SUB_CFG_TYPE_CONFIG1		(0 << 2) /* LSB aligned */
49*7caff0fcSAndrey Gusakov #define SUB_CFG_TYPE_CONFIG2		(1 << 2) /* Loosely Packed */
50*7caff0fcSAndrey Gusakov #define SUB_CFG_TYPE_CONFIG3		(2 << 2) /* LSB aligned 8-bit */
51*7caff0fcSAndrey Gusakov #define DPI_BPP_RGB888			(0 << 0)
52*7caff0fcSAndrey Gusakov #define DPI_BPP_RGB666			(1 << 0)
53*7caff0fcSAndrey Gusakov #define DPI_BPP_RGB565			(2 << 0)
54*7caff0fcSAndrey Gusakov 
55*7caff0fcSAndrey Gusakov /* Video Path */
56*7caff0fcSAndrey Gusakov #define VPCTRL0			0x0450
57*7caff0fcSAndrey Gusakov #define OPXLFMT_RGB666			(0 << 8)
58*7caff0fcSAndrey Gusakov #define OPXLFMT_RGB888			(1 << 8)
59*7caff0fcSAndrey Gusakov #define FRMSYNC_DISABLED		(0 << 4) /* Video Timing Gen Disabled */
60*7caff0fcSAndrey Gusakov #define FRMSYNC_ENABLED			(1 << 4) /* Video Timing Gen Enabled */
61*7caff0fcSAndrey Gusakov #define MSF_DISABLED			(0 << 0) /* Magic Square FRC disabled */
62*7caff0fcSAndrey Gusakov #define MSF_ENABLED			(1 << 0) /* Magic Square FRC enabled */
63*7caff0fcSAndrey Gusakov #define HTIM01			0x0454
64*7caff0fcSAndrey Gusakov #define HTIM02			0x0458
65*7caff0fcSAndrey Gusakov #define VTIM01			0x045c
66*7caff0fcSAndrey Gusakov #define VTIM02			0x0460
67*7caff0fcSAndrey Gusakov #define VFUEN0			0x0464
68*7caff0fcSAndrey Gusakov #define VFUEN				BIT(0)   /* Video Frame Timing Upload */
69*7caff0fcSAndrey Gusakov 
70*7caff0fcSAndrey Gusakov /* System */
71*7caff0fcSAndrey Gusakov #define TC_IDREG		0x0500
72*7caff0fcSAndrey Gusakov #define SYSCTRL			0x0510
73*7caff0fcSAndrey Gusakov #define DP0_AUDSRC_NO_INPUT		(0 << 3)
74*7caff0fcSAndrey Gusakov #define DP0_AUDSRC_I2S_RX		(1 << 3)
75*7caff0fcSAndrey Gusakov #define DP0_VIDSRC_NO_INPUT		(0 << 0)
76*7caff0fcSAndrey Gusakov #define DP0_VIDSRC_DSI_RX		(1 << 0)
77*7caff0fcSAndrey Gusakov #define DP0_VIDSRC_DPI_RX		(2 << 0)
78*7caff0fcSAndrey Gusakov #define DP0_VIDSRC_COLOR_BAR		(3 << 0)
79*7caff0fcSAndrey Gusakov 
80*7caff0fcSAndrey Gusakov /* Control */
81*7caff0fcSAndrey Gusakov #define DP0CTL			0x0600
82*7caff0fcSAndrey Gusakov #define VID_MN_GEN			BIT(6)   /* Auto-generate M/N values */
83*7caff0fcSAndrey Gusakov #define EF_EN				BIT(5)   /* Enable Enhanced Framing */
84*7caff0fcSAndrey Gusakov #define VID_EN				BIT(1)   /* Video transmission enable */
85*7caff0fcSAndrey Gusakov #define DP_EN				BIT(0)   /* Enable DPTX function */
86*7caff0fcSAndrey Gusakov 
87*7caff0fcSAndrey Gusakov /* Clocks */
88*7caff0fcSAndrey Gusakov #define DP0_VIDMNGEN0		0x0610
89*7caff0fcSAndrey Gusakov #define DP0_VIDMNGEN1		0x0614
90*7caff0fcSAndrey Gusakov #define DP0_VMNGENSTATUS	0x0618
91*7caff0fcSAndrey Gusakov 
92*7caff0fcSAndrey Gusakov /* Main Channel */
93*7caff0fcSAndrey Gusakov #define DP0_SECSAMPLE		0x0640
94*7caff0fcSAndrey Gusakov #define DP0_VIDSYNCDELAY	0x0644
95*7caff0fcSAndrey Gusakov #define DP0_TOTALVAL		0x0648
96*7caff0fcSAndrey Gusakov #define DP0_STARTVAL		0x064c
97*7caff0fcSAndrey Gusakov #define DP0_ACTIVEVAL		0x0650
98*7caff0fcSAndrey Gusakov #define DP0_SYNCVAL		0x0654
99*7caff0fcSAndrey Gusakov #define DP0_MISC		0x0658
100*7caff0fcSAndrey Gusakov #define TU_SIZE_RECOMMENDED		(0x3f << 16) /* LSCLK cycles per TU */
101*7caff0fcSAndrey Gusakov #define BPC_6				(0 << 5)
102*7caff0fcSAndrey Gusakov #define BPC_8				(1 << 5)
103*7caff0fcSAndrey Gusakov 
104*7caff0fcSAndrey Gusakov /* AUX channel */
105*7caff0fcSAndrey Gusakov #define DP0_AUXCFG0		0x0660
106*7caff0fcSAndrey Gusakov #define DP0_AUXCFG1		0x0664
107*7caff0fcSAndrey Gusakov #define AUX_RX_FILTER_EN		BIT(16)
108*7caff0fcSAndrey Gusakov 
109*7caff0fcSAndrey Gusakov #define DP0_AUXADDR		0x0668
110*7caff0fcSAndrey Gusakov #define DP0_AUXWDATA(i)		(0x066c + (i) * 4)
111*7caff0fcSAndrey Gusakov #define DP0_AUXRDATA(i)		(0x067c + (i) * 4)
112*7caff0fcSAndrey Gusakov #define DP0_AUXSTATUS		0x068c
113*7caff0fcSAndrey Gusakov #define AUX_STATUS_MASK			0xf0
114*7caff0fcSAndrey Gusakov #define AUX_STATUS_SHIFT		4
115*7caff0fcSAndrey Gusakov #define AUX_TIMEOUT			BIT(1)
116*7caff0fcSAndrey Gusakov #define AUX_BUSY			BIT(0)
117*7caff0fcSAndrey Gusakov #define DP0_AUXI2CADR		0x0698
118*7caff0fcSAndrey Gusakov 
119*7caff0fcSAndrey Gusakov /* Link Training */
120*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL		0x06a0
121*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_SCRMBLDIS		BIT(13)
122*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_EN810B		BIT(12)
123*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_NOTP		(0 << 8)
124*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_TP1			(1 << 8)
125*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_TP2			(2 << 8)
126*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_LANESKEW		BIT(7)
127*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_SSCG		BIT(3)
128*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_LANES_1		(0 << 2)
129*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_LANES_2		(1 << 2)
130*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_BW27		(1 << 1)
131*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_BW162		(0 << 1)
132*7caff0fcSAndrey Gusakov #define DP0_SRCCTRL_AUTOCORRECT		BIT(0)
133*7caff0fcSAndrey Gusakov #define DP0_LTSTAT		0x06d0
134*7caff0fcSAndrey Gusakov #define LT_LOOPDONE			BIT(13)
135*7caff0fcSAndrey Gusakov #define LT_STATUS_MASK			(0x1f << 8)
136*7caff0fcSAndrey Gusakov #define LT_CHANNEL1_EQ_BITS		(DP_CHANNEL_EQ_BITS << 4)
137*7caff0fcSAndrey Gusakov #define LT_INTERLANE_ALIGN_DONE		BIT(3)
138*7caff0fcSAndrey Gusakov #define LT_CHANNEL0_EQ_BITS		(DP_CHANNEL_EQ_BITS)
139*7caff0fcSAndrey Gusakov #define DP0_SNKLTCHGREQ		0x06d4
140*7caff0fcSAndrey Gusakov #define DP0_LTLOOPCTRL		0x06d8
141*7caff0fcSAndrey Gusakov #define DP0_SNKLTCTRL		0x06e4
142*7caff0fcSAndrey Gusakov 
143*7caff0fcSAndrey Gusakov /* PHY */
144*7caff0fcSAndrey Gusakov #define DP_PHY_CTRL		0x0800
145*7caff0fcSAndrey Gusakov #define DP_PHY_RST			BIT(28)  /* DP PHY Global Soft Reset */
146*7caff0fcSAndrey Gusakov #define BGREN				BIT(25)  /* AUX PHY BGR Enable */
147*7caff0fcSAndrey Gusakov #define PWR_SW_EN			BIT(24)  /* PHY Power Switch Enable */
148*7caff0fcSAndrey Gusakov #define PHY_M1_RST			BIT(12)  /* Reset PHY1 Main Channel */
149*7caff0fcSAndrey Gusakov #define PHY_RDY				BIT(16)  /* PHY Main Channels Ready */
150*7caff0fcSAndrey Gusakov #define PHY_M0_RST			BIT(8)   /* Reset PHY0 Main Channel */
151*7caff0fcSAndrey Gusakov #define PHY_A0_EN			BIT(1)   /* PHY Aux Channel0 Enable */
152*7caff0fcSAndrey Gusakov #define PHY_M0_EN			BIT(0)   /* PHY Main Channel0 Enable */
153*7caff0fcSAndrey Gusakov 
154*7caff0fcSAndrey Gusakov /* PLL */
155*7caff0fcSAndrey Gusakov #define DP0_PLLCTRL		0x0900
156*7caff0fcSAndrey Gusakov #define DP1_PLLCTRL		0x0904	/* not defined in DS */
157*7caff0fcSAndrey Gusakov #define PXL_PLLCTRL		0x0908
158*7caff0fcSAndrey Gusakov #define PLLUPDATE			BIT(2)
159*7caff0fcSAndrey Gusakov #define PLLBYP				BIT(1)
160*7caff0fcSAndrey Gusakov #define PLLEN				BIT(0)
161*7caff0fcSAndrey Gusakov #define PXL_PLLPARAM		0x0914
162*7caff0fcSAndrey Gusakov #define IN_SEL_REFCLK			(0 << 14)
163*7caff0fcSAndrey Gusakov #define SYS_PLLPARAM		0x0918
164*7caff0fcSAndrey Gusakov #define REF_FREQ_38M4			(0 << 8) /* 38.4 MHz */
165*7caff0fcSAndrey Gusakov #define REF_FREQ_19M2			(1 << 8) /* 19.2 MHz */
166*7caff0fcSAndrey Gusakov #define REF_FREQ_26M			(2 << 8) /* 26 MHz */
167*7caff0fcSAndrey Gusakov #define REF_FREQ_13M			(3 << 8) /* 13 MHz */
168*7caff0fcSAndrey Gusakov #define SYSCLK_SEL_LSCLK		(0 << 4)
169*7caff0fcSAndrey Gusakov #define LSCLK_DIV_1			(0 << 0)
170*7caff0fcSAndrey Gusakov #define LSCLK_DIV_2			(1 << 0)
171*7caff0fcSAndrey Gusakov 
172*7caff0fcSAndrey Gusakov /* Test & Debug */
173*7caff0fcSAndrey Gusakov #define TSTCTL			0x0a00
174*7caff0fcSAndrey Gusakov #define PLL_DBG			0x0a04
175*7caff0fcSAndrey Gusakov 
176*7caff0fcSAndrey Gusakov static bool tc_test_pattern;
177*7caff0fcSAndrey Gusakov module_param_named(test, tc_test_pattern, bool, 0644);
178*7caff0fcSAndrey Gusakov 
179*7caff0fcSAndrey Gusakov struct tc_edp_link {
180*7caff0fcSAndrey Gusakov 	struct drm_dp_link	base;
181*7caff0fcSAndrey Gusakov 	u8			assr;
182*7caff0fcSAndrey Gusakov 	int			scrambler_dis;
183*7caff0fcSAndrey Gusakov 	int			spread;
184*7caff0fcSAndrey Gusakov 	int			coding8b10b;
185*7caff0fcSAndrey Gusakov 	u8			swing;
186*7caff0fcSAndrey Gusakov 	u8			preemp;
187*7caff0fcSAndrey Gusakov };
188*7caff0fcSAndrey Gusakov 
189*7caff0fcSAndrey Gusakov struct tc_data {
190*7caff0fcSAndrey Gusakov 	struct device		*dev;
191*7caff0fcSAndrey Gusakov 	struct regmap		*regmap;
192*7caff0fcSAndrey Gusakov 	struct drm_dp_aux	aux;
193*7caff0fcSAndrey Gusakov 
194*7caff0fcSAndrey Gusakov 	struct drm_bridge	bridge;
195*7caff0fcSAndrey Gusakov 	struct drm_connector	connector;
196*7caff0fcSAndrey Gusakov 	struct drm_panel	*panel;
197*7caff0fcSAndrey Gusakov 
198*7caff0fcSAndrey Gusakov 	/* link settings */
199*7caff0fcSAndrey Gusakov 	struct tc_edp_link	link;
200*7caff0fcSAndrey Gusakov 
201*7caff0fcSAndrey Gusakov 	/* display edid */
202*7caff0fcSAndrey Gusakov 	struct edid		*edid;
203*7caff0fcSAndrey Gusakov 	/* current mode */
204*7caff0fcSAndrey Gusakov 	struct drm_display_mode	*mode;
205*7caff0fcSAndrey Gusakov 
206*7caff0fcSAndrey Gusakov 	u32			rev;
207*7caff0fcSAndrey Gusakov 	u8			assr;
208*7caff0fcSAndrey Gusakov 
209*7caff0fcSAndrey Gusakov 	struct gpio_desc	*sd_gpio;
210*7caff0fcSAndrey Gusakov 	struct gpio_desc	*reset_gpio;
211*7caff0fcSAndrey Gusakov 	struct clk		*refclk;
212*7caff0fcSAndrey Gusakov };
213*7caff0fcSAndrey Gusakov 
214*7caff0fcSAndrey Gusakov static inline struct tc_data *aux_to_tc(struct drm_dp_aux *a)
215*7caff0fcSAndrey Gusakov {
216*7caff0fcSAndrey Gusakov 	return container_of(a, struct tc_data, aux);
217*7caff0fcSAndrey Gusakov }
218*7caff0fcSAndrey Gusakov 
219*7caff0fcSAndrey Gusakov static inline struct tc_data *bridge_to_tc(struct drm_bridge *b)
220*7caff0fcSAndrey Gusakov {
221*7caff0fcSAndrey Gusakov 	return container_of(b, struct tc_data, bridge);
222*7caff0fcSAndrey Gusakov }
223*7caff0fcSAndrey Gusakov 
224*7caff0fcSAndrey Gusakov static inline struct tc_data *connector_to_tc(struct drm_connector *c)
225*7caff0fcSAndrey Gusakov {
226*7caff0fcSAndrey Gusakov 	return container_of(c, struct tc_data, connector);
227*7caff0fcSAndrey Gusakov }
228*7caff0fcSAndrey Gusakov 
229*7caff0fcSAndrey Gusakov /* Simple macros to avoid repeated error checks */
230*7caff0fcSAndrey Gusakov #define tc_write(reg, var)					\
231*7caff0fcSAndrey Gusakov 	do {							\
232*7caff0fcSAndrey Gusakov 		ret = regmap_write(tc->regmap, reg, var);	\
233*7caff0fcSAndrey Gusakov 		if (ret)					\
234*7caff0fcSAndrey Gusakov 			goto err;				\
235*7caff0fcSAndrey Gusakov 	} while (0)
236*7caff0fcSAndrey Gusakov #define tc_read(reg, var)					\
237*7caff0fcSAndrey Gusakov 	do {							\
238*7caff0fcSAndrey Gusakov 		ret = regmap_read(tc->regmap, reg, var);	\
239*7caff0fcSAndrey Gusakov 		if (ret)					\
240*7caff0fcSAndrey Gusakov 			goto err;				\
241*7caff0fcSAndrey Gusakov 	} while (0)
242*7caff0fcSAndrey Gusakov 
243*7caff0fcSAndrey Gusakov static inline int tc_poll_timeout(struct regmap *map, unsigned int addr,
244*7caff0fcSAndrey Gusakov 				  unsigned int cond_mask,
245*7caff0fcSAndrey Gusakov 				  unsigned int cond_value,
246*7caff0fcSAndrey Gusakov 				  unsigned long sleep_us, u64 timeout_us)
247*7caff0fcSAndrey Gusakov {
248*7caff0fcSAndrey Gusakov 	ktime_t timeout = ktime_add_us(ktime_get(), timeout_us);
249*7caff0fcSAndrey Gusakov 	unsigned int val;
250*7caff0fcSAndrey Gusakov 	int ret;
251*7caff0fcSAndrey Gusakov 
252*7caff0fcSAndrey Gusakov 	for (;;) {
253*7caff0fcSAndrey Gusakov 		ret = regmap_read(map, addr, &val);
254*7caff0fcSAndrey Gusakov 		if (ret)
255*7caff0fcSAndrey Gusakov 			break;
256*7caff0fcSAndrey Gusakov 		if ((val & cond_mask) == cond_value)
257*7caff0fcSAndrey Gusakov 			break;
258*7caff0fcSAndrey Gusakov 		if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) {
259*7caff0fcSAndrey Gusakov 			ret = regmap_read(map, addr, &val);
260*7caff0fcSAndrey Gusakov 			break;
261*7caff0fcSAndrey Gusakov 		}
262*7caff0fcSAndrey Gusakov 		if (sleep_us)
263*7caff0fcSAndrey Gusakov 			usleep_range((sleep_us >> 2) + 1, sleep_us);
264*7caff0fcSAndrey Gusakov 	}
265*7caff0fcSAndrey Gusakov 	return ret ?: (((val & cond_mask) == cond_value) ? 0 : -ETIMEDOUT);
266*7caff0fcSAndrey Gusakov }
267*7caff0fcSAndrey Gusakov 
268*7caff0fcSAndrey Gusakov static int tc_aux_wait_busy(struct tc_data *tc, unsigned int timeout_ms)
269*7caff0fcSAndrey Gusakov {
270*7caff0fcSAndrey Gusakov 	return tc_poll_timeout(tc->regmap, DP0_AUXSTATUS, AUX_BUSY, 0,
271*7caff0fcSAndrey Gusakov 			       1000, 1000 * timeout_ms);
272*7caff0fcSAndrey Gusakov }
273*7caff0fcSAndrey Gusakov 
274*7caff0fcSAndrey Gusakov static int tc_aux_get_status(struct tc_data *tc, u8 *reply)
275*7caff0fcSAndrey Gusakov {
276*7caff0fcSAndrey Gusakov 	int ret;
277*7caff0fcSAndrey Gusakov 	u32 value;
278*7caff0fcSAndrey Gusakov 
279*7caff0fcSAndrey Gusakov 	ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &value);
280*7caff0fcSAndrey Gusakov 	if (ret < 0)
281*7caff0fcSAndrey Gusakov 		return ret;
282*7caff0fcSAndrey Gusakov 	if (value & AUX_BUSY) {
283*7caff0fcSAndrey Gusakov 		if (value & AUX_TIMEOUT) {
284*7caff0fcSAndrey Gusakov 			dev_err(tc->dev, "i2c access timeout!\n");
285*7caff0fcSAndrey Gusakov 			return -ETIMEDOUT;
286*7caff0fcSAndrey Gusakov 		}
287*7caff0fcSAndrey Gusakov 		return -EBUSY;
288*7caff0fcSAndrey Gusakov 	}
289*7caff0fcSAndrey Gusakov 
290*7caff0fcSAndrey Gusakov 	*reply = (value & AUX_STATUS_MASK) >> AUX_STATUS_SHIFT;
291*7caff0fcSAndrey Gusakov 	return 0;
292*7caff0fcSAndrey Gusakov }
293*7caff0fcSAndrey Gusakov 
294*7caff0fcSAndrey Gusakov static ssize_t tc_aux_transfer(struct drm_dp_aux *aux,
295*7caff0fcSAndrey Gusakov 			       struct drm_dp_aux_msg *msg)
296*7caff0fcSAndrey Gusakov {
297*7caff0fcSAndrey Gusakov 	struct tc_data *tc = aux_to_tc(aux);
298*7caff0fcSAndrey Gusakov 	size_t size = min_t(size_t, 8, msg->size);
299*7caff0fcSAndrey Gusakov 	u8 request = msg->request & ~DP_AUX_I2C_MOT;
300*7caff0fcSAndrey Gusakov 	u8 *buf = msg->buffer;
301*7caff0fcSAndrey Gusakov 	u32 tmp = 0;
302*7caff0fcSAndrey Gusakov 	int i = 0;
303*7caff0fcSAndrey Gusakov 	int ret;
304*7caff0fcSAndrey Gusakov 
305*7caff0fcSAndrey Gusakov 	if (size == 0)
306*7caff0fcSAndrey Gusakov 		return 0;
307*7caff0fcSAndrey Gusakov 
308*7caff0fcSAndrey Gusakov 	ret = tc_aux_wait_busy(tc, 100);
309*7caff0fcSAndrey Gusakov 	if (ret)
310*7caff0fcSAndrey Gusakov 		goto err;
311*7caff0fcSAndrey Gusakov 
312*7caff0fcSAndrey Gusakov 	if (request == DP_AUX_I2C_WRITE || request == DP_AUX_NATIVE_WRITE) {
313*7caff0fcSAndrey Gusakov 		/* Store data */
314*7caff0fcSAndrey Gusakov 		while (i < size) {
315*7caff0fcSAndrey Gusakov 			if (request == DP_AUX_NATIVE_WRITE)
316*7caff0fcSAndrey Gusakov 				tmp = tmp | (buf[i] << (8 * (i & 0x3)));
317*7caff0fcSAndrey Gusakov 			else
318*7caff0fcSAndrey Gusakov 				tmp = (tmp << 8) | buf[i];
319*7caff0fcSAndrey Gusakov 			i++;
320*7caff0fcSAndrey Gusakov 			if (((i % 4) == 0) || (i == size)) {
321*7caff0fcSAndrey Gusakov 				tc_write(DP0_AUXWDATA(i >> 2), tmp);
322*7caff0fcSAndrey Gusakov 				tmp = 0;
323*7caff0fcSAndrey Gusakov 			}
324*7caff0fcSAndrey Gusakov 		}
325*7caff0fcSAndrey Gusakov 	} else if (request != DP_AUX_I2C_READ &&
326*7caff0fcSAndrey Gusakov 		   request != DP_AUX_NATIVE_READ) {
327*7caff0fcSAndrey Gusakov 		return -EINVAL;
328*7caff0fcSAndrey Gusakov 	}
329*7caff0fcSAndrey Gusakov 
330*7caff0fcSAndrey Gusakov 	/* Store address */
331*7caff0fcSAndrey Gusakov 	tc_write(DP0_AUXADDR, msg->address);
332*7caff0fcSAndrey Gusakov 	/* Start transfer */
333*7caff0fcSAndrey Gusakov 	tc_write(DP0_AUXCFG0, ((size - 1) << 8) | request);
334*7caff0fcSAndrey Gusakov 
335*7caff0fcSAndrey Gusakov 	ret = tc_aux_wait_busy(tc, 100);
336*7caff0fcSAndrey Gusakov 	if (ret)
337*7caff0fcSAndrey Gusakov 		goto err;
338*7caff0fcSAndrey Gusakov 
339*7caff0fcSAndrey Gusakov 	ret = tc_aux_get_status(tc, &msg->reply);
340*7caff0fcSAndrey Gusakov 	if (ret)
341*7caff0fcSAndrey Gusakov 		goto err;
342*7caff0fcSAndrey Gusakov 
343*7caff0fcSAndrey Gusakov 	if (request == DP_AUX_I2C_READ || request == DP_AUX_NATIVE_READ) {
344*7caff0fcSAndrey Gusakov 		/* Read data */
345*7caff0fcSAndrey Gusakov 		while (i < size) {
346*7caff0fcSAndrey Gusakov 			if ((i % 4) == 0)
347*7caff0fcSAndrey Gusakov 				tc_read(DP0_AUXRDATA(i >> 2), &tmp);
348*7caff0fcSAndrey Gusakov 			buf[i] = tmp & 0xff;
349*7caff0fcSAndrey Gusakov 			tmp = tmp >> 8;
350*7caff0fcSAndrey Gusakov 			i++;
351*7caff0fcSAndrey Gusakov 		}
352*7caff0fcSAndrey Gusakov 	}
353*7caff0fcSAndrey Gusakov 
354*7caff0fcSAndrey Gusakov 	return size;
355*7caff0fcSAndrey Gusakov err:
356*7caff0fcSAndrey Gusakov 	return ret;
357*7caff0fcSAndrey Gusakov }
358*7caff0fcSAndrey Gusakov 
359*7caff0fcSAndrey Gusakov static const char * const training_pattern1_errors[] = {
360*7caff0fcSAndrey Gusakov 	"No errors",
361*7caff0fcSAndrey Gusakov 	"Aux write error",
362*7caff0fcSAndrey Gusakov 	"Aux read error",
363*7caff0fcSAndrey Gusakov 	"Max voltage reached error",
364*7caff0fcSAndrey Gusakov 	"Loop counter expired error",
365*7caff0fcSAndrey Gusakov 	"res", "res", "res"
366*7caff0fcSAndrey Gusakov };
367*7caff0fcSAndrey Gusakov 
368*7caff0fcSAndrey Gusakov static const char * const training_pattern2_errors[] = {
369*7caff0fcSAndrey Gusakov 	"No errors",
370*7caff0fcSAndrey Gusakov 	"Aux write error",
371*7caff0fcSAndrey Gusakov 	"Aux read error",
372*7caff0fcSAndrey Gusakov 	"Clock recovery failed error",
373*7caff0fcSAndrey Gusakov 	"Loop counter expired error",
374*7caff0fcSAndrey Gusakov 	"res", "res", "res"
375*7caff0fcSAndrey Gusakov };
376*7caff0fcSAndrey Gusakov 
377*7caff0fcSAndrey Gusakov static u32 tc_srcctrl(struct tc_data *tc)
378*7caff0fcSAndrey Gusakov {
379*7caff0fcSAndrey Gusakov 	/*
380*7caff0fcSAndrey Gusakov 	 * No training pattern, skew lane 1 data by two LSCLK cycles with
381*7caff0fcSAndrey Gusakov 	 * respect to lane 0 data, AutoCorrect Mode = 0
382*7caff0fcSAndrey Gusakov 	 */
383*7caff0fcSAndrey Gusakov 	u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW;
384*7caff0fcSAndrey Gusakov 
385*7caff0fcSAndrey Gusakov 	if (tc->link.scrambler_dis)
386*7caff0fcSAndrey Gusakov 		reg |= DP0_SRCCTRL_SCRMBLDIS;	/* Scrambler Disabled */
387*7caff0fcSAndrey Gusakov 	if (tc->link.coding8b10b)
388*7caff0fcSAndrey Gusakov 		/* Enable 8/10B Encoder (TxData[19:16] not used) */
389*7caff0fcSAndrey Gusakov 		reg |= DP0_SRCCTRL_EN810B;
390*7caff0fcSAndrey Gusakov 	if (tc->link.spread)
391*7caff0fcSAndrey Gusakov 		reg |= DP0_SRCCTRL_SSCG;	/* Spread Spectrum Enable */
392*7caff0fcSAndrey Gusakov 	if (tc->link.base.num_lanes == 2)
393*7caff0fcSAndrey Gusakov 		reg |= DP0_SRCCTRL_LANES_2;	/* Two Main Channel Lanes */
394*7caff0fcSAndrey Gusakov 	if (tc->link.base.rate != 162000)
395*7caff0fcSAndrey Gusakov 		reg |= DP0_SRCCTRL_BW27;	/* 2.7 Gbps link */
396*7caff0fcSAndrey Gusakov 	return reg;
397*7caff0fcSAndrey Gusakov }
398*7caff0fcSAndrey Gusakov 
399*7caff0fcSAndrey Gusakov static void tc_wait_pll_lock(struct tc_data *tc)
400*7caff0fcSAndrey Gusakov {
401*7caff0fcSAndrey Gusakov 	/* Wait for PLL to lock: up to 2.09 ms, depending on refclk */
402*7caff0fcSAndrey Gusakov 	usleep_range(3000, 6000);
403*7caff0fcSAndrey Gusakov }
404*7caff0fcSAndrey Gusakov 
405*7caff0fcSAndrey Gusakov static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock)
406*7caff0fcSAndrey Gusakov {
407*7caff0fcSAndrey Gusakov 	int ret;
408*7caff0fcSAndrey Gusakov 	int i_pre, best_pre = 1;
409*7caff0fcSAndrey Gusakov 	int i_post, best_post = 1;
410*7caff0fcSAndrey Gusakov 	int div, best_div = 1;
411*7caff0fcSAndrey Gusakov 	int mul, best_mul = 1;
412*7caff0fcSAndrey Gusakov 	int delta, best_delta;
413*7caff0fcSAndrey Gusakov 	int ext_div[] = {1, 2, 3, 5, 7};
414*7caff0fcSAndrey Gusakov 	int best_pixelclock = 0;
415*7caff0fcSAndrey Gusakov 	int vco_hi = 0;
416*7caff0fcSAndrey Gusakov 
417*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock,
418*7caff0fcSAndrey Gusakov 		refclk);
419*7caff0fcSAndrey Gusakov 	best_delta = pixelclock;
420*7caff0fcSAndrey Gusakov 	/* Loop over all possible ext_divs, skipping invalid configurations */
421*7caff0fcSAndrey Gusakov 	for (i_pre = 0; i_pre < ARRAY_SIZE(ext_div); i_pre++) {
422*7caff0fcSAndrey Gusakov 		/*
423*7caff0fcSAndrey Gusakov 		 * refclk / ext_pre_div should be in the 1 to 200 MHz range.
424*7caff0fcSAndrey Gusakov 		 * We don't allow any refclk > 200 MHz, only check lower bounds.
425*7caff0fcSAndrey Gusakov 		 */
426*7caff0fcSAndrey Gusakov 		if (refclk / ext_div[i_pre] < 1000000)
427*7caff0fcSAndrey Gusakov 			continue;
428*7caff0fcSAndrey Gusakov 		for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) {
429*7caff0fcSAndrey Gusakov 			for (div = 1; div <= 16; div++) {
430*7caff0fcSAndrey Gusakov 				u32 clk;
431*7caff0fcSAndrey Gusakov 				u64 tmp;
432*7caff0fcSAndrey Gusakov 
433*7caff0fcSAndrey Gusakov 				tmp = pixelclock * ext_div[i_pre] *
434*7caff0fcSAndrey Gusakov 				      ext_div[i_post] * div;
435*7caff0fcSAndrey Gusakov 				do_div(tmp, refclk);
436*7caff0fcSAndrey Gusakov 				mul = tmp;
437*7caff0fcSAndrey Gusakov 
438*7caff0fcSAndrey Gusakov 				/* Check limits */
439*7caff0fcSAndrey Gusakov 				if ((mul < 1) || (mul > 128))
440*7caff0fcSAndrey Gusakov 					continue;
441*7caff0fcSAndrey Gusakov 
442*7caff0fcSAndrey Gusakov 				clk = (refclk / ext_div[i_pre] / div) * mul;
443*7caff0fcSAndrey Gusakov 				/*
444*7caff0fcSAndrey Gusakov 				 * refclk * mul / (ext_pre_div * pre_div)
445*7caff0fcSAndrey Gusakov 				 * should be in the 150 to 650 MHz range
446*7caff0fcSAndrey Gusakov 				 */
447*7caff0fcSAndrey Gusakov 				if ((clk > 650000000) || (clk < 150000000))
448*7caff0fcSAndrey Gusakov 					continue;
449*7caff0fcSAndrey Gusakov 
450*7caff0fcSAndrey Gusakov 				clk = clk / ext_div[i_post];
451*7caff0fcSAndrey Gusakov 				delta = clk - pixelclock;
452*7caff0fcSAndrey Gusakov 
453*7caff0fcSAndrey Gusakov 				if (abs(delta) < abs(best_delta)) {
454*7caff0fcSAndrey Gusakov 					best_pre = i_pre;
455*7caff0fcSAndrey Gusakov 					best_post = i_post;
456*7caff0fcSAndrey Gusakov 					best_div = div;
457*7caff0fcSAndrey Gusakov 					best_mul = mul;
458*7caff0fcSAndrey Gusakov 					best_delta = delta;
459*7caff0fcSAndrey Gusakov 					best_pixelclock = clk;
460*7caff0fcSAndrey Gusakov 				}
461*7caff0fcSAndrey Gusakov 			}
462*7caff0fcSAndrey Gusakov 		}
463*7caff0fcSAndrey Gusakov 	}
464*7caff0fcSAndrey Gusakov 	if (best_pixelclock == 0) {
465*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "Failed to calc clock for %d pixelclock\n",
466*7caff0fcSAndrey Gusakov 			pixelclock);
467*7caff0fcSAndrey Gusakov 		return -EINVAL;
468*7caff0fcSAndrey Gusakov 	}
469*7caff0fcSAndrey Gusakov 
470*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock,
471*7caff0fcSAndrey Gusakov 		best_delta);
472*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "PLL: %d / %d / %d * %d / %d\n", refclk,
473*7caff0fcSAndrey Gusakov 		ext_div[best_pre], best_div, best_mul, ext_div[best_post]);
474*7caff0fcSAndrey Gusakov 
475*7caff0fcSAndrey Gusakov 	/* if VCO >= 300 MHz */
476*7caff0fcSAndrey Gusakov 	if (refclk / ext_div[best_pre] / best_div * best_mul >= 300000000)
477*7caff0fcSAndrey Gusakov 		vco_hi = 1;
478*7caff0fcSAndrey Gusakov 	/* see DS */
479*7caff0fcSAndrey Gusakov 	if (best_div == 16)
480*7caff0fcSAndrey Gusakov 		best_div = 0;
481*7caff0fcSAndrey Gusakov 	if (best_mul == 128)
482*7caff0fcSAndrey Gusakov 		best_mul = 0;
483*7caff0fcSAndrey Gusakov 
484*7caff0fcSAndrey Gusakov 	/* Power up PLL and switch to bypass */
485*7caff0fcSAndrey Gusakov 	tc_write(PXL_PLLCTRL, PLLBYP | PLLEN);
486*7caff0fcSAndrey Gusakov 
487*7caff0fcSAndrey Gusakov 	tc_write(PXL_PLLPARAM,
488*7caff0fcSAndrey Gusakov 		 (vco_hi << 24) |		/* For PLL VCO >= 300 MHz = 1 */
489*7caff0fcSAndrey Gusakov 		 (ext_div[best_pre] << 20) |	/* External Pre-divider */
490*7caff0fcSAndrey Gusakov 		 (ext_div[best_post] << 16) |	/* External Post-divider */
491*7caff0fcSAndrey Gusakov 		 IN_SEL_REFCLK |		/* Use RefClk as PLL input */
492*7caff0fcSAndrey Gusakov 		 (best_div << 8) |		/* Divider for PLL RefClk */
493*7caff0fcSAndrey Gusakov 		 (best_mul << 0));		/* Multiplier for PLL */
494*7caff0fcSAndrey Gusakov 
495*7caff0fcSAndrey Gusakov 	/* Force PLL parameter update and disable bypass */
496*7caff0fcSAndrey Gusakov 	tc_write(PXL_PLLCTRL, PLLUPDATE | PLLEN);
497*7caff0fcSAndrey Gusakov 
498*7caff0fcSAndrey Gusakov 	tc_wait_pll_lock(tc);
499*7caff0fcSAndrey Gusakov 
500*7caff0fcSAndrey Gusakov 	return 0;
501*7caff0fcSAndrey Gusakov err:
502*7caff0fcSAndrey Gusakov 	return ret;
503*7caff0fcSAndrey Gusakov }
504*7caff0fcSAndrey Gusakov 
505*7caff0fcSAndrey Gusakov static int tc_pxl_pll_dis(struct tc_data *tc)
506*7caff0fcSAndrey Gusakov {
507*7caff0fcSAndrey Gusakov 	/* Enable PLL bypass, power down PLL */
508*7caff0fcSAndrey Gusakov 	return regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP);
509*7caff0fcSAndrey Gusakov }
510*7caff0fcSAndrey Gusakov 
511*7caff0fcSAndrey Gusakov static int tc_stream_clock_calc(struct tc_data *tc)
512*7caff0fcSAndrey Gusakov {
513*7caff0fcSAndrey Gusakov 	int ret;
514*7caff0fcSAndrey Gusakov 	/*
515*7caff0fcSAndrey Gusakov 	 * If the Stream clock and Link Symbol clock are
516*7caff0fcSAndrey Gusakov 	 * asynchronous with each other, the value of M changes over
517*7caff0fcSAndrey Gusakov 	 * time. This way of generating link clock and stream
518*7caff0fcSAndrey Gusakov 	 * clock is called Asynchronous Clock mode. The value M
519*7caff0fcSAndrey Gusakov 	 * must change while the value N stays constant. The
520*7caff0fcSAndrey Gusakov 	 * value of N in this Asynchronous Clock mode must be set
521*7caff0fcSAndrey Gusakov 	 * to 2^15 or 32,768.
522*7caff0fcSAndrey Gusakov 	 *
523*7caff0fcSAndrey Gusakov 	 * LSCLK = 1/10 of high speed link clock
524*7caff0fcSAndrey Gusakov 	 *
525*7caff0fcSAndrey Gusakov 	 * f_STRMCLK = M/N * f_LSCLK
526*7caff0fcSAndrey Gusakov 	 * M/N = f_STRMCLK / f_LSCLK
527*7caff0fcSAndrey Gusakov 	 *
528*7caff0fcSAndrey Gusakov 	 */
529*7caff0fcSAndrey Gusakov 	tc_write(DP0_VIDMNGEN1, 32768);
530*7caff0fcSAndrey Gusakov 
531*7caff0fcSAndrey Gusakov 	return 0;
532*7caff0fcSAndrey Gusakov err:
533*7caff0fcSAndrey Gusakov 	return ret;
534*7caff0fcSAndrey Gusakov }
535*7caff0fcSAndrey Gusakov 
536*7caff0fcSAndrey Gusakov static int tc_aux_link_setup(struct tc_data *tc)
537*7caff0fcSAndrey Gusakov {
538*7caff0fcSAndrey Gusakov 	unsigned long rate;
539*7caff0fcSAndrey Gusakov 	u32 value;
540*7caff0fcSAndrey Gusakov 	int ret;
541*7caff0fcSAndrey Gusakov 
542*7caff0fcSAndrey Gusakov 	rate = clk_get_rate(tc->refclk);
543*7caff0fcSAndrey Gusakov 	switch (rate) {
544*7caff0fcSAndrey Gusakov 	case 38400000:
545*7caff0fcSAndrey Gusakov 		value = REF_FREQ_38M4;
546*7caff0fcSAndrey Gusakov 		break;
547*7caff0fcSAndrey Gusakov 	case 26000000:
548*7caff0fcSAndrey Gusakov 		value = REF_FREQ_26M;
549*7caff0fcSAndrey Gusakov 		break;
550*7caff0fcSAndrey Gusakov 	case 19200000:
551*7caff0fcSAndrey Gusakov 		value = REF_FREQ_19M2;
552*7caff0fcSAndrey Gusakov 		break;
553*7caff0fcSAndrey Gusakov 	case 13000000:
554*7caff0fcSAndrey Gusakov 		value = REF_FREQ_13M;
555*7caff0fcSAndrey Gusakov 		break;
556*7caff0fcSAndrey Gusakov 	default:
557*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "Invalid refclk rate: %lu Hz\n", rate);
558*7caff0fcSAndrey Gusakov 		return -EINVAL;
559*7caff0fcSAndrey Gusakov 	}
560*7caff0fcSAndrey Gusakov 
561*7caff0fcSAndrey Gusakov 	/* Setup DP-PHY / PLL */
562*7caff0fcSAndrey Gusakov 	value |= SYSCLK_SEL_LSCLK | LSCLK_DIV_2;
563*7caff0fcSAndrey Gusakov 	tc_write(SYS_PLLPARAM, value);
564*7caff0fcSAndrey Gusakov 
565*7caff0fcSAndrey Gusakov 	tc_write(DP_PHY_CTRL, BGREN | PWR_SW_EN | BIT(2) | PHY_A0_EN);
566*7caff0fcSAndrey Gusakov 
567*7caff0fcSAndrey Gusakov 	/*
568*7caff0fcSAndrey Gusakov 	 * Initially PLLs are in bypass. Force PLL parameter update,
569*7caff0fcSAndrey Gusakov 	 * disable PLL bypass, enable PLL
570*7caff0fcSAndrey Gusakov 	 */
571*7caff0fcSAndrey Gusakov 	tc_write(DP0_PLLCTRL, PLLUPDATE | PLLEN);
572*7caff0fcSAndrey Gusakov 	tc_wait_pll_lock(tc);
573*7caff0fcSAndrey Gusakov 
574*7caff0fcSAndrey Gusakov 	tc_write(DP1_PLLCTRL, PLLUPDATE | PLLEN);
575*7caff0fcSAndrey Gusakov 	tc_wait_pll_lock(tc);
576*7caff0fcSAndrey Gusakov 
577*7caff0fcSAndrey Gusakov 	ret = tc_poll_timeout(tc->regmap, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 1,
578*7caff0fcSAndrey Gusakov 			      1000);
579*7caff0fcSAndrey Gusakov 	if (ret == -ETIMEDOUT) {
580*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "Timeout waiting for PHY to become ready");
581*7caff0fcSAndrey Gusakov 		return ret;
582*7caff0fcSAndrey Gusakov 	} else if (ret)
583*7caff0fcSAndrey Gusakov 		goto err;
584*7caff0fcSAndrey Gusakov 
585*7caff0fcSAndrey Gusakov 	/* Setup AUX link */
586*7caff0fcSAndrey Gusakov 	tc_write(DP0_AUXCFG1, AUX_RX_FILTER_EN |
587*7caff0fcSAndrey Gusakov 		 (0x06 << 8) |	/* Aux Bit Period Calculator Threshold */
588*7caff0fcSAndrey Gusakov 		 (0x3f << 0));	/* Aux Response Timeout Timer */
589*7caff0fcSAndrey Gusakov 
590*7caff0fcSAndrey Gusakov 	return 0;
591*7caff0fcSAndrey Gusakov err:
592*7caff0fcSAndrey Gusakov 	dev_err(tc->dev, "tc_aux_link_setup failed: %d\n", ret);
593*7caff0fcSAndrey Gusakov 	return ret;
594*7caff0fcSAndrey Gusakov }
595*7caff0fcSAndrey Gusakov 
596*7caff0fcSAndrey Gusakov static int tc_get_display_props(struct tc_data *tc)
597*7caff0fcSAndrey Gusakov {
598*7caff0fcSAndrey Gusakov 	int ret;
599*7caff0fcSAndrey Gusakov 	/* temp buffer */
600*7caff0fcSAndrey Gusakov 	u8 tmp[8];
601*7caff0fcSAndrey Gusakov 
602*7caff0fcSAndrey Gusakov 	/* Read DP Rx Link Capability */
603*7caff0fcSAndrey Gusakov 	ret = drm_dp_link_probe(&tc->aux, &tc->link.base);
604*7caff0fcSAndrey Gusakov 	if (ret < 0)
605*7caff0fcSAndrey Gusakov 		goto err_dpcd_read;
606*7caff0fcSAndrey Gusakov 	if ((tc->link.base.rate != 162000) && (tc->link.base.rate != 270000))
607*7caff0fcSAndrey Gusakov 		goto err_dpcd_inval;
608*7caff0fcSAndrey Gusakov 
609*7caff0fcSAndrey Gusakov 	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, tmp);
610*7caff0fcSAndrey Gusakov 	if (ret < 0)
611*7caff0fcSAndrey Gusakov 		goto err_dpcd_read;
612*7caff0fcSAndrey Gusakov 	tc->link.spread = tmp[0] & BIT(0); /* 0.5% down spread */
613*7caff0fcSAndrey Gusakov 
614*7caff0fcSAndrey Gusakov 	ret = drm_dp_dpcd_readb(&tc->aux, DP_MAIN_LINK_CHANNEL_CODING, tmp);
615*7caff0fcSAndrey Gusakov 	if (ret < 0)
616*7caff0fcSAndrey Gusakov 		goto err_dpcd_read;
617*7caff0fcSAndrey Gusakov 	tc->link.coding8b10b = tmp[0] & BIT(0);
618*7caff0fcSAndrey Gusakov 	tc->link.scrambler_dis = 0;
619*7caff0fcSAndrey Gusakov 	/* read assr */
620*7caff0fcSAndrey Gusakov 	ret = drm_dp_dpcd_readb(&tc->aux, DP_EDP_CONFIGURATION_SET, tmp);
621*7caff0fcSAndrey Gusakov 	if (ret < 0)
622*7caff0fcSAndrey Gusakov 		goto err_dpcd_read;
623*7caff0fcSAndrey Gusakov 	tc->link.assr = tmp[0] & DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
624*7caff0fcSAndrey Gusakov 
625*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n",
626*7caff0fcSAndrey Gusakov 		tc->link.base.revision >> 4, tc->link.base.revision & 0x0f,
627*7caff0fcSAndrey Gusakov 		(tc->link.base.rate == 162000) ? "1.62Gbps" : "2.7Gbps",
628*7caff0fcSAndrey Gusakov 		tc->link.base.num_lanes,
629*7caff0fcSAndrey Gusakov 		(tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ?
630*7caff0fcSAndrey Gusakov 		"enhanced" : "non-enhanced");
631*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "ANSI 8B/10B: %d\n", tc->link.coding8b10b);
632*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n",
633*7caff0fcSAndrey Gusakov 		tc->link.assr, tc->assr);
634*7caff0fcSAndrey Gusakov 
635*7caff0fcSAndrey Gusakov 	return 0;
636*7caff0fcSAndrey Gusakov 
637*7caff0fcSAndrey Gusakov err_dpcd_read:
638*7caff0fcSAndrey Gusakov 	dev_err(tc->dev, "failed to read DPCD: %d\n", ret);
639*7caff0fcSAndrey Gusakov 	return ret;
640*7caff0fcSAndrey Gusakov err_dpcd_inval:
641*7caff0fcSAndrey Gusakov 	dev_err(tc->dev, "invalid DPCD\n");
642*7caff0fcSAndrey Gusakov 	return -EINVAL;
643*7caff0fcSAndrey Gusakov }
644*7caff0fcSAndrey Gusakov 
645*7caff0fcSAndrey Gusakov static int tc_set_video_mode(struct tc_data *tc, struct drm_display_mode *mode)
646*7caff0fcSAndrey Gusakov {
647*7caff0fcSAndrey Gusakov 	int ret;
648*7caff0fcSAndrey Gusakov 	int vid_sync_dly;
649*7caff0fcSAndrey Gusakov 	int max_tu_symbol;
650*7caff0fcSAndrey Gusakov 
651*7caff0fcSAndrey Gusakov 	int left_margin = mode->htotal - mode->hsync_end;
652*7caff0fcSAndrey Gusakov 	int right_margin = mode->hsync_start - mode->hdisplay;
653*7caff0fcSAndrey Gusakov 	int hsync_len = mode->hsync_end - mode->hsync_start;
654*7caff0fcSAndrey Gusakov 	int upper_margin = mode->vtotal - mode->vsync_end;
655*7caff0fcSAndrey Gusakov 	int lower_margin = mode->vsync_start - mode->vdisplay;
656*7caff0fcSAndrey Gusakov 	int vsync_len = mode->vsync_end - mode->vsync_start;
657*7caff0fcSAndrey Gusakov 
658*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "set mode %dx%d\n",
659*7caff0fcSAndrey Gusakov 		mode->hdisplay, mode->vdisplay);
660*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "H margin %d,%d sync %d\n",
661*7caff0fcSAndrey Gusakov 		left_margin, right_margin, hsync_len);
662*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "V margin %d,%d sync %d\n",
663*7caff0fcSAndrey Gusakov 		upper_margin, lower_margin, vsync_len);
664*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal);
665*7caff0fcSAndrey Gusakov 
666*7caff0fcSAndrey Gusakov 
667*7caff0fcSAndrey Gusakov 	/* LCD Ctl Frame Size */
668*7caff0fcSAndrey Gusakov 	tc_write(VPCTRL0, (0x40 << 20) /* VSDELAY */ |
669*7caff0fcSAndrey Gusakov 		 OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED);
670*7caff0fcSAndrey Gusakov 	tc_write(HTIM01, (left_margin << 16) |		/* H back porch */
671*7caff0fcSAndrey Gusakov 			 (hsync_len << 0));		/* Hsync */
672*7caff0fcSAndrey Gusakov 	tc_write(HTIM02, (right_margin << 16) |		/* H front porch */
673*7caff0fcSAndrey Gusakov 			 (mode->hdisplay << 0));	/* width */
674*7caff0fcSAndrey Gusakov 	tc_write(VTIM01, (upper_margin << 16) |		/* V back porch */
675*7caff0fcSAndrey Gusakov 			 (vsync_len << 0));		/* Vsync */
676*7caff0fcSAndrey Gusakov 	tc_write(VTIM02, (lower_margin << 16) |		/* V front porch */
677*7caff0fcSAndrey Gusakov 			 (mode->vdisplay << 0));	/* height */
678*7caff0fcSAndrey Gusakov 	tc_write(VFUEN0, VFUEN);		/* update settings */
679*7caff0fcSAndrey Gusakov 
680*7caff0fcSAndrey Gusakov 	/* Test pattern settings */
681*7caff0fcSAndrey Gusakov 	tc_write(TSTCTL,
682*7caff0fcSAndrey Gusakov 		 (120 << 24) |	/* Red Color component value */
683*7caff0fcSAndrey Gusakov 		 (20 << 16) |	/* Green Color component value */
684*7caff0fcSAndrey Gusakov 		 (99 << 8) |	/* Blue Color component value */
685*7caff0fcSAndrey Gusakov 		 (1 << 4) |	/* Enable I2C Filter */
686*7caff0fcSAndrey Gusakov 		 (2 << 0) |	/* Color bar Mode */
687*7caff0fcSAndrey Gusakov 		 0);
688*7caff0fcSAndrey Gusakov 
689*7caff0fcSAndrey Gusakov 	/* DP Main Stream Attributes */
690*7caff0fcSAndrey Gusakov 	vid_sync_dly = hsync_len + left_margin + mode->hdisplay;
691*7caff0fcSAndrey Gusakov 	tc_write(DP0_VIDSYNCDELAY,
692*7caff0fcSAndrey Gusakov 		 (0x003e << 16) |	/* thresh_dly */
693*7caff0fcSAndrey Gusakov 		 (vid_sync_dly << 0));
694*7caff0fcSAndrey Gusakov 
695*7caff0fcSAndrey Gusakov 	tc_write(DP0_TOTALVAL, (mode->vtotal << 16) | (mode->htotal));
696*7caff0fcSAndrey Gusakov 
697*7caff0fcSAndrey Gusakov 	tc_write(DP0_STARTVAL,
698*7caff0fcSAndrey Gusakov 		 ((upper_margin + vsync_len) << 16) |
699*7caff0fcSAndrey Gusakov 		 ((left_margin + hsync_len) << 0));
700*7caff0fcSAndrey Gusakov 
701*7caff0fcSAndrey Gusakov 	tc_write(DP0_ACTIVEVAL, (mode->vdisplay << 16) | (mode->hdisplay));
702*7caff0fcSAndrey Gusakov 
703*7caff0fcSAndrey Gusakov 	tc_write(DP0_SYNCVAL, (vsync_len << 16) | (hsync_len << 0));
704*7caff0fcSAndrey Gusakov 
705*7caff0fcSAndrey Gusakov 	tc_write(DPIPXLFMT, VS_POL_ACTIVE_LOW | HS_POL_ACTIVE_LOW |
706*7caff0fcSAndrey Gusakov 		 DE_POL_ACTIVE_HIGH | SUB_CFG_TYPE_CONFIG1 | DPI_BPP_RGB888);
707*7caff0fcSAndrey Gusakov 
708*7caff0fcSAndrey Gusakov 	/*
709*7caff0fcSAndrey Gusakov 	 * Recommended maximum number of symbols transferred in a transfer unit:
710*7caff0fcSAndrey Gusakov 	 * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size,
711*7caff0fcSAndrey Gusakov 	 *              (output active video bandwidth in bytes))
712*7caff0fcSAndrey Gusakov 	 * Must be less than tu_size.
713*7caff0fcSAndrey Gusakov 	 */
714*7caff0fcSAndrey Gusakov 	max_tu_symbol = TU_SIZE_RECOMMENDED - 1;
715*7caff0fcSAndrey Gusakov 	tc_write(DP0_MISC, (max_tu_symbol << 23) | TU_SIZE_RECOMMENDED | BPC_8);
716*7caff0fcSAndrey Gusakov 
717*7caff0fcSAndrey Gusakov 	return 0;
718*7caff0fcSAndrey Gusakov err:
719*7caff0fcSAndrey Gusakov 	return ret;
720*7caff0fcSAndrey Gusakov }
721*7caff0fcSAndrey Gusakov 
722*7caff0fcSAndrey Gusakov static int tc_link_training(struct tc_data *tc, int pattern)
723*7caff0fcSAndrey Gusakov {
724*7caff0fcSAndrey Gusakov 	const char * const *errors;
725*7caff0fcSAndrey Gusakov 	u32 srcctrl = tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS |
726*7caff0fcSAndrey Gusakov 		      DP0_SRCCTRL_AUTOCORRECT;
727*7caff0fcSAndrey Gusakov 	int timeout;
728*7caff0fcSAndrey Gusakov 	int retry;
729*7caff0fcSAndrey Gusakov 	u32 value;
730*7caff0fcSAndrey Gusakov 	int ret;
731*7caff0fcSAndrey Gusakov 
732*7caff0fcSAndrey Gusakov 	if (pattern == DP_TRAINING_PATTERN_1) {
733*7caff0fcSAndrey Gusakov 		srcctrl |= DP0_SRCCTRL_TP1;
734*7caff0fcSAndrey Gusakov 		errors = training_pattern1_errors;
735*7caff0fcSAndrey Gusakov 	} else {
736*7caff0fcSAndrey Gusakov 		srcctrl |= DP0_SRCCTRL_TP2;
737*7caff0fcSAndrey Gusakov 		errors = training_pattern2_errors;
738*7caff0fcSAndrey Gusakov 	}
739*7caff0fcSAndrey Gusakov 
740*7caff0fcSAndrey Gusakov 	/* Set DPCD 0x102 for Training Part 1 or 2 */
741*7caff0fcSAndrey Gusakov 	tc_write(DP0_SNKLTCTRL, DP_LINK_SCRAMBLING_DISABLE | pattern);
742*7caff0fcSAndrey Gusakov 
743*7caff0fcSAndrey Gusakov 	tc_write(DP0_LTLOOPCTRL,
744*7caff0fcSAndrey Gusakov 		 (0x0f << 28) |	/* Defer Iteration Count */
745*7caff0fcSAndrey Gusakov 		 (0x0f << 24) |	/* Loop Iteration Count */
746*7caff0fcSAndrey Gusakov 		 (0x0d << 0));	/* Loop Timer Delay */
747*7caff0fcSAndrey Gusakov 
748*7caff0fcSAndrey Gusakov 	retry = 5;
749*7caff0fcSAndrey Gusakov 	do {
750*7caff0fcSAndrey Gusakov 		/* Set DP0 Training Pattern */
751*7caff0fcSAndrey Gusakov 		tc_write(DP0_SRCCTRL, srcctrl);
752*7caff0fcSAndrey Gusakov 
753*7caff0fcSAndrey Gusakov 		/* Enable DP0 to start Link Training */
754*7caff0fcSAndrey Gusakov 		tc_write(DP0CTL, DP_EN);
755*7caff0fcSAndrey Gusakov 
756*7caff0fcSAndrey Gusakov 		/* wait */
757*7caff0fcSAndrey Gusakov 		timeout = 1000;
758*7caff0fcSAndrey Gusakov 		do {
759*7caff0fcSAndrey Gusakov 			tc_read(DP0_LTSTAT, &value);
760*7caff0fcSAndrey Gusakov 			udelay(1);
761*7caff0fcSAndrey Gusakov 		} while ((!(value & LT_LOOPDONE)) && (--timeout));
762*7caff0fcSAndrey Gusakov 		if (timeout == 0) {
763*7caff0fcSAndrey Gusakov 			dev_err(tc->dev, "Link training timeout!\n");
764*7caff0fcSAndrey Gusakov 		} else {
765*7caff0fcSAndrey Gusakov 			int pattern = (value >> 11) & 0x3;
766*7caff0fcSAndrey Gusakov 			int error = (value >> 8) & 0x7;
767*7caff0fcSAndrey Gusakov 
768*7caff0fcSAndrey Gusakov 			dev_dbg(tc->dev,
769*7caff0fcSAndrey Gusakov 				"Link training phase %d done after %d uS: %s\n",
770*7caff0fcSAndrey Gusakov 				pattern, 1000 - timeout, errors[error]);
771*7caff0fcSAndrey Gusakov 			if (pattern == DP_TRAINING_PATTERN_1 && error == 0)
772*7caff0fcSAndrey Gusakov 				break;
773*7caff0fcSAndrey Gusakov 			if (pattern == DP_TRAINING_PATTERN_2) {
774*7caff0fcSAndrey Gusakov 				value &= LT_CHANNEL1_EQ_BITS |
775*7caff0fcSAndrey Gusakov 					 LT_INTERLANE_ALIGN_DONE |
776*7caff0fcSAndrey Gusakov 					 LT_CHANNEL0_EQ_BITS;
777*7caff0fcSAndrey Gusakov 				/* in case of two lanes */
778*7caff0fcSAndrey Gusakov 				if ((tc->link.base.num_lanes == 2) &&
779*7caff0fcSAndrey Gusakov 				    (value == (LT_CHANNEL1_EQ_BITS |
780*7caff0fcSAndrey Gusakov 					       LT_INTERLANE_ALIGN_DONE |
781*7caff0fcSAndrey Gusakov 					       LT_CHANNEL0_EQ_BITS)))
782*7caff0fcSAndrey Gusakov 					break;
783*7caff0fcSAndrey Gusakov 				/* in case of one line */
784*7caff0fcSAndrey Gusakov 				if ((tc->link.base.num_lanes == 1) &&
785*7caff0fcSAndrey Gusakov 				    (value == (LT_INTERLANE_ALIGN_DONE |
786*7caff0fcSAndrey Gusakov 					       LT_CHANNEL0_EQ_BITS)))
787*7caff0fcSAndrey Gusakov 					break;
788*7caff0fcSAndrey Gusakov 			}
789*7caff0fcSAndrey Gusakov 		}
790*7caff0fcSAndrey Gusakov 		/* restart */
791*7caff0fcSAndrey Gusakov 		tc_write(DP0CTL, 0);
792*7caff0fcSAndrey Gusakov 		usleep_range(10, 20);
793*7caff0fcSAndrey Gusakov 	} while (--retry);
794*7caff0fcSAndrey Gusakov 	if (retry == 0) {
795*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "Failed to finish training phase %d\n",
796*7caff0fcSAndrey Gusakov 			pattern);
797*7caff0fcSAndrey Gusakov 	}
798*7caff0fcSAndrey Gusakov 
799*7caff0fcSAndrey Gusakov 	return 0;
800*7caff0fcSAndrey Gusakov err:
801*7caff0fcSAndrey Gusakov 	return ret;
802*7caff0fcSAndrey Gusakov }
803*7caff0fcSAndrey Gusakov 
804*7caff0fcSAndrey Gusakov static int tc_main_link_setup(struct tc_data *tc)
805*7caff0fcSAndrey Gusakov {
806*7caff0fcSAndrey Gusakov 	struct drm_dp_aux *aux = &tc->aux;
807*7caff0fcSAndrey Gusakov 	struct device *dev = tc->dev;
808*7caff0fcSAndrey Gusakov 	unsigned int rate;
809*7caff0fcSAndrey Gusakov 	u32 dp_phy_ctrl;
810*7caff0fcSAndrey Gusakov 	int timeout;
811*7caff0fcSAndrey Gusakov 	bool aligned;
812*7caff0fcSAndrey Gusakov 	bool ready;
813*7caff0fcSAndrey Gusakov 	u32 value;
814*7caff0fcSAndrey Gusakov 	int ret;
815*7caff0fcSAndrey Gusakov 	u8 tmp[8];
816*7caff0fcSAndrey Gusakov 
817*7caff0fcSAndrey Gusakov 	/* display mode should be set at this point */
818*7caff0fcSAndrey Gusakov 	if (!tc->mode)
819*7caff0fcSAndrey Gusakov 		return -EINVAL;
820*7caff0fcSAndrey Gusakov 
821*7caff0fcSAndrey Gusakov 	/* from excel file - DP0_SrcCtrl */
822*7caff0fcSAndrey Gusakov 	tc_write(DP0_SRCCTRL, DP0_SRCCTRL_SCRMBLDIS | DP0_SRCCTRL_EN810B |
823*7caff0fcSAndrey Gusakov 		 DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_LANES_2 |
824*7caff0fcSAndrey Gusakov 		 DP0_SRCCTRL_BW27 | DP0_SRCCTRL_AUTOCORRECT);
825*7caff0fcSAndrey Gusakov 	/* from excel file - DP1_SrcCtrl */
826*7caff0fcSAndrey Gusakov 	tc_write(0x07a0, 0x00003083);
827*7caff0fcSAndrey Gusakov 
828*7caff0fcSAndrey Gusakov 	rate = clk_get_rate(tc->refclk);
829*7caff0fcSAndrey Gusakov 	switch (rate) {
830*7caff0fcSAndrey Gusakov 	case 38400000:
831*7caff0fcSAndrey Gusakov 		value = REF_FREQ_38M4;
832*7caff0fcSAndrey Gusakov 		break;
833*7caff0fcSAndrey Gusakov 	case 26000000:
834*7caff0fcSAndrey Gusakov 		value = REF_FREQ_26M;
835*7caff0fcSAndrey Gusakov 		break;
836*7caff0fcSAndrey Gusakov 	case 19200000:
837*7caff0fcSAndrey Gusakov 		value = REF_FREQ_19M2;
838*7caff0fcSAndrey Gusakov 		break;
839*7caff0fcSAndrey Gusakov 	case 13000000:
840*7caff0fcSAndrey Gusakov 		value = REF_FREQ_13M;
841*7caff0fcSAndrey Gusakov 		break;
842*7caff0fcSAndrey Gusakov 	default:
843*7caff0fcSAndrey Gusakov 		return -EINVAL;
844*7caff0fcSAndrey Gusakov 	}
845*7caff0fcSAndrey Gusakov 	value |= SYSCLK_SEL_LSCLK | LSCLK_DIV_2;
846*7caff0fcSAndrey Gusakov 	tc_write(SYS_PLLPARAM, value);
847*7caff0fcSAndrey Gusakov 	/* Setup Main Link */
848*7caff0fcSAndrey Gusakov 	dp_phy_ctrl = BGREN | PWR_SW_EN | BIT(2) | PHY_A0_EN |  PHY_M0_EN;
849*7caff0fcSAndrey Gusakov 	tc_write(DP_PHY_CTRL, dp_phy_ctrl);
850*7caff0fcSAndrey Gusakov 	msleep(100);
851*7caff0fcSAndrey Gusakov 
852*7caff0fcSAndrey Gusakov 	/* PLL setup */
853*7caff0fcSAndrey Gusakov 	tc_write(DP0_PLLCTRL, PLLUPDATE | PLLEN);
854*7caff0fcSAndrey Gusakov 	tc_wait_pll_lock(tc);
855*7caff0fcSAndrey Gusakov 
856*7caff0fcSAndrey Gusakov 	tc_write(DP1_PLLCTRL, PLLUPDATE | PLLEN);
857*7caff0fcSAndrey Gusakov 	tc_wait_pll_lock(tc);
858*7caff0fcSAndrey Gusakov 
859*7caff0fcSAndrey Gusakov 	/* PXL PLL setup */
860*7caff0fcSAndrey Gusakov 	if (tc_test_pattern) {
861*7caff0fcSAndrey Gusakov 		ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
862*7caff0fcSAndrey Gusakov 				    1000 * tc->mode->clock);
863*7caff0fcSAndrey Gusakov 		if (ret)
864*7caff0fcSAndrey Gusakov 			goto err;
865*7caff0fcSAndrey Gusakov 	}
866*7caff0fcSAndrey Gusakov 
867*7caff0fcSAndrey Gusakov 	/* Reset/Enable Main Links */
868*7caff0fcSAndrey Gusakov 	dp_phy_ctrl |= DP_PHY_RST | PHY_M1_RST | PHY_M0_RST;
869*7caff0fcSAndrey Gusakov 	tc_write(DP_PHY_CTRL, dp_phy_ctrl);
870*7caff0fcSAndrey Gusakov 	usleep_range(100, 200);
871*7caff0fcSAndrey Gusakov 	dp_phy_ctrl &= ~(DP_PHY_RST | PHY_M1_RST | PHY_M0_RST);
872*7caff0fcSAndrey Gusakov 	tc_write(DP_PHY_CTRL, dp_phy_ctrl);
873*7caff0fcSAndrey Gusakov 
874*7caff0fcSAndrey Gusakov 	timeout = 1000;
875*7caff0fcSAndrey Gusakov 	do {
876*7caff0fcSAndrey Gusakov 		tc_read(DP_PHY_CTRL, &value);
877*7caff0fcSAndrey Gusakov 		udelay(1);
878*7caff0fcSAndrey Gusakov 	} while ((!(value & PHY_RDY)) && (--timeout));
879*7caff0fcSAndrey Gusakov 
880*7caff0fcSAndrey Gusakov 	if (timeout == 0) {
881*7caff0fcSAndrey Gusakov 		dev_err(dev, "timeout waiting for phy become ready");
882*7caff0fcSAndrey Gusakov 		return -ETIMEDOUT;
883*7caff0fcSAndrey Gusakov 	}
884*7caff0fcSAndrey Gusakov 
885*7caff0fcSAndrey Gusakov 	/* Set misc: 8 bits per color */
886*7caff0fcSAndrey Gusakov 	ret = regmap_update_bits(tc->regmap, DP0_MISC, BPC_8, BPC_8);
887*7caff0fcSAndrey Gusakov 	if (ret)
888*7caff0fcSAndrey Gusakov 		goto err;
889*7caff0fcSAndrey Gusakov 
890*7caff0fcSAndrey Gusakov 	/*
891*7caff0fcSAndrey Gusakov 	 * ASSR mode
892*7caff0fcSAndrey Gusakov 	 * on TC358767 side ASSR configured through strap pin
893*7caff0fcSAndrey Gusakov 	 * seems there is no way to change this setting from SW
894*7caff0fcSAndrey Gusakov 	 *
895*7caff0fcSAndrey Gusakov 	 * check is tc configured for same mode
896*7caff0fcSAndrey Gusakov 	 */
897*7caff0fcSAndrey Gusakov 	if (tc->assr != tc->link.assr) {
898*7caff0fcSAndrey Gusakov 		dev_dbg(dev, "Trying to set display to ASSR: %d\n",
899*7caff0fcSAndrey Gusakov 			tc->assr);
900*7caff0fcSAndrey Gusakov 		/* try to set ASSR on display side */
901*7caff0fcSAndrey Gusakov 		tmp[0] = tc->assr;
902*7caff0fcSAndrey Gusakov 		ret = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, tmp[0]);
903*7caff0fcSAndrey Gusakov 		if (ret < 0)
904*7caff0fcSAndrey Gusakov 			goto err_dpcd_read;
905*7caff0fcSAndrey Gusakov 		/* read back */
906*7caff0fcSAndrey Gusakov 		ret = drm_dp_dpcd_readb(aux, DP_EDP_CONFIGURATION_SET, tmp);
907*7caff0fcSAndrey Gusakov 		if (ret < 0)
908*7caff0fcSAndrey Gusakov 			goto err_dpcd_read;
909*7caff0fcSAndrey Gusakov 
910*7caff0fcSAndrey Gusakov 		if (tmp[0] != tc->assr) {
911*7caff0fcSAndrey Gusakov 			dev_warn(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n",
912*7caff0fcSAndrey Gusakov 				 tc->assr);
913*7caff0fcSAndrey Gusakov 			/* trying with disabled scrambler */
914*7caff0fcSAndrey Gusakov 			tc->link.scrambler_dis = 1;
915*7caff0fcSAndrey Gusakov 		}
916*7caff0fcSAndrey Gusakov 	}
917*7caff0fcSAndrey Gusakov 
918*7caff0fcSAndrey Gusakov 	/* Setup Link & DPRx Config for Training */
919*7caff0fcSAndrey Gusakov 	ret = drm_dp_link_configure(aux, &tc->link.base);
920*7caff0fcSAndrey Gusakov 	if (ret < 0)
921*7caff0fcSAndrey Gusakov 		goto err_dpcd_write;
922*7caff0fcSAndrey Gusakov 
923*7caff0fcSAndrey Gusakov 	/* DOWNSPREAD_CTRL */
924*7caff0fcSAndrey Gusakov 	tmp[0] = tc->link.spread ? DP_SPREAD_AMP_0_5 : 0x00;
925*7caff0fcSAndrey Gusakov 	/* MAIN_LINK_CHANNEL_CODING_SET */
926*7caff0fcSAndrey Gusakov 	tmp[1] =  tc->link.coding8b10b ? DP_SET_ANSI_8B10B : 0x00;
927*7caff0fcSAndrey Gusakov 	ret = drm_dp_dpcd_write(aux, DP_DOWNSPREAD_CTRL, tmp, 2);
928*7caff0fcSAndrey Gusakov 	if (ret < 0)
929*7caff0fcSAndrey Gusakov 		goto err_dpcd_write;
930*7caff0fcSAndrey Gusakov 
931*7caff0fcSAndrey Gusakov 	ret = tc_link_training(tc, DP_TRAINING_PATTERN_1);
932*7caff0fcSAndrey Gusakov 	if (ret)
933*7caff0fcSAndrey Gusakov 		goto err;
934*7caff0fcSAndrey Gusakov 
935*7caff0fcSAndrey Gusakov 	ret = tc_link_training(tc, DP_TRAINING_PATTERN_2);
936*7caff0fcSAndrey Gusakov 	if (ret)
937*7caff0fcSAndrey Gusakov 		goto err;
938*7caff0fcSAndrey Gusakov 
939*7caff0fcSAndrey Gusakov 	/* Clear DPCD 0x102 */
940*7caff0fcSAndrey Gusakov 	/* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */
941*7caff0fcSAndrey Gusakov 	tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00;
942*7caff0fcSAndrey Gusakov 	ret = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, tmp[0]);
943*7caff0fcSAndrey Gusakov 	if (ret < 0)
944*7caff0fcSAndrey Gusakov 		goto err_dpcd_write;
945*7caff0fcSAndrey Gusakov 
946*7caff0fcSAndrey Gusakov 	/* Clear Training Pattern, set AutoCorrect Mode = 1 */
947*7caff0fcSAndrey Gusakov 	tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_AUTOCORRECT);
948*7caff0fcSAndrey Gusakov 
949*7caff0fcSAndrey Gusakov 	/* Wait */
950*7caff0fcSAndrey Gusakov 	timeout = 100;
951*7caff0fcSAndrey Gusakov 	do {
952*7caff0fcSAndrey Gusakov 		udelay(1);
953*7caff0fcSAndrey Gusakov 		/* Read DPCD 0x202-0x207 */
954*7caff0fcSAndrey Gusakov 		ret = drm_dp_dpcd_read_link_status(aux, tmp + 2);
955*7caff0fcSAndrey Gusakov 		if (ret < 0)
956*7caff0fcSAndrey Gusakov 			goto err_dpcd_read;
957*7caff0fcSAndrey Gusakov 		ready = (tmp[2] == ((DP_CHANNEL_EQ_BITS << 4) | /* Lane1 */
958*7caff0fcSAndrey Gusakov 				     DP_CHANNEL_EQ_BITS));      /* Lane0 */
959*7caff0fcSAndrey Gusakov 		aligned = tmp[4] & DP_INTERLANE_ALIGN_DONE;
960*7caff0fcSAndrey Gusakov 	} while ((--timeout) && !(ready && aligned));
961*7caff0fcSAndrey Gusakov 
962*7caff0fcSAndrey Gusakov 	if (timeout == 0) {
963*7caff0fcSAndrey Gusakov 		/* Read DPCD 0x200-0x201 */
964*7caff0fcSAndrey Gusakov 		ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT, tmp, 2);
965*7caff0fcSAndrey Gusakov 		if (ret < 0)
966*7caff0fcSAndrey Gusakov 			goto err_dpcd_read;
967*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0200 SINK_COUNT: 0x%02x\n", tmp[0]);
968*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0201 DEVICE_SERVICE_IRQ_VECTOR: 0x%02x\n",
969*7caff0fcSAndrey Gusakov 			 tmp[1]);
970*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0202 LANE0_1_STATUS: 0x%02x\n", tmp[2]);
971*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n",
972*7caff0fcSAndrey Gusakov 			 tmp[4]);
973*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0205 SINK_STATUS: 0x%02x\n", tmp[5]);
974*7caff0fcSAndrey Gusakov 		dev_info(dev, "0x0206 ADJUST_REQUEST_LANE0_1: 0x%02x\n",
975*7caff0fcSAndrey Gusakov 			 tmp[6]);
976*7caff0fcSAndrey Gusakov 
977*7caff0fcSAndrey Gusakov 		if (!ready)
978*7caff0fcSAndrey Gusakov 			dev_err(dev, "Lane0/1 not ready\n");
979*7caff0fcSAndrey Gusakov 		if (!aligned)
980*7caff0fcSAndrey Gusakov 			dev_err(dev, "Lane0/1 not aligned\n");
981*7caff0fcSAndrey Gusakov 		return -EAGAIN;
982*7caff0fcSAndrey Gusakov 	}
983*7caff0fcSAndrey Gusakov 
984*7caff0fcSAndrey Gusakov 	ret = tc_set_video_mode(tc, tc->mode);
985*7caff0fcSAndrey Gusakov 	if (ret)
986*7caff0fcSAndrey Gusakov 		goto err;
987*7caff0fcSAndrey Gusakov 
988*7caff0fcSAndrey Gusakov 	/* Set M/N */
989*7caff0fcSAndrey Gusakov 	ret = tc_stream_clock_calc(tc);
990*7caff0fcSAndrey Gusakov 	if (ret)
991*7caff0fcSAndrey Gusakov 		goto err;
992*7caff0fcSAndrey Gusakov 
993*7caff0fcSAndrey Gusakov 	return 0;
994*7caff0fcSAndrey Gusakov err_dpcd_read:
995*7caff0fcSAndrey Gusakov 	dev_err(tc->dev, "Failed to read DPCD: %d\n", ret);
996*7caff0fcSAndrey Gusakov 	return ret;
997*7caff0fcSAndrey Gusakov err_dpcd_write:
998*7caff0fcSAndrey Gusakov 	dev_err(tc->dev, "Failed to write DPCD: %d\n", ret);
999*7caff0fcSAndrey Gusakov err:
1000*7caff0fcSAndrey Gusakov 	return ret;
1001*7caff0fcSAndrey Gusakov }
1002*7caff0fcSAndrey Gusakov 
1003*7caff0fcSAndrey Gusakov static int tc_main_link_stream(struct tc_data *tc, int state)
1004*7caff0fcSAndrey Gusakov {
1005*7caff0fcSAndrey Gusakov 	int ret;
1006*7caff0fcSAndrey Gusakov 	u32 value;
1007*7caff0fcSAndrey Gusakov 
1008*7caff0fcSAndrey Gusakov 	dev_dbg(tc->dev, "stream: %d\n", state);
1009*7caff0fcSAndrey Gusakov 
1010*7caff0fcSAndrey Gusakov 	if (state) {
1011*7caff0fcSAndrey Gusakov 		value = VID_MN_GEN | DP_EN;
1012*7caff0fcSAndrey Gusakov 		if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
1013*7caff0fcSAndrey Gusakov 			value |= EF_EN;
1014*7caff0fcSAndrey Gusakov 		tc_write(DP0CTL, value);
1015*7caff0fcSAndrey Gusakov 		/*
1016*7caff0fcSAndrey Gusakov 		 * VID_EN assertion should be delayed by at least N * LSCLK
1017*7caff0fcSAndrey Gusakov 		 * cycles from the time VID_MN_GEN is enabled in order to
1018*7caff0fcSAndrey Gusakov 		 * generate stable values for VID_M. LSCLK is 270 MHz or
1019*7caff0fcSAndrey Gusakov 		 * 162 MHz, VID_N is set to 32768 in  tc_stream_clock_calc(),
1020*7caff0fcSAndrey Gusakov 		 * so a delay of at least 203 us should suffice.
1021*7caff0fcSAndrey Gusakov 		 */
1022*7caff0fcSAndrey Gusakov 		usleep_range(500, 1000);
1023*7caff0fcSAndrey Gusakov 		value |= VID_EN;
1024*7caff0fcSAndrey Gusakov 		tc_write(DP0CTL, value);
1025*7caff0fcSAndrey Gusakov 		/* Set input interface */
1026*7caff0fcSAndrey Gusakov 		value = DP0_AUDSRC_NO_INPUT;
1027*7caff0fcSAndrey Gusakov 		if (tc_test_pattern)
1028*7caff0fcSAndrey Gusakov 			value |= DP0_VIDSRC_COLOR_BAR;
1029*7caff0fcSAndrey Gusakov 		else
1030*7caff0fcSAndrey Gusakov 			value |= DP0_VIDSRC_DPI_RX;
1031*7caff0fcSAndrey Gusakov 		tc_write(SYSCTRL, value);
1032*7caff0fcSAndrey Gusakov 	} else {
1033*7caff0fcSAndrey Gusakov 		tc_write(DP0CTL, 0);
1034*7caff0fcSAndrey Gusakov 	}
1035*7caff0fcSAndrey Gusakov 
1036*7caff0fcSAndrey Gusakov 	return 0;
1037*7caff0fcSAndrey Gusakov err:
1038*7caff0fcSAndrey Gusakov 	return ret;
1039*7caff0fcSAndrey Gusakov }
1040*7caff0fcSAndrey Gusakov 
1041*7caff0fcSAndrey Gusakov static enum drm_connector_status
1042*7caff0fcSAndrey Gusakov tc_connector_detect(struct drm_connector *connector, bool force)
1043*7caff0fcSAndrey Gusakov {
1044*7caff0fcSAndrey Gusakov 	return connector_status_connected;
1045*7caff0fcSAndrey Gusakov }
1046*7caff0fcSAndrey Gusakov 
1047*7caff0fcSAndrey Gusakov static void tc_bridge_pre_enable(struct drm_bridge *bridge)
1048*7caff0fcSAndrey Gusakov {
1049*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1050*7caff0fcSAndrey Gusakov 
1051*7caff0fcSAndrey Gusakov 	drm_panel_prepare(tc->panel);
1052*7caff0fcSAndrey Gusakov }
1053*7caff0fcSAndrey Gusakov 
1054*7caff0fcSAndrey Gusakov static void tc_bridge_enable(struct drm_bridge *bridge)
1055*7caff0fcSAndrey Gusakov {
1056*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1057*7caff0fcSAndrey Gusakov 	int ret;
1058*7caff0fcSAndrey Gusakov 
1059*7caff0fcSAndrey Gusakov 	ret = tc_main_link_setup(tc);
1060*7caff0fcSAndrey Gusakov 	if (ret < 0) {
1061*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "main link setup error: %d\n", ret);
1062*7caff0fcSAndrey Gusakov 		return;
1063*7caff0fcSAndrey Gusakov 	}
1064*7caff0fcSAndrey Gusakov 
1065*7caff0fcSAndrey Gusakov 	ret = tc_main_link_stream(tc, 1);
1066*7caff0fcSAndrey Gusakov 	if (ret < 0) {
1067*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "main link stream start error: %d\n", ret);
1068*7caff0fcSAndrey Gusakov 		return;
1069*7caff0fcSAndrey Gusakov 	}
1070*7caff0fcSAndrey Gusakov 
1071*7caff0fcSAndrey Gusakov 	drm_panel_enable(tc->panel);
1072*7caff0fcSAndrey Gusakov }
1073*7caff0fcSAndrey Gusakov 
1074*7caff0fcSAndrey Gusakov static void tc_bridge_disable(struct drm_bridge *bridge)
1075*7caff0fcSAndrey Gusakov {
1076*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1077*7caff0fcSAndrey Gusakov 	int ret;
1078*7caff0fcSAndrey Gusakov 
1079*7caff0fcSAndrey Gusakov 	drm_panel_disable(tc->panel);
1080*7caff0fcSAndrey Gusakov 
1081*7caff0fcSAndrey Gusakov 	ret = tc_main_link_stream(tc, 0);
1082*7caff0fcSAndrey Gusakov 	if (ret < 0)
1083*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "main link stream stop error: %d\n", ret);
1084*7caff0fcSAndrey Gusakov }
1085*7caff0fcSAndrey Gusakov 
1086*7caff0fcSAndrey Gusakov static void tc_bridge_post_disable(struct drm_bridge *bridge)
1087*7caff0fcSAndrey Gusakov {
1088*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1089*7caff0fcSAndrey Gusakov 
1090*7caff0fcSAndrey Gusakov 	drm_panel_unprepare(tc->panel);
1091*7caff0fcSAndrey Gusakov }
1092*7caff0fcSAndrey Gusakov 
1093*7caff0fcSAndrey Gusakov static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
1094*7caff0fcSAndrey Gusakov 				 const struct drm_display_mode *mode,
1095*7caff0fcSAndrey Gusakov 				 struct drm_display_mode *adj)
1096*7caff0fcSAndrey Gusakov {
1097*7caff0fcSAndrey Gusakov 	/* Fixup sync polarities, both hsync and vsync are active low */
1098*7caff0fcSAndrey Gusakov 	adj->flags = mode->flags;
1099*7caff0fcSAndrey Gusakov 	adj->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
1100*7caff0fcSAndrey Gusakov 	adj->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
1101*7caff0fcSAndrey Gusakov 
1102*7caff0fcSAndrey Gusakov 	return true;
1103*7caff0fcSAndrey Gusakov }
1104*7caff0fcSAndrey Gusakov 
1105*7caff0fcSAndrey Gusakov static int tc_connector_mode_valid(struct drm_connector *connector,
1106*7caff0fcSAndrey Gusakov 				   struct drm_display_mode *mode)
1107*7caff0fcSAndrey Gusakov {
1108*7caff0fcSAndrey Gusakov 	/* Accept any mode */
1109*7caff0fcSAndrey Gusakov 	return MODE_OK;
1110*7caff0fcSAndrey Gusakov }
1111*7caff0fcSAndrey Gusakov 
1112*7caff0fcSAndrey Gusakov static void tc_bridge_mode_set(struct drm_bridge *bridge,
1113*7caff0fcSAndrey Gusakov 			       struct drm_display_mode *mode,
1114*7caff0fcSAndrey Gusakov 			       struct drm_display_mode *adj)
1115*7caff0fcSAndrey Gusakov {
1116*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1117*7caff0fcSAndrey Gusakov 
1118*7caff0fcSAndrey Gusakov 	tc->mode = mode;
1119*7caff0fcSAndrey Gusakov }
1120*7caff0fcSAndrey Gusakov 
1121*7caff0fcSAndrey Gusakov static int tc_connector_get_modes(struct drm_connector *connector)
1122*7caff0fcSAndrey Gusakov {
1123*7caff0fcSAndrey Gusakov 	struct tc_data *tc = connector_to_tc(connector);
1124*7caff0fcSAndrey Gusakov 	struct edid *edid;
1125*7caff0fcSAndrey Gusakov 	unsigned int count;
1126*7caff0fcSAndrey Gusakov 
1127*7caff0fcSAndrey Gusakov 	if (tc->panel && tc->panel->funcs && tc->panel->funcs->get_modes) {
1128*7caff0fcSAndrey Gusakov 		count = tc->panel->funcs->get_modes(tc->panel);
1129*7caff0fcSAndrey Gusakov 		if (count > 0)
1130*7caff0fcSAndrey Gusakov 			return count;
1131*7caff0fcSAndrey Gusakov 	}
1132*7caff0fcSAndrey Gusakov 
1133*7caff0fcSAndrey Gusakov 	edid = drm_get_edid(connector, &tc->aux.ddc);
1134*7caff0fcSAndrey Gusakov 
1135*7caff0fcSAndrey Gusakov 	kfree(tc->edid);
1136*7caff0fcSAndrey Gusakov 	tc->edid = edid;
1137*7caff0fcSAndrey Gusakov 	if (!edid)
1138*7caff0fcSAndrey Gusakov 		return 0;
1139*7caff0fcSAndrey Gusakov 
1140*7caff0fcSAndrey Gusakov 	drm_mode_connector_update_edid_property(connector, edid);
1141*7caff0fcSAndrey Gusakov 	count = drm_add_edid_modes(connector, edid);
1142*7caff0fcSAndrey Gusakov 
1143*7caff0fcSAndrey Gusakov 	return count;
1144*7caff0fcSAndrey Gusakov }
1145*7caff0fcSAndrey Gusakov 
1146*7caff0fcSAndrey Gusakov static void tc_connector_set_polling(struct tc_data *tc,
1147*7caff0fcSAndrey Gusakov 				     struct drm_connector *connector)
1148*7caff0fcSAndrey Gusakov {
1149*7caff0fcSAndrey Gusakov 	/* TODO: add support for HPD */
1150*7caff0fcSAndrey Gusakov 	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
1151*7caff0fcSAndrey Gusakov 			    DRM_CONNECTOR_POLL_DISCONNECT;
1152*7caff0fcSAndrey Gusakov }
1153*7caff0fcSAndrey Gusakov 
1154*7caff0fcSAndrey Gusakov static struct drm_encoder *
1155*7caff0fcSAndrey Gusakov tc_connector_best_encoder(struct drm_connector *connector)
1156*7caff0fcSAndrey Gusakov {
1157*7caff0fcSAndrey Gusakov 	struct tc_data *tc = connector_to_tc(connector);
1158*7caff0fcSAndrey Gusakov 
1159*7caff0fcSAndrey Gusakov 	return tc->bridge.encoder;
1160*7caff0fcSAndrey Gusakov }
1161*7caff0fcSAndrey Gusakov 
1162*7caff0fcSAndrey Gusakov static const struct drm_connector_helper_funcs tc_connector_helper_funcs = {
1163*7caff0fcSAndrey Gusakov 	.get_modes = tc_connector_get_modes,
1164*7caff0fcSAndrey Gusakov 	.mode_valid = tc_connector_mode_valid,
1165*7caff0fcSAndrey Gusakov 	.best_encoder = tc_connector_best_encoder,
1166*7caff0fcSAndrey Gusakov };
1167*7caff0fcSAndrey Gusakov 
1168*7caff0fcSAndrey Gusakov static void tc_connector_destroy(struct drm_connector *connector)
1169*7caff0fcSAndrey Gusakov {
1170*7caff0fcSAndrey Gusakov 	drm_connector_unregister(connector);
1171*7caff0fcSAndrey Gusakov 	drm_connector_cleanup(connector);
1172*7caff0fcSAndrey Gusakov }
1173*7caff0fcSAndrey Gusakov 
1174*7caff0fcSAndrey Gusakov static const struct drm_connector_funcs tc_connector_funcs = {
1175*7caff0fcSAndrey Gusakov 	.dpms = drm_atomic_helper_connector_dpms,
1176*7caff0fcSAndrey Gusakov 	.fill_modes = drm_helper_probe_single_connector_modes,
1177*7caff0fcSAndrey Gusakov 	.detect = tc_connector_detect,
1178*7caff0fcSAndrey Gusakov 	.destroy = tc_connector_destroy,
1179*7caff0fcSAndrey Gusakov 	.reset = drm_atomic_helper_connector_reset,
1180*7caff0fcSAndrey Gusakov 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
1181*7caff0fcSAndrey Gusakov 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
1182*7caff0fcSAndrey Gusakov };
1183*7caff0fcSAndrey Gusakov 
1184*7caff0fcSAndrey Gusakov static int tc_bridge_attach(struct drm_bridge *bridge)
1185*7caff0fcSAndrey Gusakov {
1186*7caff0fcSAndrey Gusakov 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
1187*7caff0fcSAndrey Gusakov 	struct tc_data *tc = bridge_to_tc(bridge);
1188*7caff0fcSAndrey Gusakov 	struct drm_device *drm = bridge->dev;
1189*7caff0fcSAndrey Gusakov 	int ret;
1190*7caff0fcSAndrey Gusakov 
1191*7caff0fcSAndrey Gusakov 	/* Create eDP connector */
1192*7caff0fcSAndrey Gusakov 	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
1193*7caff0fcSAndrey Gusakov 	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
1194*7caff0fcSAndrey Gusakov 				 DRM_MODE_CONNECTOR_eDP);
1195*7caff0fcSAndrey Gusakov 	if (ret)
1196*7caff0fcSAndrey Gusakov 		return ret;
1197*7caff0fcSAndrey Gusakov 
1198*7caff0fcSAndrey Gusakov 	if (tc->panel)
1199*7caff0fcSAndrey Gusakov 		drm_panel_attach(tc->panel, &tc->connector);
1200*7caff0fcSAndrey Gusakov 
1201*7caff0fcSAndrey Gusakov 	drm_display_info_set_bus_formats(&tc->connector.display_info,
1202*7caff0fcSAndrey Gusakov 					 &bus_format, 1);
1203*7caff0fcSAndrey Gusakov 	drm_mode_connector_attach_encoder(&tc->connector, tc->bridge.encoder);
1204*7caff0fcSAndrey Gusakov 
1205*7caff0fcSAndrey Gusakov 	return 0;
1206*7caff0fcSAndrey Gusakov }
1207*7caff0fcSAndrey Gusakov 
1208*7caff0fcSAndrey Gusakov static const struct drm_bridge_funcs tc_bridge_funcs = {
1209*7caff0fcSAndrey Gusakov 	.attach = tc_bridge_attach,
1210*7caff0fcSAndrey Gusakov 	.mode_set = tc_bridge_mode_set,
1211*7caff0fcSAndrey Gusakov 	.pre_enable = tc_bridge_pre_enable,
1212*7caff0fcSAndrey Gusakov 	.enable = tc_bridge_enable,
1213*7caff0fcSAndrey Gusakov 	.disable = tc_bridge_disable,
1214*7caff0fcSAndrey Gusakov 	.post_disable = tc_bridge_post_disable,
1215*7caff0fcSAndrey Gusakov 	.mode_fixup = tc_bridge_mode_fixup,
1216*7caff0fcSAndrey Gusakov };
1217*7caff0fcSAndrey Gusakov 
1218*7caff0fcSAndrey Gusakov static bool tc_readable_reg(struct device *dev, unsigned int reg)
1219*7caff0fcSAndrey Gusakov {
1220*7caff0fcSAndrey Gusakov 	return reg != SYSCTRL;
1221*7caff0fcSAndrey Gusakov }
1222*7caff0fcSAndrey Gusakov 
1223*7caff0fcSAndrey Gusakov static const struct regmap_range tc_volatile_ranges[] = {
1224*7caff0fcSAndrey Gusakov 	regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS),
1225*7caff0fcSAndrey Gusakov 	regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ),
1226*7caff0fcSAndrey Gusakov 	regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL),
1227*7caff0fcSAndrey Gusakov 	regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL),
1228*7caff0fcSAndrey Gusakov 	regmap_reg_range(VFUEN0, VFUEN0),
1229*7caff0fcSAndrey Gusakov };
1230*7caff0fcSAndrey Gusakov 
1231*7caff0fcSAndrey Gusakov static const struct regmap_access_table tc_volatile_table = {
1232*7caff0fcSAndrey Gusakov 	.yes_ranges = tc_volatile_ranges,
1233*7caff0fcSAndrey Gusakov 	.n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges),
1234*7caff0fcSAndrey Gusakov };
1235*7caff0fcSAndrey Gusakov 
1236*7caff0fcSAndrey Gusakov static bool tc_writeable_reg(struct device *dev, unsigned int reg)
1237*7caff0fcSAndrey Gusakov {
1238*7caff0fcSAndrey Gusakov 	return (reg != TC_IDREG) &&
1239*7caff0fcSAndrey Gusakov 	       (reg != DP0_LTSTAT) &&
1240*7caff0fcSAndrey Gusakov 	       (reg != DP0_SNKLTCHGREQ);
1241*7caff0fcSAndrey Gusakov }
1242*7caff0fcSAndrey Gusakov 
1243*7caff0fcSAndrey Gusakov static const struct regmap_config tc_regmap_config = {
1244*7caff0fcSAndrey Gusakov 	.name = "tc358767",
1245*7caff0fcSAndrey Gusakov 	.reg_bits = 16,
1246*7caff0fcSAndrey Gusakov 	.val_bits = 32,
1247*7caff0fcSAndrey Gusakov 	.reg_stride = 4,
1248*7caff0fcSAndrey Gusakov 	.max_register = PLL_DBG,
1249*7caff0fcSAndrey Gusakov 	.cache_type = REGCACHE_RBTREE,
1250*7caff0fcSAndrey Gusakov 	.readable_reg = tc_readable_reg,
1251*7caff0fcSAndrey Gusakov 	.volatile_table = &tc_volatile_table,
1252*7caff0fcSAndrey Gusakov 	.writeable_reg = tc_writeable_reg,
1253*7caff0fcSAndrey Gusakov 	.reg_format_endian = REGMAP_ENDIAN_BIG,
1254*7caff0fcSAndrey Gusakov 	.val_format_endian = REGMAP_ENDIAN_LITTLE,
1255*7caff0fcSAndrey Gusakov };
1256*7caff0fcSAndrey Gusakov 
1257*7caff0fcSAndrey Gusakov static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
1258*7caff0fcSAndrey Gusakov {
1259*7caff0fcSAndrey Gusakov 	struct device *dev = &client->dev;
1260*7caff0fcSAndrey Gusakov 	struct device_node *ep;
1261*7caff0fcSAndrey Gusakov 	struct tc_data *tc;
1262*7caff0fcSAndrey Gusakov 	int ret;
1263*7caff0fcSAndrey Gusakov 
1264*7caff0fcSAndrey Gusakov 	tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL);
1265*7caff0fcSAndrey Gusakov 	if (!tc)
1266*7caff0fcSAndrey Gusakov 		return -ENOMEM;
1267*7caff0fcSAndrey Gusakov 
1268*7caff0fcSAndrey Gusakov 	tc->dev = dev;
1269*7caff0fcSAndrey Gusakov 
1270*7caff0fcSAndrey Gusakov 	/* port@2 is the output port */
1271*7caff0fcSAndrey Gusakov 	ep = of_graph_get_endpoint_by_regs(dev->of_node, 2, -1);
1272*7caff0fcSAndrey Gusakov 	if (ep) {
1273*7caff0fcSAndrey Gusakov 		struct device_node *remote;
1274*7caff0fcSAndrey Gusakov 
1275*7caff0fcSAndrey Gusakov 		remote = of_graph_get_remote_port_parent(ep);
1276*7caff0fcSAndrey Gusakov 		if (!remote) {
1277*7caff0fcSAndrey Gusakov 			dev_warn(dev, "endpoint %s not connected\n",
1278*7caff0fcSAndrey Gusakov 				 ep->full_name);
1279*7caff0fcSAndrey Gusakov 			of_node_put(ep);
1280*7caff0fcSAndrey Gusakov 			return -ENODEV;
1281*7caff0fcSAndrey Gusakov 		}
1282*7caff0fcSAndrey Gusakov 		of_node_put(ep);
1283*7caff0fcSAndrey Gusakov 		tc->panel = of_drm_find_panel(remote);
1284*7caff0fcSAndrey Gusakov 		if (tc->panel) {
1285*7caff0fcSAndrey Gusakov 			dev_dbg(dev, "found panel %s\n", remote->full_name);
1286*7caff0fcSAndrey Gusakov 		} else {
1287*7caff0fcSAndrey Gusakov 			dev_dbg(dev, "waiting for panel %s\n",
1288*7caff0fcSAndrey Gusakov 				remote->full_name);
1289*7caff0fcSAndrey Gusakov 			of_node_put(remote);
1290*7caff0fcSAndrey Gusakov 			return -EPROBE_DEFER;
1291*7caff0fcSAndrey Gusakov 		}
1292*7caff0fcSAndrey Gusakov 		of_node_put(remote);
1293*7caff0fcSAndrey Gusakov 	}
1294*7caff0fcSAndrey Gusakov 
1295*7caff0fcSAndrey Gusakov 	/* Shut down GPIO is optional */
1296*7caff0fcSAndrey Gusakov 	tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
1297*7caff0fcSAndrey Gusakov 	if (IS_ERR(tc->sd_gpio))
1298*7caff0fcSAndrey Gusakov 		return PTR_ERR(tc->sd_gpio);
1299*7caff0fcSAndrey Gusakov 
1300*7caff0fcSAndrey Gusakov 	if (tc->sd_gpio) {
1301*7caff0fcSAndrey Gusakov 		gpiod_set_value_cansleep(tc->sd_gpio, 0);
1302*7caff0fcSAndrey Gusakov 		usleep_range(5000, 10000);
1303*7caff0fcSAndrey Gusakov 	}
1304*7caff0fcSAndrey Gusakov 
1305*7caff0fcSAndrey Gusakov 	/* Reset GPIO is optional */
1306*7caff0fcSAndrey Gusakov 	tc->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
1307*7caff0fcSAndrey Gusakov 	if (IS_ERR(tc->reset_gpio))
1308*7caff0fcSAndrey Gusakov 		return PTR_ERR(tc->reset_gpio);
1309*7caff0fcSAndrey Gusakov 
1310*7caff0fcSAndrey Gusakov 	if (tc->reset_gpio) {
1311*7caff0fcSAndrey Gusakov 		gpiod_set_value_cansleep(tc->reset_gpio, 1);
1312*7caff0fcSAndrey Gusakov 		usleep_range(5000, 10000);
1313*7caff0fcSAndrey Gusakov 	}
1314*7caff0fcSAndrey Gusakov 
1315*7caff0fcSAndrey Gusakov 	tc->refclk = devm_clk_get(dev, "ref");
1316*7caff0fcSAndrey Gusakov 	if (IS_ERR(tc->refclk)) {
1317*7caff0fcSAndrey Gusakov 		ret = PTR_ERR(tc->refclk);
1318*7caff0fcSAndrey Gusakov 		dev_err(dev, "Failed to get refclk: %d\n", ret);
1319*7caff0fcSAndrey Gusakov 		return ret;
1320*7caff0fcSAndrey Gusakov 	}
1321*7caff0fcSAndrey Gusakov 
1322*7caff0fcSAndrey Gusakov 	tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config);
1323*7caff0fcSAndrey Gusakov 	if (IS_ERR(tc->regmap)) {
1324*7caff0fcSAndrey Gusakov 		ret = PTR_ERR(tc->regmap);
1325*7caff0fcSAndrey Gusakov 		dev_err(dev, "Failed to initialize regmap: %d\n", ret);
1326*7caff0fcSAndrey Gusakov 		return ret;
1327*7caff0fcSAndrey Gusakov 	}
1328*7caff0fcSAndrey Gusakov 
1329*7caff0fcSAndrey Gusakov 	ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev);
1330*7caff0fcSAndrey Gusakov 	if (ret) {
1331*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "can not read device ID: %d\n", ret);
1332*7caff0fcSAndrey Gusakov 		return ret;
1333*7caff0fcSAndrey Gusakov 	}
1334*7caff0fcSAndrey Gusakov 
1335*7caff0fcSAndrey Gusakov 	if ((tc->rev != 0x6601) && (tc->rev != 0x6603)) {
1336*7caff0fcSAndrey Gusakov 		dev_err(tc->dev, "invalid device ID: 0x%08x\n", tc->rev);
1337*7caff0fcSAndrey Gusakov 		return -EINVAL;
1338*7caff0fcSAndrey Gusakov 	}
1339*7caff0fcSAndrey Gusakov 
1340*7caff0fcSAndrey Gusakov 	tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */
1341*7caff0fcSAndrey Gusakov 
1342*7caff0fcSAndrey Gusakov 	ret = tc_aux_link_setup(tc);
1343*7caff0fcSAndrey Gusakov 	if (ret)
1344*7caff0fcSAndrey Gusakov 		return ret;
1345*7caff0fcSAndrey Gusakov 
1346*7caff0fcSAndrey Gusakov 	/* Register DP AUX channel */
1347*7caff0fcSAndrey Gusakov 	tc->aux.name = "TC358767 AUX i2c adapter";
1348*7caff0fcSAndrey Gusakov 	tc->aux.dev = tc->dev;
1349*7caff0fcSAndrey Gusakov 	tc->aux.transfer = tc_aux_transfer;
1350*7caff0fcSAndrey Gusakov 	ret = drm_dp_aux_register(&tc->aux);
1351*7caff0fcSAndrey Gusakov 	if (ret)
1352*7caff0fcSAndrey Gusakov 		return ret;
1353*7caff0fcSAndrey Gusakov 
1354*7caff0fcSAndrey Gusakov 	ret = tc_get_display_props(tc);
1355*7caff0fcSAndrey Gusakov 	if (ret)
1356*7caff0fcSAndrey Gusakov 		goto err_unregister_aux;
1357*7caff0fcSAndrey Gusakov 
1358*7caff0fcSAndrey Gusakov 	tc_connector_set_polling(tc, &tc->connector);
1359*7caff0fcSAndrey Gusakov 
1360*7caff0fcSAndrey Gusakov 	tc->bridge.funcs = &tc_bridge_funcs;
1361*7caff0fcSAndrey Gusakov 	tc->bridge.of_node = dev->of_node;
1362*7caff0fcSAndrey Gusakov 	ret = drm_bridge_add(&tc->bridge);
1363*7caff0fcSAndrey Gusakov 	if (ret) {
1364*7caff0fcSAndrey Gusakov 		dev_err(dev, "Failed to add drm_bridge: %d\n", ret);
1365*7caff0fcSAndrey Gusakov 		goto err_unregister_aux;
1366*7caff0fcSAndrey Gusakov 	}
1367*7caff0fcSAndrey Gusakov 
1368*7caff0fcSAndrey Gusakov 	i2c_set_clientdata(client, tc);
1369*7caff0fcSAndrey Gusakov 
1370*7caff0fcSAndrey Gusakov 	return 0;
1371*7caff0fcSAndrey Gusakov err_unregister_aux:
1372*7caff0fcSAndrey Gusakov 	drm_dp_aux_unregister(&tc->aux);
1373*7caff0fcSAndrey Gusakov 	return ret;
1374*7caff0fcSAndrey Gusakov }
1375*7caff0fcSAndrey Gusakov 
1376*7caff0fcSAndrey Gusakov static int tc_remove(struct i2c_client *client)
1377*7caff0fcSAndrey Gusakov {
1378*7caff0fcSAndrey Gusakov 	struct tc_data *tc = i2c_get_clientdata(client);
1379*7caff0fcSAndrey Gusakov 
1380*7caff0fcSAndrey Gusakov 	drm_bridge_remove(&tc->bridge);
1381*7caff0fcSAndrey Gusakov 	drm_dp_aux_unregister(&tc->aux);
1382*7caff0fcSAndrey Gusakov 
1383*7caff0fcSAndrey Gusakov 	tc_pxl_pll_dis(tc);
1384*7caff0fcSAndrey Gusakov 
1385*7caff0fcSAndrey Gusakov 	return 0;
1386*7caff0fcSAndrey Gusakov }
1387*7caff0fcSAndrey Gusakov 
1388*7caff0fcSAndrey Gusakov static const struct i2c_device_id tc358767_i2c_ids[] = {
1389*7caff0fcSAndrey Gusakov 	{ "tc358767", 0 },
1390*7caff0fcSAndrey Gusakov 	{ }
1391*7caff0fcSAndrey Gusakov };
1392*7caff0fcSAndrey Gusakov MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids);
1393*7caff0fcSAndrey Gusakov 
1394*7caff0fcSAndrey Gusakov static const struct of_device_id tc358767_of_ids[] = {
1395*7caff0fcSAndrey Gusakov 	{ .compatible = "toshiba,tc358767", },
1396*7caff0fcSAndrey Gusakov 	{ }
1397*7caff0fcSAndrey Gusakov };
1398*7caff0fcSAndrey Gusakov MODULE_DEVICE_TABLE(of, tc358767_of_ids);
1399*7caff0fcSAndrey Gusakov 
1400*7caff0fcSAndrey Gusakov static struct i2c_driver tc358767_driver = {
1401*7caff0fcSAndrey Gusakov 	.driver = {
1402*7caff0fcSAndrey Gusakov 		.name = "tc358767",
1403*7caff0fcSAndrey Gusakov 		.of_match_table = tc358767_of_ids,
1404*7caff0fcSAndrey Gusakov 	},
1405*7caff0fcSAndrey Gusakov 	.id_table = tc358767_i2c_ids,
1406*7caff0fcSAndrey Gusakov 	.probe = tc_probe,
1407*7caff0fcSAndrey Gusakov 	.remove	= tc_remove,
1408*7caff0fcSAndrey Gusakov };
1409*7caff0fcSAndrey Gusakov module_i2c_driver(tc358767_driver);
1410*7caff0fcSAndrey Gusakov 
1411*7caff0fcSAndrey Gusakov MODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com>");
1412*7caff0fcSAndrey Gusakov MODULE_DESCRIPTION("tc358767 eDP encoder driver");
1413*7caff0fcSAndrey Gusakov MODULE_LICENSE("GPL");
1414