xref: /openbmc/u-boot/drivers/video/tegra124/dp.c (revision 59dd5aa8)
1*59dd5aa8SSimon Glass /*
2*59dd5aa8SSimon Glass  * Copyright (c) 2011-2013, NVIDIA Corporation.
3*59dd5aa8SSimon Glass  * Copyright 2014 Google Inc.
4*59dd5aa8SSimon Glass  *
5*59dd5aa8SSimon Glass  * SPDX-License-Identifier:     GPL-2.0
6*59dd5aa8SSimon Glass  */
7*59dd5aa8SSimon Glass 
8*59dd5aa8SSimon Glass #include <common.h>
9*59dd5aa8SSimon Glass #include <displayport.h>
10*59dd5aa8SSimon Glass #include <dm.h>
11*59dd5aa8SSimon Glass #include <div64.h>
12*59dd5aa8SSimon Glass #include <errno.h>
13*59dd5aa8SSimon Glass #include <fdtdec.h>
14*59dd5aa8SSimon Glass #include <asm/io.h>
15*59dd5aa8SSimon Glass #include <asm/arch-tegra/dc.h>
16*59dd5aa8SSimon Glass #include "displayport.h"
17*59dd5aa8SSimon Glass #include "edid.h"
18*59dd5aa8SSimon Glass #include "sor.h"
19*59dd5aa8SSimon Glass 
20*59dd5aa8SSimon Glass DECLARE_GLOBAL_DATA_PTR;
21*59dd5aa8SSimon Glass 
22*59dd5aa8SSimon Glass struct tegra_dp_plat {
23*59dd5aa8SSimon Glass 	ulong base;
24*59dd5aa8SSimon Glass };
25*59dd5aa8SSimon Glass 
26*59dd5aa8SSimon Glass struct tegra_dp_priv {
27*59dd5aa8SSimon Glass 	struct dpaux_ctlr *regs;
28*59dd5aa8SSimon Glass 	struct tegra_dc_sor_data *sor;
29*59dd5aa8SSimon Glass 	u8 revision;
30*59dd5aa8SSimon Glass 	int enabled;
31*59dd5aa8SSimon Glass };
32*59dd5aa8SSimon Glass 
33*59dd5aa8SSimon Glass struct tegra_dp_priv dp_data;
34*59dd5aa8SSimon Glass 
35*59dd5aa8SSimon Glass static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg)
36*59dd5aa8SSimon Glass {
37*59dd5aa8SSimon Glass 	return readl((u32 *)dp->regs + reg);
38*59dd5aa8SSimon Glass }
39*59dd5aa8SSimon Glass 
40*59dd5aa8SSimon Glass static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg,
41*59dd5aa8SSimon Glass 				      u32 val)
42*59dd5aa8SSimon Glass {
43*59dd5aa8SSimon Glass 	writel(val, (u32 *)dp->regs + reg);
44*59dd5aa8SSimon Glass }
45*59dd5aa8SSimon Glass 
46*59dd5aa8SSimon Glass static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp,
47*59dd5aa8SSimon Glass 					   u32 reg, u32 mask, u32 exp_val,
48*59dd5aa8SSimon Glass 					   u32 poll_interval_us,
49*59dd5aa8SSimon Glass 					   u32 timeout_us)
50*59dd5aa8SSimon Glass {
51*59dd5aa8SSimon Glass 	u32 reg_val = 0;
52*59dd5aa8SSimon Glass 	u32 temp = timeout_us;
53*59dd5aa8SSimon Glass 
54*59dd5aa8SSimon Glass 	do {
55*59dd5aa8SSimon Glass 		udelay(poll_interval_us);
56*59dd5aa8SSimon Glass 		reg_val = tegra_dpaux_readl(dp, reg);
57*59dd5aa8SSimon Glass 		if (timeout_us > poll_interval_us)
58*59dd5aa8SSimon Glass 			timeout_us -= poll_interval_us;
59*59dd5aa8SSimon Glass 		else
60*59dd5aa8SSimon Glass 			break;
61*59dd5aa8SSimon Glass 	} while ((reg_val & mask) != exp_val);
62*59dd5aa8SSimon Glass 
63*59dd5aa8SSimon Glass 	if ((reg_val & mask) == exp_val)
64*59dd5aa8SSimon Glass 		return 0;	/* success */
65*59dd5aa8SSimon Glass 	debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
66*59dd5aa8SSimon Glass 	      reg, reg_val, mask, exp_val);
67*59dd5aa8SSimon Glass 	return temp;
68*59dd5aa8SSimon Glass }
69*59dd5aa8SSimon Glass 
70*59dd5aa8SSimon Glass static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp)
71*59dd5aa8SSimon Glass {
72*59dd5aa8SSimon Glass 	/* According to DP spec, each aux transaction needs to finish
73*59dd5aa8SSimon Glass 	   within 40ms. */
74*59dd5aa8SSimon Glass 	if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL,
75*59dd5aa8SSimon Glass 					 DPAUX_DP_AUXCTL_TRANSACTREQ_MASK,
76*59dd5aa8SSimon Glass 					 DPAUX_DP_AUXCTL_TRANSACTREQ_DONE,
77*59dd5aa8SSimon Glass 					 100, DP_AUX_TIMEOUT_MS * 1000) != 0) {
78*59dd5aa8SSimon Glass 		debug("dp: DPAUX transaction timeout\n");
79*59dd5aa8SSimon Glass 		return -1;
80*59dd5aa8SSimon Glass 	}
81*59dd5aa8SSimon Glass 	return 0;
82*59dd5aa8SSimon Glass }
83*59dd5aa8SSimon Glass 
84*59dd5aa8SSimon Glass static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd,
85*59dd5aa8SSimon Glass 					  u32 addr, u8 *data, u32 *size,
86*59dd5aa8SSimon Glass 					  u32 *aux_stat)
87*59dd5aa8SSimon Glass {
88*59dd5aa8SSimon Glass 	int i;
89*59dd5aa8SSimon Glass 	u32 reg_val;
90*59dd5aa8SSimon Glass 	u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
91*59dd5aa8SSimon Glass 	u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
92*59dd5aa8SSimon Glass 	u32 temp_data;
93*59dd5aa8SSimon Glass 
94*59dd5aa8SSimon Glass 	if (*size > DP_AUX_MAX_BYTES)
95*59dd5aa8SSimon Glass 		return -1;	/* only write one chunk of data */
96*59dd5aa8SSimon Glass 
97*59dd5aa8SSimon Glass 	/* Make sure the command is write command */
98*59dd5aa8SSimon Glass 	switch (cmd) {
99*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_I2CWR:
100*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_MOTWR:
101*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_AUXWR:
102*59dd5aa8SSimon Glass 		break;
103*59dd5aa8SSimon Glass 	default:
104*59dd5aa8SSimon Glass 		debug("dp: aux write cmd 0x%x is invalid\n", cmd);
105*59dd5aa8SSimon Glass 		return -EINVAL;
106*59dd5aa8SSimon Glass 	}
107*59dd5aa8SSimon Glass 
108*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
109*59dd5aa8SSimon Glass 	for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) {
110*59dd5aa8SSimon Glass 		memcpy(&temp_data, data, 4);
111*59dd5aa8SSimon Glass 		tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data);
112*59dd5aa8SSimon Glass 		data += 4;
113*59dd5aa8SSimon Glass 	}
114*59dd5aa8SSimon Glass 
115*59dd5aa8SSimon Glass 	reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
116*59dd5aa8SSimon Glass 	reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
117*59dd5aa8SSimon Glass 	reg_val |= cmd;
118*59dd5aa8SSimon Glass 	reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
119*59dd5aa8SSimon Glass 	reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
120*59dd5aa8SSimon Glass 
121*59dd5aa8SSimon Glass 	while ((timeout_retries > 0) && (defer_retries > 0)) {
122*59dd5aa8SSimon Glass 		if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
123*59dd5aa8SSimon Glass 		    (defer_retries != DP_AUX_DEFER_MAX_TRIES))
124*59dd5aa8SSimon Glass 			udelay(1);
125*59dd5aa8SSimon Glass 
126*59dd5aa8SSimon Glass 		reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
127*59dd5aa8SSimon Glass 		tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
128*59dd5aa8SSimon Glass 
129*59dd5aa8SSimon Glass 		if (tegra_dpaux_wait_transaction(dp))
130*59dd5aa8SSimon Glass 			debug("dp: aux write transaction timeout\n");
131*59dd5aa8SSimon Glass 
132*59dd5aa8SSimon Glass 		*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
133*59dd5aa8SSimon Glass 
134*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
135*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
136*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
137*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
138*59dd5aa8SSimon Glass 			if (timeout_retries-- > 0) {
139*59dd5aa8SSimon Glass 				debug("dp: aux write retry (0x%x) -- %d\n",
140*59dd5aa8SSimon Glass 				      *aux_stat, timeout_retries);
141*59dd5aa8SSimon Glass 				/* clear the error bits */
142*59dd5aa8SSimon Glass 				tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
143*59dd5aa8SSimon Glass 						   *aux_stat);
144*59dd5aa8SSimon Glass 				continue;
145*59dd5aa8SSimon Glass 			} else {
146*59dd5aa8SSimon Glass 				debug("dp: aux write got error (0x%x)\n",
147*59dd5aa8SSimon Glass 				      *aux_stat);
148*59dd5aa8SSimon Glass 				return -ETIMEDOUT;
149*59dd5aa8SSimon Glass 			}
150*59dd5aa8SSimon Glass 		}
151*59dd5aa8SSimon Glass 
152*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
153*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
154*59dd5aa8SSimon Glass 			if (defer_retries-- > 0) {
155*59dd5aa8SSimon Glass 				debug("dp: aux write defer (0x%x) -- %d\n",
156*59dd5aa8SSimon Glass 				      *aux_stat, defer_retries);
157*59dd5aa8SSimon Glass 				/* clear the error bits */
158*59dd5aa8SSimon Glass 				tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
159*59dd5aa8SSimon Glass 						   *aux_stat);
160*59dd5aa8SSimon Glass 				continue;
161*59dd5aa8SSimon Glass 			} else {
162*59dd5aa8SSimon Glass 				debug("dp: aux write defer exceeds max retries (0x%x)\n",
163*59dd5aa8SSimon Glass 				      *aux_stat);
164*59dd5aa8SSimon Glass 				return -ETIMEDOUT;
165*59dd5aa8SSimon Glass 			}
166*59dd5aa8SSimon Glass 		}
167*59dd5aa8SSimon Glass 
168*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
169*59dd5aa8SSimon Glass 			DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
170*59dd5aa8SSimon Glass 			*size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
171*59dd5aa8SSimon Glass 			return 0;
172*59dd5aa8SSimon Glass 		} else {
173*59dd5aa8SSimon Glass 			debug("dp: aux write failed (0x%x)\n", *aux_stat);
174*59dd5aa8SSimon Glass 			return -EIO;
175*59dd5aa8SSimon Glass 		}
176*59dd5aa8SSimon Glass 	}
177*59dd5aa8SSimon Glass 	/* Should never come to here */
178*59dd5aa8SSimon Glass 	return -EIO;
179*59dd5aa8SSimon Glass }
180*59dd5aa8SSimon Glass 
181*59dd5aa8SSimon Glass static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd,
182*59dd5aa8SSimon Glass 					 u32 addr, u8 *data, u32 *size,
183*59dd5aa8SSimon Glass 					 u32 *aux_stat)
184*59dd5aa8SSimon Glass {
185*59dd5aa8SSimon Glass 	u32 reg_val;
186*59dd5aa8SSimon Glass 	u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
187*59dd5aa8SSimon Glass 	u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
188*59dd5aa8SSimon Glass 
189*59dd5aa8SSimon Glass 	if (*size > DP_AUX_MAX_BYTES) {
190*59dd5aa8SSimon Glass 		debug("only read one chunk\n");
191*59dd5aa8SSimon Glass 		return -EIO;	/* only read one chunk */
192*59dd5aa8SSimon Glass 	}
193*59dd5aa8SSimon Glass 
194*59dd5aa8SSimon Glass 	/* Check to make sure the command is read command */
195*59dd5aa8SSimon Glass 	switch (cmd) {
196*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_I2CRD:
197*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT:
198*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_MOTRD:
199*59dd5aa8SSimon Glass 	case DPAUX_DP_AUXCTL_CMD_AUXRD:
200*59dd5aa8SSimon Glass 		break;
201*59dd5aa8SSimon Glass 	default:
202*59dd5aa8SSimon Glass 		debug("dp: aux read cmd 0x%x is invalid\n", cmd);
203*59dd5aa8SSimon Glass 		return -EIO;
204*59dd5aa8SSimon Glass 	}
205*59dd5aa8SSimon Glass 
206*59dd5aa8SSimon Glass 	*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
207*59dd5aa8SSimon Glass 	if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
208*59dd5aa8SSimon Glass 		debug("dp: HPD is not detected\n");
209*59dd5aa8SSimon Glass 		return -EIO;
210*59dd5aa8SSimon Glass 	}
211*59dd5aa8SSimon Glass 
212*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
213*59dd5aa8SSimon Glass 
214*59dd5aa8SSimon Glass 	reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
215*59dd5aa8SSimon Glass 	reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
216*59dd5aa8SSimon Glass 	reg_val |= cmd;
217*59dd5aa8SSimon Glass 	reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
218*59dd5aa8SSimon Glass 	reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
219*59dd5aa8SSimon Glass 	while ((timeout_retries > 0) && (defer_retries > 0)) {
220*59dd5aa8SSimon Glass 		if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
221*59dd5aa8SSimon Glass 		    (defer_retries != DP_AUX_DEFER_MAX_TRIES))
222*59dd5aa8SSimon Glass 			udelay(DP_DPCP_RETRY_SLEEP_NS * 2);
223*59dd5aa8SSimon Glass 
224*59dd5aa8SSimon Glass 		reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
225*59dd5aa8SSimon Glass 		tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
226*59dd5aa8SSimon Glass 
227*59dd5aa8SSimon Glass 		if (tegra_dpaux_wait_transaction(dp))
228*59dd5aa8SSimon Glass 			debug("dp: aux read transaction timeout\n");
229*59dd5aa8SSimon Glass 
230*59dd5aa8SSimon Glass 		*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
231*59dd5aa8SSimon Glass 
232*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
233*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
234*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
235*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
236*59dd5aa8SSimon Glass 			if (timeout_retries-- > 0) {
237*59dd5aa8SSimon Glass 				debug("dp: aux read retry (0x%x) -- %d\n",
238*59dd5aa8SSimon Glass 				      *aux_stat, timeout_retries);
239*59dd5aa8SSimon Glass 				/* clear the error bits */
240*59dd5aa8SSimon Glass 				tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
241*59dd5aa8SSimon Glass 						   *aux_stat);
242*59dd5aa8SSimon Glass 				continue;	/* retry */
243*59dd5aa8SSimon Glass 			} else {
244*59dd5aa8SSimon Glass 				debug("dp: aux read got error (0x%x)\n",
245*59dd5aa8SSimon Glass 				      *aux_stat);
246*59dd5aa8SSimon Glass 				return -ETIMEDOUT;
247*59dd5aa8SSimon Glass 			}
248*59dd5aa8SSimon Glass 		}
249*59dd5aa8SSimon Glass 
250*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
251*59dd5aa8SSimon Glass 		    (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
252*59dd5aa8SSimon Glass 			if (defer_retries-- > 0) {
253*59dd5aa8SSimon Glass 				debug("dp: aux read defer (0x%x) -- %d\n",
254*59dd5aa8SSimon Glass 				      *aux_stat, defer_retries);
255*59dd5aa8SSimon Glass 				/* clear the error bits */
256*59dd5aa8SSimon Glass 				tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
257*59dd5aa8SSimon Glass 						   *aux_stat);
258*59dd5aa8SSimon Glass 				continue;
259*59dd5aa8SSimon Glass 			} else {
260*59dd5aa8SSimon Glass 				debug("dp: aux read defer exceeds max retries (0x%x)\n",
261*59dd5aa8SSimon Glass 				      *aux_stat);
262*59dd5aa8SSimon Glass 				return -ETIMEDOUT;
263*59dd5aa8SSimon Glass 			}
264*59dd5aa8SSimon Glass 		}
265*59dd5aa8SSimon Glass 
266*59dd5aa8SSimon Glass 		if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
267*59dd5aa8SSimon Glass 			DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
268*59dd5aa8SSimon Glass 			int i;
269*59dd5aa8SSimon Glass 			u32 temp_data[4];
270*59dd5aa8SSimon Glass 
271*59dd5aa8SSimon Glass 			for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i)
272*59dd5aa8SSimon Glass 				temp_data[i] = tegra_dpaux_readl(dp,
273*59dd5aa8SSimon Glass 						DPAUX_DP_AUXDATA_READ_W(i));
274*59dd5aa8SSimon Glass 
275*59dd5aa8SSimon Glass 			*size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
276*59dd5aa8SSimon Glass 			memcpy(data, temp_data, *size);
277*59dd5aa8SSimon Glass 
278*59dd5aa8SSimon Glass 			return 0;
279*59dd5aa8SSimon Glass 		} else {
280*59dd5aa8SSimon Glass 			debug("dp: aux read failed (0x%x\n", *aux_stat);
281*59dd5aa8SSimon Glass 			return -EIO;
282*59dd5aa8SSimon Glass 		}
283*59dd5aa8SSimon Glass 	}
284*59dd5aa8SSimon Glass 	/* Should never come to here */
285*59dd5aa8SSimon Glass 	debug("%s: can't\n", __func__);
286*59dd5aa8SSimon Glass 
287*59dd5aa8SSimon Glass 	return -EIO;
288*59dd5aa8SSimon Glass }
289*59dd5aa8SSimon Glass 
290*59dd5aa8SSimon Glass static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr,
291*59dd5aa8SSimon Glass 			u8 *data, u32 *size, u32 *aux_stat)
292*59dd5aa8SSimon Glass {
293*59dd5aa8SSimon Glass 	u32 finished = 0;
294*59dd5aa8SSimon Glass 	u32 cur_size;
295*59dd5aa8SSimon Glass 	int ret = 0;
296*59dd5aa8SSimon Glass 
297*59dd5aa8SSimon Glass 	do {
298*59dd5aa8SSimon Glass 		cur_size = *size - finished;
299*59dd5aa8SSimon Glass 		if (cur_size > DP_AUX_MAX_BYTES)
300*59dd5aa8SSimon Glass 			cur_size = DP_AUX_MAX_BYTES;
301*59dd5aa8SSimon Glass 
302*59dd5aa8SSimon Glass 		ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr,
303*59dd5aa8SSimon Glass 						data, &cur_size, aux_stat);
304*59dd5aa8SSimon Glass 		if (ret)
305*59dd5aa8SSimon Glass 			break;
306*59dd5aa8SSimon Glass 
307*59dd5aa8SSimon Glass 		/* cur_size should be the real size returned */
308*59dd5aa8SSimon Glass 		addr += cur_size;
309*59dd5aa8SSimon Glass 		data += cur_size;
310*59dd5aa8SSimon Glass 		finished += cur_size;
311*59dd5aa8SSimon Glass 
312*59dd5aa8SSimon Glass 	} while (*size > finished);
313*59dd5aa8SSimon Glass 	*size = finished;
314*59dd5aa8SSimon Glass 
315*59dd5aa8SSimon Glass 	return ret;
316*59dd5aa8SSimon Glass }
317*59dd5aa8SSimon Glass 
318*59dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd,
319*59dd5aa8SSimon Glass 				 u8 *data_ptr)
320*59dd5aa8SSimon Glass {
321*59dd5aa8SSimon Glass 	u32 size = 1;
322*59dd5aa8SSimon Glass 	u32 status = 0;
323*59dd5aa8SSimon Glass 	int ret;
324*59dd5aa8SSimon Glass 
325*59dd5aa8SSimon Glass 	ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
326*59dd5aa8SSimon Glass 					cmd, data_ptr, &size, &status);
327*59dd5aa8SSimon Glass 	if (ret) {
328*59dd5aa8SSimon Glass 		debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n",
329*59dd5aa8SSimon Glass 		      cmd, status);
330*59dd5aa8SSimon Glass 	}
331*59dd5aa8SSimon Glass 
332*59dd5aa8SSimon Glass 	return ret;
333*59dd5aa8SSimon Glass }
334*59dd5aa8SSimon Glass 
335*59dd5aa8SSimon Glass static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd,
336*59dd5aa8SSimon Glass 				u8 data)
337*59dd5aa8SSimon Glass {
338*59dd5aa8SSimon Glass 	u32 size = 1;
339*59dd5aa8SSimon Glass 	u32 status = 0;
340*59dd5aa8SSimon Glass 	int ret;
341*59dd5aa8SSimon Glass 
342*59dd5aa8SSimon Glass 	ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
343*59dd5aa8SSimon Glass 					cmd, &data, &size, &status);
344*59dd5aa8SSimon Glass 	if (ret) {
345*59dd5aa8SSimon Glass 		debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n",
346*59dd5aa8SSimon Glass 		      cmd, status);
347*59dd5aa8SSimon Glass 	}
348*59dd5aa8SSimon Glass 
349*59dd5aa8SSimon Glass 	return ret;
350*59dd5aa8SSimon Glass }
351*59dd5aa8SSimon Glass 
352*59dd5aa8SSimon Glass static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr,
353*59dd5aa8SSimon Glass 				 u8 addr, u8 *data, u32 size, u32 *aux_stat)
354*59dd5aa8SSimon Glass {
355*59dd5aa8SSimon Glass 	u32 finished = 0;
356*59dd5aa8SSimon Glass 	int ret = 0;
357*59dd5aa8SSimon Glass 
358*59dd5aa8SSimon Glass 	do {
359*59dd5aa8SSimon Glass 		u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished);
360*59dd5aa8SSimon Glass 
361*59dd5aa8SSimon Glass 		u32 len = 1;
362*59dd5aa8SSimon Glass 		ret = tegra_dc_dpaux_write_chunk(
363*59dd5aa8SSimon Glass 				dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr,
364*59dd5aa8SSimon Glass 				&addr, &len, aux_stat);
365*59dd5aa8SSimon Glass 		if (ret) {
366*59dd5aa8SSimon Glass 			debug("%s: error sending address to read.\n",
367*59dd5aa8SSimon Glass 			      __func__);
368*59dd5aa8SSimon Glass 			return ret;
369*59dd5aa8SSimon Glass 		}
370*59dd5aa8SSimon Glass 
371*59dd5aa8SSimon Glass 		ret = tegra_dc_dpaux_read_chunk(
372*59dd5aa8SSimon Glass 				dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr,
373*59dd5aa8SSimon Glass 				data, &cur_size, aux_stat);
374*59dd5aa8SSimon Glass 		if (ret) {
375*59dd5aa8SSimon Glass 			debug("%s: error reading data.\n", __func__);
376*59dd5aa8SSimon Glass 			return ret;
377*59dd5aa8SSimon Glass 		}
378*59dd5aa8SSimon Glass 
379*59dd5aa8SSimon Glass 		/* cur_size should be the real size returned */
380*59dd5aa8SSimon Glass 		addr += cur_size;
381*59dd5aa8SSimon Glass 		data += cur_size;
382*59dd5aa8SSimon Glass 		finished += cur_size;
383*59dd5aa8SSimon Glass 	} while (size > finished);
384*59dd5aa8SSimon Glass 
385*59dd5aa8SSimon Glass 	return finished;
386*59dd5aa8SSimon Glass }
387*59dd5aa8SSimon Glass 
388*59dd5aa8SSimon Glass static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp)
389*59dd5aa8SSimon Glass {
390*59dd5aa8SSimon Glass 	/* clear interrupt */
391*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff);
392*59dd5aa8SSimon Glass 	/* do not enable interrupt for now. Enable them when Isr in place */
393*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0);
394*59dd5aa8SSimon Glass 
395*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL,
396*59dd5aa8SSimon Glass 			   DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 |
397*59dd5aa8SSimon Glass 			   DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 |
398*59dd5aa8SSimon Glass 			   0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT |
399*59dd5aa8SSimon Glass 			   DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE);
400*59dd5aa8SSimon Glass 
401*59dd5aa8SSimon Glass 	tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE,
402*59dd5aa8SSimon Glass 			   DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP);
403*59dd5aa8SSimon Glass }
404*59dd5aa8SSimon Glass 
405*59dd5aa8SSimon Glass #ifdef DEBUG
406*59dd5aa8SSimon Glass static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp,
407*59dd5aa8SSimon Glass 	const struct tegra_dp_link_config *link_cfg)
408*59dd5aa8SSimon Glass {
409*59dd5aa8SSimon Glass 	debug("DP config: cfg_name               cfg_value\n");
410*59dd5aa8SSimon Glass 	debug("           Lane Count             %d\n",
411*59dd5aa8SSimon Glass 	      link_cfg->max_lane_count);
412*59dd5aa8SSimon Glass 	debug("           SupportEnhancedFraming %s\n",
413*59dd5aa8SSimon Glass 	      link_cfg->support_enhanced_framing ? "Y" : "N");
414*59dd5aa8SSimon Glass 	debug("           Bandwidth              %d\n",
415*59dd5aa8SSimon Glass 	      link_cfg->max_link_bw);
416*59dd5aa8SSimon Glass 	debug("           bpp                    %d\n",
417*59dd5aa8SSimon Glass 	      link_cfg->bits_per_pixel);
418*59dd5aa8SSimon Glass 	debug("           EnhancedFraming        %s\n",
419*59dd5aa8SSimon Glass 	      link_cfg->enhanced_framing ? "Y" : "N");
420*59dd5aa8SSimon Glass 	debug("           Scramble_enabled       %s\n",
421*59dd5aa8SSimon Glass 	      link_cfg->scramble_ena ? "Y" : "N");
422*59dd5aa8SSimon Glass 	debug("           LinkBW                 %d\n",
423*59dd5aa8SSimon Glass 	      link_cfg->link_bw);
424*59dd5aa8SSimon Glass 	debug("           lane_count             %d\n",
425*59dd5aa8SSimon Glass 	      link_cfg->lane_count);
426*59dd5aa8SSimon Glass 	debug("           activespolarity        %d\n",
427*59dd5aa8SSimon Glass 	      link_cfg->activepolarity);
428*59dd5aa8SSimon Glass 	debug("           active_count           %d\n",
429*59dd5aa8SSimon Glass 	      link_cfg->active_count);
430*59dd5aa8SSimon Glass 	debug("           tu_size                %d\n",
431*59dd5aa8SSimon Glass 	      link_cfg->tu_size);
432*59dd5aa8SSimon Glass 	debug("           active_frac            %d\n",
433*59dd5aa8SSimon Glass 	      link_cfg->active_frac);
434*59dd5aa8SSimon Glass 	debug("           watermark              %d\n",
435*59dd5aa8SSimon Glass 	      link_cfg->watermark);
436*59dd5aa8SSimon Glass 	debug("           hblank_sym             %d\n",
437*59dd5aa8SSimon Glass 	      link_cfg->hblank_sym);
438*59dd5aa8SSimon Glass 	debug("           vblank_sym             %d\n",
439*59dd5aa8SSimon Glass 	      link_cfg->vblank_sym);
440*59dd5aa8SSimon Glass }
441*59dd5aa8SSimon Glass #endif
442*59dd5aa8SSimon Glass 
443*59dd5aa8SSimon Glass /*
444*59dd5aa8SSimon Glass  * Calcuate if given cfg can meet the mode request.
445*59dd5aa8SSimon Glass  * Return 0 if mode is possible, -1 otherwise
446*59dd5aa8SSimon Glass  */
447*59dd5aa8SSimon Glass static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp,
448*59dd5aa8SSimon Glass 				   const struct display_timing *timing,
449*59dd5aa8SSimon Glass 				   struct tegra_dp_link_config *link_cfg)
450*59dd5aa8SSimon Glass {
451*59dd5aa8SSimon Glass 	const u32	link_rate = 27 * link_cfg->link_bw * 1000 * 1000;
452*59dd5aa8SSimon Glass 	const u64	f	  = 100000;	/* precision factor */
453*59dd5aa8SSimon Glass 	u32	num_linkclk_line; /* Number of link clocks per line */
454*59dd5aa8SSimon Glass 	u64	ratio_f; /* Ratio of incoming to outgoing data rate */
455*59dd5aa8SSimon Glass 	u64	frac_f;
456*59dd5aa8SSimon Glass 	u64	activesym_f;	/* Activesym per TU */
457*59dd5aa8SSimon Glass 	u64	activecount_f;
458*59dd5aa8SSimon Glass 	u32	activecount;
459*59dd5aa8SSimon Glass 	u32	activepolarity;
460*59dd5aa8SSimon Glass 	u64	approx_value_f;
461*59dd5aa8SSimon Glass 	u32	activefrac		  = 0;
462*59dd5aa8SSimon Glass 	u64	accumulated_error_f	  = 0;
463*59dd5aa8SSimon Glass 	u32	lowest_neg_activecount	  = 0;
464*59dd5aa8SSimon Glass 	u32	lowest_neg_activepolarity = 0;
465*59dd5aa8SSimon Glass 	u32	lowest_neg_tusize	  = 64;
466*59dd5aa8SSimon Glass 	u32	num_symbols_per_line;
467*59dd5aa8SSimon Glass 	u64	lowest_neg_activefrac	  = 0;
468*59dd5aa8SSimon Glass 	u64	lowest_neg_error_f	  = 64 * f;
469*59dd5aa8SSimon Glass 	u64	watermark_f;
470*59dd5aa8SSimon Glass 	int	i;
471*59dd5aa8SSimon Glass 	int	neg;
472*59dd5aa8SSimon Glass 
473*59dd5aa8SSimon Glass 	if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ ||
474*59dd5aa8SSimon Glass 	    !link_cfg->bits_per_pixel)
475*59dd5aa8SSimon Glass 		return -1;
476*59dd5aa8SSimon Glass 
477*59dd5aa8SSimon Glass 	if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >=
478*59dd5aa8SSimon Glass 		(u64)link_rate * 8 * link_cfg->lane_count)
479*59dd5aa8SSimon Glass 		return -1;
480*59dd5aa8SSimon Glass 
481*59dd5aa8SSimon Glass 	num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ,
482*59dd5aa8SSimon Glass 				       timing->pixelclock.typ));
483*59dd5aa8SSimon Glass 
484*59dd5aa8SSimon Glass 	ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f;
485*59dd5aa8SSimon Glass 	ratio_f /= 8;
486*59dd5aa8SSimon Glass 	do_div(ratio_f, link_rate * link_cfg->lane_count);
487*59dd5aa8SSimon Glass 
488*59dd5aa8SSimon Glass 	for (i = 64; i >= 32; --i) {
489*59dd5aa8SSimon Glass 		activesym_f	= ratio_f * i;
490*59dd5aa8SSimon Glass 		activecount_f	= lldiv(activesym_f, (u32)f) * f;
491*59dd5aa8SSimon Glass 		frac_f		= activesym_f - activecount_f;
492*59dd5aa8SSimon Glass 		activecount	= (u32)(lldiv(activecount_f, (u32)f));
493*59dd5aa8SSimon Glass 
494*59dd5aa8SSimon Glass 		if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */
495*59dd5aa8SSimon Glass 			activepolarity = 0;
496*59dd5aa8SSimon Glass 		else {
497*59dd5aa8SSimon Glass 			activepolarity = 1;
498*59dd5aa8SSimon Glass 			frac_f = f - frac_f;
499*59dd5aa8SSimon Glass 		}
500*59dd5aa8SSimon Glass 
501*59dd5aa8SSimon Glass 		if (frac_f != 0) {
502*59dd5aa8SSimon Glass 			/* warning: frac_f should be 64-bit */
503*59dd5aa8SSimon Glass 			frac_f = lldiv(f * f, frac_f); /* 1 / fraction */
504*59dd5aa8SSimon Glass 			if (frac_f > (15 * f))
505*59dd5aa8SSimon Glass 				activefrac = activepolarity ? 1 : 15;
506*59dd5aa8SSimon Glass 			else
507*59dd5aa8SSimon Glass 				activefrac = activepolarity ?
508*59dd5aa8SSimon Glass 					(u32)lldiv(frac_f, (u32)f) + 1 :
509*59dd5aa8SSimon Glass 					(u32)lldiv(frac_f, (u32)f);
510*59dd5aa8SSimon Glass 		}
511*59dd5aa8SSimon Glass 
512*59dd5aa8SSimon Glass 		if (activefrac == 1)
513*59dd5aa8SSimon Glass 			activepolarity = 0;
514*59dd5aa8SSimon Glass 
515*59dd5aa8SSimon Glass 		if (activepolarity == 1)
516*59dd5aa8SSimon Glass 			approx_value_f = activefrac ? lldiv(
517*59dd5aa8SSimon Glass 				(activecount_f + (activefrac * f - f) * f),
518*59dd5aa8SSimon Glass 				(activefrac * f)) :
519*59dd5aa8SSimon Glass 				activecount_f + f;
520*59dd5aa8SSimon Glass 		else
521*59dd5aa8SSimon Glass 			approx_value_f = activefrac ?
522*59dd5aa8SSimon Glass 				activecount_f + lldiv(f, activefrac) :
523*59dd5aa8SSimon Glass 				activecount_f;
524*59dd5aa8SSimon Glass 
525*59dd5aa8SSimon Glass 		if (activesym_f < approx_value_f) {
526*59dd5aa8SSimon Glass 			accumulated_error_f = num_linkclk_line *
527*59dd5aa8SSimon Glass 				lldiv(approx_value_f - activesym_f, i);
528*59dd5aa8SSimon Glass 			neg = 1;
529*59dd5aa8SSimon Glass 		} else {
530*59dd5aa8SSimon Glass 			accumulated_error_f = num_linkclk_line *
531*59dd5aa8SSimon Glass 				lldiv(activesym_f - approx_value_f, i);
532*59dd5aa8SSimon Glass 			neg = 0;
533*59dd5aa8SSimon Glass 		}
534*59dd5aa8SSimon Glass 
535*59dd5aa8SSimon Glass 		if ((neg && (lowest_neg_error_f > accumulated_error_f)) ||
536*59dd5aa8SSimon Glass 		    (accumulated_error_f == 0)) {
537*59dd5aa8SSimon Glass 			lowest_neg_error_f = accumulated_error_f;
538*59dd5aa8SSimon Glass 			lowest_neg_tusize = i;
539*59dd5aa8SSimon Glass 			lowest_neg_activecount = activecount;
540*59dd5aa8SSimon Glass 			lowest_neg_activepolarity = activepolarity;
541*59dd5aa8SSimon Glass 			lowest_neg_activefrac = activefrac;
542*59dd5aa8SSimon Glass 
543*59dd5aa8SSimon Glass 			if (accumulated_error_f == 0)
544*59dd5aa8SSimon Glass 				break;
545*59dd5aa8SSimon Glass 		}
546*59dd5aa8SSimon Glass 	}
547*59dd5aa8SSimon Glass 
548*59dd5aa8SSimon Glass 	if (lowest_neg_activefrac == 0) {
549*59dd5aa8SSimon Glass 		link_cfg->activepolarity = 0;
550*59dd5aa8SSimon Glass 		link_cfg->active_count   = lowest_neg_activepolarity ?
551*59dd5aa8SSimon Glass 			lowest_neg_activecount : lowest_neg_activecount - 1;
552*59dd5aa8SSimon Glass 		link_cfg->tu_size	      = lowest_neg_tusize;
553*59dd5aa8SSimon Glass 		link_cfg->active_frac    = 1;
554*59dd5aa8SSimon Glass 	} else {
555*59dd5aa8SSimon Glass 		link_cfg->activepolarity = lowest_neg_activepolarity;
556*59dd5aa8SSimon Glass 		link_cfg->active_count   = (u32)lowest_neg_activecount;
557*59dd5aa8SSimon Glass 		link_cfg->tu_size	      = lowest_neg_tusize;
558*59dd5aa8SSimon Glass 		link_cfg->active_frac    = (u32)lowest_neg_activefrac;
559*59dd5aa8SSimon Glass 	}
560*59dd5aa8SSimon Glass 
561*59dd5aa8SSimon Glass 	watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f);
562*59dd5aa8SSimon Glass 	link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f,
563*59dd5aa8SSimon Glass 		f)) + link_cfg->bits_per_pixel / 4 - 1;
564*59dd5aa8SSimon Glass 	num_symbols_per_line = (timing->hactive.typ *
565*59dd5aa8SSimon Glass 				link_cfg->bits_per_pixel) /
566*59dd5aa8SSimon Glass 			       (8 * link_cfg->lane_count);
567*59dd5aa8SSimon Glass 
568*59dd5aa8SSimon Glass 	if (link_cfg->watermark > 30) {
569*59dd5aa8SSimon Glass 		debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n");
570*59dd5aa8SSimon Glass 		link_cfg->watermark = 30;
571*59dd5aa8SSimon Glass 		return -1;
572*59dd5aa8SSimon Glass 	} else if (link_cfg->watermark > num_symbols_per_line) {
573*59dd5aa8SSimon Glass 		debug("dp: sor setting: force watermark to the number of symbols in the line\n");
574*59dd5aa8SSimon Glass 		link_cfg->watermark = num_symbols_per_line;
575*59dd5aa8SSimon Glass 		return -1;
576*59dd5aa8SSimon Glass 	}
577*59dd5aa8SSimon Glass 
578*59dd5aa8SSimon Glass 	/*
579*59dd5aa8SSimon Glass 	 * Refer to dev_disp.ref for more information.
580*59dd5aa8SSimon Glass 	 * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width -
581*59dd5aa8SSimon Glass 	 *                      SetRasterBlankStart.X - 7) * link_clk / pclk)
582*59dd5aa8SSimon Glass 	 *                      - 3 * enhanced_framing - Y
583*59dd5aa8SSimon Glass 	 * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12
584*59dd5aa8SSimon Glass 	 */
585*59dd5aa8SSimon Glass 	link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ +
586*59dd5aa8SSimon Glass 			timing->hfront_porch.typ + timing->hsync_len.typ - 7) *
587*59dd5aa8SSimon Glass 			link_rate, timing->pixelclock.typ) -
588*59dd5aa8SSimon Glass 			3 * link_cfg->enhanced_framing -
589*59dd5aa8SSimon Glass 			(12 / link_cfg->lane_count);
590*59dd5aa8SSimon Glass 
591*59dd5aa8SSimon Glass 	if (link_cfg->hblank_sym < 0)
592*59dd5aa8SSimon Glass 		link_cfg->hblank_sym = 0;
593*59dd5aa8SSimon Glass 
594*59dd5aa8SSimon Glass 
595*59dd5aa8SSimon Glass 	/*
596*59dd5aa8SSimon Glass 	 * Refer to dev_disp.ref for more information.
597*59dd5aa8SSimon Glass 	 * # symbols/vblank = ((SetRasterBlankStart.X -
598*59dd5aa8SSimon Glass 	 *                      SetRasterBlankEen.X - 25) * link_clk / pclk)
599*59dd5aa8SSimon Glass 	 *                      - Y - 1;
600*59dd5aa8SSimon Glass 	 * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
601*59dd5aa8SSimon Glass 	 */
602*59dd5aa8SSimon Glass 	link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25)
603*59dd5aa8SSimon Glass 			* link_rate, timing->pixelclock.typ) - (36 /
604*59dd5aa8SSimon Glass 			link_cfg->lane_count) - 4;
605*59dd5aa8SSimon Glass 
606*59dd5aa8SSimon Glass 	if (link_cfg->vblank_sym < 0)
607*59dd5aa8SSimon Glass 		link_cfg->vblank_sym = 0;
608*59dd5aa8SSimon Glass 
609*59dd5aa8SSimon Glass 	link_cfg->is_valid = 1;
610*59dd5aa8SSimon Glass #ifdef DEBUG
611*59dd5aa8SSimon Glass 	tegra_dc_dp_dump_link_cfg(dp, link_cfg);
612*59dd5aa8SSimon Glass #endif
613*59dd5aa8SSimon Glass 
614*59dd5aa8SSimon Glass 	return 0;
615*59dd5aa8SSimon Glass }
616*59dd5aa8SSimon Glass 
617*59dd5aa8SSimon Glass static int tegra_dc_dp_init_max_link_cfg(
618*59dd5aa8SSimon Glass 			const struct display_timing *timing,
619*59dd5aa8SSimon Glass 			struct tegra_dp_priv *dp,
620*59dd5aa8SSimon Glass 			struct tegra_dp_link_config *link_cfg)
621*59dd5aa8SSimon Glass {
622*59dd5aa8SSimon Glass 	const int drive_current = 0x40404040;
623*59dd5aa8SSimon Glass 	const int preemphasis = 0x0f0f0f0f;
624*59dd5aa8SSimon Glass 	const int postcursor = 0;
625*59dd5aa8SSimon Glass 	u8 dpcd_data;
626*59dd5aa8SSimon Glass 	int ret;
627*59dd5aa8SSimon Glass 
628*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data);
629*59dd5aa8SSimon Glass 	if (ret)
630*59dd5aa8SSimon Glass 		return ret;
631*59dd5aa8SSimon Glass 	link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK;
632*59dd5aa8SSimon Glass 
633*59dd5aa8SSimon Glass 	link_cfg->support_enhanced_framing =
634*59dd5aa8SSimon Glass 		(dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ?
635*59dd5aa8SSimon Glass 		1 : 0;
636*59dd5aa8SSimon Glass 
637*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data);
638*59dd5aa8SSimon Glass 	if (ret)
639*59dd5aa8SSimon Glass 		return ret;
640*59dd5aa8SSimon Glass 	link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ?
641*59dd5aa8SSimon Glass 				1 : 0;
642*59dd5aa8SSimon Glass 
643*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE,
644*59dd5aa8SSimon Glass 				    &link_cfg->max_link_bw);
645*59dd5aa8SSimon Glass 	if (ret)
646*59dd5aa8SSimon Glass 		return ret;
647*59dd5aa8SSimon Glass 
648*59dd5aa8SSimon Glass 	/*
649*59dd5aa8SSimon Glass 	 * Set to a high value for link training and attach.
650*59dd5aa8SSimon Glass 	 * Will be re-programmed when dp is enabled.
651*59dd5aa8SSimon Glass 	 */
652*59dd5aa8SSimon Glass 	link_cfg->drive_current = drive_current;
653*59dd5aa8SSimon Glass 	link_cfg->preemphasis = preemphasis;
654*59dd5aa8SSimon Glass 	link_cfg->postcursor = postcursor;
655*59dd5aa8SSimon Glass 
656*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data);
657*59dd5aa8SSimon Glass 	if (ret)
658*59dd5aa8SSimon Glass 		return ret;
659*59dd5aa8SSimon Glass 
660*59dd5aa8SSimon Glass 	link_cfg->alt_scramber_reset_cap =
661*59dd5aa8SSimon Glass 		(dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ?
662*59dd5aa8SSimon Glass 		1 : 0;
663*59dd5aa8SSimon Glass 	link_cfg->only_enhanced_framing =
664*59dd5aa8SSimon Glass 		(dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ?
665*59dd5aa8SSimon Glass 		1 : 0;
666*59dd5aa8SSimon Glass 
667*59dd5aa8SSimon Glass 	link_cfg->lane_count = link_cfg->max_lane_count;
668*59dd5aa8SSimon Glass 	link_cfg->link_bw = link_cfg->max_link_bw;
669*59dd5aa8SSimon Glass 	link_cfg->enhanced_framing = link_cfg->support_enhanced_framing;
670*59dd5aa8SSimon Glass 
671*59dd5aa8SSimon Glass 	tegra_dc_dp_calc_config(dp, timing, link_cfg);
672*59dd5aa8SSimon Glass 	return 0;
673*59dd5aa8SSimon Glass }
674*59dd5aa8SSimon Glass 
675*59dd5aa8SSimon Glass static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
676*59dd5aa8SSimon Glass 				struct tegra_dc_sor_data *sor, int ena)
677*59dd5aa8SSimon Glass {
678*59dd5aa8SSimon Glass 	int ret;
679*59dd5aa8SSimon Glass 
680*59dd5aa8SSimon Glass 	u8 dpcd_data = ena ?
681*59dd5aa8SSimon Glass 		DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE :
682*59dd5aa8SSimon Glass 		DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE;
683*59dd5aa8SSimon Glass 
684*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET,
685*59dd5aa8SSimon Glass 				     dpcd_data);
686*59dd5aa8SSimon Glass 	if (ret)
687*59dd5aa8SSimon Glass 		return ret;
688*59dd5aa8SSimon Glass 
689*59dd5aa8SSimon Glass 	/* Also reset the scrambler to 0xfffe */
690*59dd5aa8SSimon Glass 	tegra_dc_sor_set_internal_panel(sor, ena);
691*59dd5aa8SSimon Glass 	return 0;
692*59dd5aa8SSimon Glass }
693*59dd5aa8SSimon Glass 
694*59dd5aa8SSimon Glass static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
695*59dd5aa8SSimon Glass 				       struct tegra_dc_sor_data *sor,
696*59dd5aa8SSimon Glass 				       u8 link_bw)
697*59dd5aa8SSimon Glass {
698*59dd5aa8SSimon Glass 	tegra_dc_sor_set_link_bandwidth(sor, link_bw);
699*59dd5aa8SSimon Glass 
700*59dd5aa8SSimon Glass 	/* Sink side */
701*59dd5aa8SSimon Glass 	return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw);
702*59dd5aa8SSimon Glass }
703*59dd5aa8SSimon Glass 
704*59dd5aa8SSimon Glass static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp,
705*59dd5aa8SSimon Glass 		const struct tegra_dp_link_config *link_cfg,
706*59dd5aa8SSimon Glass 		struct tegra_dc_sor_data *sor)
707*59dd5aa8SSimon Glass {
708*59dd5aa8SSimon Glass 	u8	dpcd_data;
709*59dd5aa8SSimon Glass 	int	ret;
710*59dd5aa8SSimon Glass 
711*59dd5aa8SSimon Glass 	/* check if panel support enhanched_framing */
712*59dd5aa8SSimon Glass 	dpcd_data = link_cfg->lane_count;
713*59dd5aa8SSimon Glass 	if (link_cfg->enhanced_framing)
714*59dd5aa8SSimon Glass 		dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T;
715*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data);
716*59dd5aa8SSimon Glass 	if (ret)
717*59dd5aa8SSimon Glass 		return ret;
718*59dd5aa8SSimon Glass 
719*59dd5aa8SSimon Glass 	tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
720*59dd5aa8SSimon Glass 
721*59dd5aa8SSimon Glass 	/* Also power down lanes that will not be used */
722*59dd5aa8SSimon Glass 	return 0;
723*59dd5aa8SSimon Glass }
724*59dd5aa8SSimon Glass 
725*59dd5aa8SSimon Glass static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp,
726*59dd5aa8SSimon Glass 				    const struct tegra_dp_link_config *cfg)
727*59dd5aa8SSimon Glass {
728*59dd5aa8SSimon Glass 	u32 lane;
729*59dd5aa8SSimon Glass 	u8 mask;
730*59dd5aa8SSimon Glass 	u8 data;
731*59dd5aa8SSimon Glass 	int ret;
732*59dd5aa8SSimon Glass 
733*59dd5aa8SSimon Glass 	for (lane = 0; lane < cfg->lane_count; ++lane) {
734*59dd5aa8SSimon Glass 		ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ?
735*59dd5aa8SSimon Glass 				DP_LANE2_3_STATUS : DP_LANE0_1_STATUS,
736*59dd5aa8SSimon Glass 				&data);
737*59dd5aa8SSimon Glass 		if (ret)
738*59dd5aa8SSimon Glass 			return ret;
739*59dd5aa8SSimon Glass 		mask = (lane & 1) ?
740*59dd5aa8SSimon Glass 			NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES |
741*59dd5aa8SSimon Glass 			NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES |
742*59dd5aa8SSimon Glass 			NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES :
743*59dd5aa8SSimon Glass 			DP_LANE_CR_DONE |
744*59dd5aa8SSimon Glass 			DP_LANE_CHANNEL_EQ_DONE |
745*59dd5aa8SSimon Glass 			DP_LANE_SYMBOL_LOCKED;
746*59dd5aa8SSimon Glass 		if ((data & mask) != mask)
747*59dd5aa8SSimon Glass 			return -1;
748*59dd5aa8SSimon Glass 	}
749*59dd5aa8SSimon Glass 	return 0;
750*59dd5aa8SSimon Glass }
751*59dd5aa8SSimon Glass 
752*59dd5aa8SSimon Glass /*
753*59dd5aa8SSimon Glass  * All link training functions are ported from kernel dc driver.
754*59dd5aa8SSimon Glass  * See more details at drivers/video/tegra/dc/dp.c
755*59dd5aa8SSimon Glass  */
756*59dd5aa8SSimon Glass static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp,
757*59dd5aa8SSimon Glass 		const struct tegra_dp_link_config *link_cfg,
758*59dd5aa8SSimon Glass 		struct tegra_dc_sor_data *sor)
759*59dd5aa8SSimon Glass {
760*59dd5aa8SSimon Glass 	u8	link_bw;
761*59dd5aa8SSimon Glass 	u8	lane_count;
762*59dd5aa8SSimon Glass 	u16	data16;
763*59dd5aa8SSimon Glass 	u32	data32;
764*59dd5aa8SSimon Glass 	u32	size;
765*59dd5aa8SSimon Glass 	u32	status;
766*59dd5aa8SSimon Glass 	int	j;
767*59dd5aa8SSimon Glass 	u32	mask = 0xffff >> ((4 - link_cfg->lane_count) * 4);
768*59dd5aa8SSimon Glass 
769*59dd5aa8SSimon Glass 	tegra_dc_sor_set_lane_parm(sor, link_cfg);
770*59dd5aa8SSimon Glass 	tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET,
771*59dd5aa8SSimon Glass 			       DP_SET_ANSI_8B10B);
772*59dd5aa8SSimon Glass 
773*59dd5aa8SSimon Glass 	/* Send TP1 */
774*59dd5aa8SSimon Glass 	tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg);
775*59dd5aa8SSimon Glass 	tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
776*59dd5aa8SSimon Glass 			       DP_TRAINING_PATTERN_1);
777*59dd5aa8SSimon Glass 
778*59dd5aa8SSimon Glass 	for (j = 0; j < link_cfg->lane_count; ++j)
779*59dd5aa8SSimon Glass 		tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
780*59dd5aa8SSimon Glass 	udelay(520);
781*59dd5aa8SSimon Glass 
782*59dd5aa8SSimon Glass 	size = sizeof(data16);
783*59dd5aa8SSimon Glass 	tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
784*59dd5aa8SSimon Glass 			    DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status);
785*59dd5aa8SSimon Glass 	status = mask & 0x1111;
786*59dd5aa8SSimon Glass 	if ((data16 & status) != status) {
787*59dd5aa8SSimon Glass 		debug("dp: Link training error for TP1 (%#x, status %#x)\n",
788*59dd5aa8SSimon Glass 		      data16, status);
789*59dd5aa8SSimon Glass 		return -EFAULT;
790*59dd5aa8SSimon Glass 	}
791*59dd5aa8SSimon Glass 
792*59dd5aa8SSimon Glass 	/* enable ASSR */
793*59dd5aa8SSimon Glass 	tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena);
794*59dd5aa8SSimon Glass 	tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg);
795*59dd5aa8SSimon Glass 
796*59dd5aa8SSimon Glass 	tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
797*59dd5aa8SSimon Glass 			       link_cfg->link_bw == 20 ? 0x23 : 0x22);
798*59dd5aa8SSimon Glass 	for (j = 0; j < link_cfg->lane_count; ++j)
799*59dd5aa8SSimon Glass 		tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
800*59dd5aa8SSimon Glass 	udelay(520);
801*59dd5aa8SSimon Glass 
802*59dd5aa8SSimon Glass 	size = sizeof(data32);
803*59dd5aa8SSimon Glass 	tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS,
804*59dd5aa8SSimon Glass 			    (u8 *)&data32, &size, &status);
805*59dd5aa8SSimon Glass 	if ((data32 & mask) != (0x7777 & mask)) {
806*59dd5aa8SSimon Glass 		debug("dp: Link training error for TP2/3 (0x%x)\n", data32);
807*59dd5aa8SSimon Glass 		return -EFAULT;
808*59dd5aa8SSimon Glass 	}
809*59dd5aa8SSimon Glass 
810*59dd5aa8SSimon Glass 	tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled,
811*59dd5aa8SSimon Glass 				    link_cfg);
812*59dd5aa8SSimon Glass 	tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0);
813*59dd5aa8SSimon Glass 
814*59dd5aa8SSimon Glass 	if (tegra_dc_dp_link_trained(dp, link_cfg)) {
815*59dd5aa8SSimon Glass 		tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
816*59dd5aa8SSimon Glass 		debug("Fast link training failed, link bw %d, lane # %d\n",
817*59dd5aa8SSimon Glass 		      link_bw, lane_count);
818*59dd5aa8SSimon Glass 		return -EFAULT;
819*59dd5aa8SSimon Glass 	}
820*59dd5aa8SSimon Glass 
821*59dd5aa8SSimon Glass 	debug("Fast link training succeeded, link bw %d, lane %d\n",
822*59dd5aa8SSimon Glass 	      link_cfg->link_bw, link_cfg->lane_count);
823*59dd5aa8SSimon Glass 
824*59dd5aa8SSimon Glass 	return 0;
825*59dd5aa8SSimon Glass }
826*59dd5aa8SSimon Glass 
827*59dd5aa8SSimon Glass static int tegra_dp_link_config(struct tegra_dp_priv *dp,
828*59dd5aa8SSimon Glass 	const struct tegra_dp_link_config *link_cfg,
829*59dd5aa8SSimon Glass 	struct tegra_dc_sor_data *sor)
830*59dd5aa8SSimon Glass {
831*59dd5aa8SSimon Glass 	u8	dpcd_data;
832*59dd5aa8SSimon Glass 	u8	link_bw;
833*59dd5aa8SSimon Glass 	u8	lane_count;
834*59dd5aa8SSimon Glass 	u32	retry;
835*59dd5aa8SSimon Glass 	int	ret;
836*59dd5aa8SSimon Glass 
837*59dd5aa8SSimon Glass 	if (link_cfg->lane_count == 0) {
838*59dd5aa8SSimon Glass 		debug("dp: error: lane count is 0. Can not set link config.\n");
839*59dd5aa8SSimon Glass 		return -1;
840*59dd5aa8SSimon Glass 	}
841*59dd5aa8SSimon Glass 
842*59dd5aa8SSimon Glass 	/* Set power state if it is not in normal level */
843*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data);
844*59dd5aa8SSimon Glass 	if (ret)
845*59dd5aa8SSimon Glass 		return ret;
846*59dd5aa8SSimon Glass 	if (dpcd_data == DP_SET_POWER_D3) {
847*59dd5aa8SSimon Glass 		dpcd_data = DP_SET_POWER_D0;
848*59dd5aa8SSimon Glass 		retry = 3;	/* DP spec requires 3 retries */
849*59dd5aa8SSimon Glass 		do {
850*59dd5aa8SSimon Glass 			ret = tegra_dc_dp_dpcd_write(dp,
851*59dd5aa8SSimon Glass 				DP_SET_POWER, dpcd_data);
852*59dd5aa8SSimon Glass 		} while ((--retry > 0) && ret);
853*59dd5aa8SSimon Glass 		if (ret) {
854*59dd5aa8SSimon Glass 			debug("dp: Failed to set DP panel power\n");
855*59dd5aa8SSimon Glass 			return ret;
856*59dd5aa8SSimon Glass 		}
857*59dd5aa8SSimon Glass 	}
858*59dd5aa8SSimon Glass 
859*59dd5aa8SSimon Glass 	/* Enable ASSR if possible */
860*59dd5aa8SSimon Glass 	if (link_cfg->alt_scramber_reset_cap) {
861*59dd5aa8SSimon Glass 		ret = tegra_dc_dp_set_assr(dp, sor, 1);
862*59dd5aa8SSimon Glass 		if (ret)
863*59dd5aa8SSimon Glass 			return ret;
864*59dd5aa8SSimon Glass 	}
865*59dd5aa8SSimon Glass 
866*59dd5aa8SSimon Glass 	ret = tegra_dp_set_link_bandwidth(dp, sor, link_cfg->link_bw);
867*59dd5aa8SSimon Glass 	if (ret) {
868*59dd5aa8SSimon Glass 		debug("dp: Failed to set link bandwidth\n");
869*59dd5aa8SSimon Glass 		return ret;
870*59dd5aa8SSimon Glass 	}
871*59dd5aa8SSimon Glass 	ret = tegra_dp_set_lane_count(dp, link_cfg, sor);
872*59dd5aa8SSimon Glass 	if (ret) {
873*59dd5aa8SSimon Glass 		debug("dp: Failed to set lane count\n");
874*59dd5aa8SSimon Glass 		return ret;
875*59dd5aa8SSimon Glass 	}
876*59dd5aa8SSimon Glass 	tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
877*59dd5aa8SSimon Glass 
878*59dd5aa8SSimon Glass 	/* Now do the fast link training for eDP */
879*59dd5aa8SSimon Glass 	ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor);
880*59dd5aa8SSimon Glass 	if (ret) {
881*59dd5aa8SSimon Glass 		debug("dp: fast link training failed\n");
882*59dd5aa8SSimon Glass 		return ret;
883*59dd5aa8SSimon Glass 	}
884*59dd5aa8SSimon Glass 
885*59dd5aa8SSimon Glass 	/* Everything is good; double check the link config */
886*59dd5aa8SSimon Glass 	tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
887*59dd5aa8SSimon Glass 
888*59dd5aa8SSimon Glass 	if ((link_cfg->link_bw == link_bw) &&
889*59dd5aa8SSimon Glass 	    (link_cfg->lane_count == lane_count))
890*59dd5aa8SSimon Glass 		return 0;
891*59dd5aa8SSimon Glass 	else
892*59dd5aa8SSimon Glass 		return -EFAULT;
893*59dd5aa8SSimon Glass }
894*59dd5aa8SSimon Glass 
895*59dd5aa8SSimon Glass static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp,
896*59dd5aa8SSimon Glass 			struct tegra_dp_link_config *link_cfg,
897*59dd5aa8SSimon Glass 			struct tegra_dc_sor_data *sor,
898*59dd5aa8SSimon Glass 			const struct display_timing *timing)
899*59dd5aa8SSimon Glass {
900*59dd5aa8SSimon Glass 	struct tegra_dp_link_config temp_cfg;
901*59dd5aa8SSimon Glass 
902*59dd5aa8SSimon Glass 	if (!timing->pixelclock.typ || !timing->hactive.typ ||
903*59dd5aa8SSimon Glass 	    !timing->vactive.typ) {
904*59dd5aa8SSimon Glass 		debug("dp: error mode configuration");
905*59dd5aa8SSimon Glass 		return -EINVAL;
906*59dd5aa8SSimon Glass 	}
907*59dd5aa8SSimon Glass 	if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) {
908*59dd5aa8SSimon Glass 		debug("dp: error link configuration");
909*59dd5aa8SSimon Glass 		return -EINVAL;
910*59dd5aa8SSimon Glass 	}
911*59dd5aa8SSimon Glass 
912*59dd5aa8SSimon Glass 	link_cfg->is_valid = 0;
913*59dd5aa8SSimon Glass 
914*59dd5aa8SSimon Glass 	memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg));
915*59dd5aa8SSimon Glass 
916*59dd5aa8SSimon Glass 	temp_cfg.link_bw = temp_cfg.max_link_bw;
917*59dd5aa8SSimon Glass 	temp_cfg.lane_count = temp_cfg.max_lane_count;
918*59dd5aa8SSimon Glass 
919*59dd5aa8SSimon Glass 	/*
920*59dd5aa8SSimon Glass 	 * set to max link config
921*59dd5aa8SSimon Glass 	 */
922*59dd5aa8SSimon Glass 	if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) &&
923*59dd5aa8SSimon Glass 	    (!(tegra_dp_link_config(dp, &temp_cfg, sor))))
924*59dd5aa8SSimon Glass 		/* the max link cfg is doable */
925*59dd5aa8SSimon Glass 		memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg));
926*59dd5aa8SSimon Glass 
927*59dd5aa8SSimon Glass 	return link_cfg->is_valid ? 0 : -EFAULT;
928*59dd5aa8SSimon Glass }
929*59dd5aa8SSimon Glass 
930*59dd5aa8SSimon Glass static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp)
931*59dd5aa8SSimon Glass {
932*59dd5aa8SSimon Glass 	const int vdd_to_hpd_delay_ms = 200;
933*59dd5aa8SSimon Glass 	u32 val;
934*59dd5aa8SSimon Glass 	ulong start;
935*59dd5aa8SSimon Glass 
936*59dd5aa8SSimon Glass 	start = get_timer(0);
937*59dd5aa8SSimon Glass 	do {
938*59dd5aa8SSimon Glass 		val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
939*59dd5aa8SSimon Glass 		if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)
940*59dd5aa8SSimon Glass 			return 0;
941*59dd5aa8SSimon Glass 		udelay(100);
942*59dd5aa8SSimon Glass 	} while (get_timer(start) < vdd_to_hpd_delay_ms);
943*59dd5aa8SSimon Glass 
944*59dd5aa8SSimon Glass 	return -EIO;
945*59dd5aa8SSimon Glass }
946*59dd5aa8SSimon Glass 
947*59dd5aa8SSimon Glass int tegra_dp_enable(struct udevice *dev, int panel_bpp,
948*59dd5aa8SSimon Glass 		    const struct display_timing *timing)
949*59dd5aa8SSimon Glass {
950*59dd5aa8SSimon Glass 	struct tegra_dp_priv *priv = dev_get_priv(dev);
951*59dd5aa8SSimon Glass 	struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg;
952*59dd5aa8SSimon Glass 	struct tegra_dc_sor_data *sor;
953*59dd5aa8SSimon Glass 	int data;
954*59dd5aa8SSimon Glass 	int retry;
955*59dd5aa8SSimon Glass 	int ret;
956*59dd5aa8SSimon Glass 
957*59dd5aa8SSimon Glass 	memset(link_cfg, '\0', sizeof(*link_cfg));
958*59dd5aa8SSimon Glass 	link_cfg->is_valid = 0;
959*59dd5aa8SSimon Glass 	link_cfg->scramble_ena = 1;
960*59dd5aa8SSimon Glass 
961*59dd5aa8SSimon Glass 	tegra_dc_dpaux_enable(priv);
962*59dd5aa8SSimon Glass 
963*59dd5aa8SSimon Glass 	if (tegra_dp_hpd_plug(priv) < 0) {
964*59dd5aa8SSimon Glass 		debug("dp: hpd plug failed\n");
965*59dd5aa8SSimon Glass 		return -EIO;
966*59dd5aa8SSimon Glass 	}
967*59dd5aa8SSimon Glass 
968*59dd5aa8SSimon Glass 	link_cfg->bits_per_pixel = panel_bpp;
969*59dd5aa8SSimon Glass 	if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) {
970*59dd5aa8SSimon Glass 		debug("dp: failed to init link configuration\n");
971*59dd5aa8SSimon Glass 		return -ENOLINK;
972*59dd5aa8SSimon Glass 	}
973*59dd5aa8SSimon Glass 
974*59dd5aa8SSimon Glass 	ret = tegra_dc_sor_init(&sor);
975*59dd5aa8SSimon Glass 	if (ret)
976*59dd5aa8SSimon Glass 		return ret;
977*59dd5aa8SSimon Glass 	priv->sor = sor;
978*59dd5aa8SSimon Glass 	ret = tegra_dc_sor_enable_dp(sor, link_cfg);
979*59dd5aa8SSimon Glass 	if (ret)
980*59dd5aa8SSimon Glass 		return ret;
981*59dd5aa8SSimon Glass 
982*59dd5aa8SSimon Glass 	tegra_dc_sor_set_panel_power(sor, 1);
983*59dd5aa8SSimon Glass 
984*59dd5aa8SSimon Glass 	/* Write power on to DPCD */
985*59dd5aa8SSimon Glass 	data = DP_SET_POWER_D0;
986*59dd5aa8SSimon Glass 	retry = 0;
987*59dd5aa8SSimon Glass 	do {
988*59dd5aa8SSimon Glass 		ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data);
989*59dd5aa8SSimon Glass 	} while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret);
990*59dd5aa8SSimon Glass 
991*59dd5aa8SSimon Glass 	if (ret || retry >= DP_POWER_ON_MAX_TRIES) {
992*59dd5aa8SSimon Glass 		debug("dp: failed to power on panel (0x%x)\n", ret);
993*59dd5aa8SSimon Glass 		return -ENETUNREACH;
994*59dd5aa8SSimon Glass 		goto error_enable;
995*59dd5aa8SSimon Glass 	}
996*59dd5aa8SSimon Glass 
997*59dd5aa8SSimon Glass 	/* Confirm DP plugging status */
998*59dd5aa8SSimon Glass 	if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) &
999*59dd5aa8SSimon Glass 			DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
1000*59dd5aa8SSimon Glass 		debug("dp: could not detect HPD\n");
1001*59dd5aa8SSimon Glass 		return -ENXIO;
1002*59dd5aa8SSimon Glass 	}
1003*59dd5aa8SSimon Glass 
1004*59dd5aa8SSimon Glass 	/* Check DP version */
1005*59dd5aa8SSimon Glass 	if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) {
1006*59dd5aa8SSimon Glass 		debug("dp: failed to read the revision number from sink\n");
1007*59dd5aa8SSimon Glass 		return -EIO;
1008*59dd5aa8SSimon Glass 	}
1009*59dd5aa8SSimon Glass 
1010*59dd5aa8SSimon Glass 	if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) {
1011*59dd5aa8SSimon Glass 		debug("dp: error configuring link\n");
1012*59dd5aa8SSimon Glass 		return -ENOMEDIUM;
1013*59dd5aa8SSimon Glass 	}
1014*59dd5aa8SSimon Glass 
1015*59dd5aa8SSimon Glass 	tegra_dc_sor_set_power_state(sor, 1);
1016*59dd5aa8SSimon Glass 	ret = tegra_dc_sor_attach(sor, link_cfg, timing);
1017*59dd5aa8SSimon Glass 	if (ret && ret != -EEXIST)
1018*59dd5aa8SSimon Glass 		return ret;
1019*59dd5aa8SSimon Glass 
1020*59dd5aa8SSimon Glass 	/* Power down the unused lanes to save power - a few hundred mW */
1021*59dd5aa8SSimon Glass 	tegra_dc_sor_power_down_unused_lanes(sor, link_cfg);
1022*59dd5aa8SSimon Glass 
1023*59dd5aa8SSimon Glass 	priv->enabled = true;
1024*59dd5aa8SSimon Glass error_enable:
1025*59dd5aa8SSimon Glass 	return 0;
1026*59dd5aa8SSimon Glass }
1027*59dd5aa8SSimon Glass 
1028*59dd5aa8SSimon Glass static int tegra_dp_ofdata_to_platdata(struct udevice *dev)
1029*59dd5aa8SSimon Glass {
1030*59dd5aa8SSimon Glass 	struct tegra_dp_plat *plat = dev_get_platdata(dev);
1031*59dd5aa8SSimon Glass 	const void *blob = gd->fdt_blob;
1032*59dd5aa8SSimon Glass 
1033*59dd5aa8SSimon Glass 	plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg");
1034*59dd5aa8SSimon Glass 
1035*59dd5aa8SSimon Glass 	return 0;
1036*59dd5aa8SSimon Glass }
1037*59dd5aa8SSimon Glass 
1038*59dd5aa8SSimon Glass static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size)
1039*59dd5aa8SSimon Glass {
1040*59dd5aa8SSimon Glass 	struct tegra_dp_priv *priv = dev_get_priv(dev);
1041*59dd5aa8SSimon Glass 	const int tegra_edid_i2c_address = 0x50;
1042*59dd5aa8SSimon Glass 	u32 aux_stat = 0;
1043*59dd5aa8SSimon Glass 
1044*59dd5aa8SSimon Glass 	tegra_dc_dpaux_enable(priv);
1045*59dd5aa8SSimon Glass 
1046*59dd5aa8SSimon Glass 	return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf,
1047*59dd5aa8SSimon Glass 				     buf_size, &aux_stat);
1048*59dd5aa8SSimon Glass }
1049*59dd5aa8SSimon Glass 
1050*59dd5aa8SSimon Glass static const struct dm_display_port_ops dp_tegra_ops = {
1051*59dd5aa8SSimon Glass 	.read_edid = tegra_dp_read_edid,
1052*59dd5aa8SSimon Glass 	.enable = tegra_dp_enable,
1053*59dd5aa8SSimon Glass };
1054*59dd5aa8SSimon Glass 
1055*59dd5aa8SSimon Glass static int dp_tegra_probe(struct udevice *dev)
1056*59dd5aa8SSimon Glass {
1057*59dd5aa8SSimon Glass 	struct tegra_dp_plat *plat = dev_get_platdata(dev);
1058*59dd5aa8SSimon Glass 	struct tegra_dp_priv *priv = dev_get_priv(dev);
1059*59dd5aa8SSimon Glass 
1060*59dd5aa8SSimon Glass 	priv->regs = (struct dpaux_ctlr *)plat->base;
1061*59dd5aa8SSimon Glass 	priv->enabled = false;
1062*59dd5aa8SSimon Glass 
1063*59dd5aa8SSimon Glass 	return 0;
1064*59dd5aa8SSimon Glass }
1065*59dd5aa8SSimon Glass 
1066*59dd5aa8SSimon Glass static const struct udevice_id tegra_dp_ids[] = {
1067*59dd5aa8SSimon Glass 	{ .compatible = "nvidia,tegra124-dpaux" },
1068*59dd5aa8SSimon Glass 	{ }
1069*59dd5aa8SSimon Glass };
1070*59dd5aa8SSimon Glass 
1071*59dd5aa8SSimon Glass U_BOOT_DRIVER(dp_tegra) = {
1072*59dd5aa8SSimon Glass 	.name	= "dpaux_tegra",
1073*59dd5aa8SSimon Glass 	.id	= UCLASS_DISPLAY_PORT,
1074*59dd5aa8SSimon Glass 	.of_match = tegra_dp_ids,
1075*59dd5aa8SSimon Glass 	.ofdata_to_platdata = tegra_dp_ofdata_to_platdata,
1076*59dd5aa8SSimon Glass 	.probe	= dp_tegra_probe,
1077*59dd5aa8SSimon Glass 	.ops	= &dp_tegra_ops,
1078*59dd5aa8SSimon Glass 	.priv_auto_alloc_size = sizeof(struct tegra_dp_priv),
1079*59dd5aa8SSimon Glass 	.platdata_auto_alloc_size = sizeof(struct tegra_dp_plat),
1080*59dd5aa8SSimon Glass };
1081