xref: /OK3568_Linux_fs/u-boot/drivers/video/tegra124/sor.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (c) 2011-2013, NVIDIA Corporation.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <dm.h>
9*4882a593Smuzhiyun #include <errno.h>
10*4882a593Smuzhiyun #include <malloc.h>
11*4882a593Smuzhiyun #include <panel.h>
12*4882a593Smuzhiyun #include <syscon.h>
13*4882a593Smuzhiyun #include <video_bridge.h>
14*4882a593Smuzhiyun #include <asm/io.h>
15*4882a593Smuzhiyun #include <asm/arch/clock.h>
16*4882a593Smuzhiyun #include <asm/arch-tegra/dc.h>
17*4882a593Smuzhiyun #include "displayport.h"
18*4882a593Smuzhiyun #include "sor.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define DEBUG_SOR 0
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define APBDEV_PMC_DPD_SAMPLE				0x20
25*4882a593Smuzhiyun #define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE		0
26*4882a593Smuzhiyun #define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE			1
27*4882a593Smuzhiyun #define APBDEV_PMC_SEL_DPD_TIM				0x1c8
28*4882a593Smuzhiyun #define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT	0x7f
29*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ				0x1c0
30*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT		25
31*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF			(0 << 25)
32*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON			(1 << 25)
33*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT               30
34*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK        (0x3 << 30)
35*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE                (0 << 30)
36*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF             (1 << 30)
37*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON              (2 << 30)
38*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_STATUS			0x1c4
39*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT		25
40*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF		(0 << 25)
41*4882a593Smuzhiyun #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON		(1 << 25)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun struct tegra_dc_sor_data {
44*4882a593Smuzhiyun 	void *base;
45*4882a593Smuzhiyun 	void *pmc_base;
46*4882a593Smuzhiyun 	u8 portnum;	/* 0 or 1 */
47*4882a593Smuzhiyun 	int power_is_up;
48*4882a593Smuzhiyun 	struct udevice *panel;
49*4882a593Smuzhiyun };
50*4882a593Smuzhiyun 
tegra_sor_readl(struct tegra_dc_sor_data * sor,u32 reg)51*4882a593Smuzhiyun static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun 	return readl((u32 *)sor->base + reg);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun 
tegra_sor_writel(struct tegra_dc_sor_data * sor,u32 reg,u32 val)56*4882a593Smuzhiyun static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg,
57*4882a593Smuzhiyun 				    u32 val)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	writel(val, (u32 *)sor->base + reg);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
tegra_sor_write_field(struct tegra_dc_sor_data * sor,u32 reg,u32 mask,u32 val)62*4882a593Smuzhiyun static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
63*4882a593Smuzhiyun 	u32 reg, u32 mask, u32 val)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	u32 reg_val = tegra_sor_readl(sor, reg);
66*4882a593Smuzhiyun 	reg_val &= ~mask;
67*4882a593Smuzhiyun 	reg_val |= val;
68*4882a593Smuzhiyun 	tegra_sor_writel(sor, reg, reg_val);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun 
tegra_dp_disable_tx_pu(struct udevice * dev)71*4882a593Smuzhiyun void tegra_dp_disable_tx_pu(struct udevice *dev)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
76*4882a593Smuzhiyun 			      DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
tegra_dp_set_pe_vs_pc(struct udevice * dev,u32 mask,u32 pe_reg,u32 vs_reg,u32 pc_reg,u8 pc_supported)79*4882a593Smuzhiyun void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
80*4882a593Smuzhiyun 			   u32 vs_reg, u32 pc_reg, u8 pc_supported)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
85*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
86*4882a593Smuzhiyun 	if (pc_supported) {
87*4882a593Smuzhiyun 		tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
88*4882a593Smuzhiyun 				      pc_reg);
89*4882a593Smuzhiyun 	}
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
tegra_dc_sor_poll_register(struct tegra_dc_sor_data * sor,u32 reg,u32 mask,u32 exp_val,int poll_interval_us,int timeout_ms)92*4882a593Smuzhiyun static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
93*4882a593Smuzhiyun 				      u32 mask, u32 exp_val,
94*4882a593Smuzhiyun 				      int poll_interval_us, int timeout_ms)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun 	u32 reg_val = 0;
97*4882a593Smuzhiyun 	ulong start;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	start = get_timer(0);
100*4882a593Smuzhiyun 	do {
101*4882a593Smuzhiyun 		reg_val = tegra_sor_readl(sor, reg);
102*4882a593Smuzhiyun 		if (((reg_val & mask) == exp_val))
103*4882a593Smuzhiyun 			return 0;
104*4882a593Smuzhiyun 		udelay(poll_interval_us);
105*4882a593Smuzhiyun 	} while (get_timer(start) < timeout_ms);
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
108*4882a593Smuzhiyun 	      reg, reg_val, mask, exp_val);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	return -ETIMEDOUT;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
tegra_dc_sor_set_power_state(struct udevice * dev,int pu_pd)113*4882a593Smuzhiyun int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
116*4882a593Smuzhiyun 	u32 reg_val;
117*4882a593Smuzhiyun 	u32 orig_val;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	orig_val = tegra_sor_readl(sor, PWR);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	reg_val = pu_pd ? PWR_NORMAL_STATE_PU :
122*4882a593Smuzhiyun 		PWR_NORMAL_STATE_PD; /* normal state only */
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	if (reg_val == orig_val)
125*4882a593Smuzhiyun 		return 0;	/* No update needed */
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	reg_val |= PWR_SETTING_NEW_TRIGGER;
128*4882a593Smuzhiyun 	tegra_sor_writel(sor, PWR, reg_val);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	/* Poll to confirm it is done */
131*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, PWR,
132*4882a593Smuzhiyun 				       PWR_SETTING_NEW_DEFAULT_MASK,
133*4882a593Smuzhiyun 				       PWR_SETTING_NEW_DONE,
134*4882a593Smuzhiyun 				       100, TEGRA_SOR_TIMEOUT_MS)) {
135*4882a593Smuzhiyun 		debug("dc timeout waiting for SOR_PWR = NEW_DONE\n");
136*4882a593Smuzhiyun 		return -EFAULT;
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	return 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
tegra_dc_sor_set_dp_linkctl(struct udevice * dev,int ena,u8 training_pattern,const struct tegra_dp_link_config * link_cfg)142*4882a593Smuzhiyun void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
143*4882a593Smuzhiyun 				 u8 training_pattern,
144*4882a593Smuzhiyun 				 const struct tegra_dp_link_config *link_cfg)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
147*4882a593Smuzhiyun 	u32 reg_val;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	if (ena)
152*4882a593Smuzhiyun 		reg_val |= DP_LINKCTL_ENABLE_YES;
153*4882a593Smuzhiyun 	else
154*4882a593Smuzhiyun 		reg_val &= DP_LINKCTL_ENABLE_NO;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	reg_val &= ~DP_LINKCTL_TUSIZE_MASK;
157*4882a593Smuzhiyun 	reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (link_cfg->enhanced_framing)
160*4882a593Smuzhiyun 		reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	switch (training_pattern) {
165*4882a593Smuzhiyun 	case training_pattern_1:
166*4882a593Smuzhiyun 		tegra_sor_writel(sor, DP_TPG, 0x41414141);
167*4882a593Smuzhiyun 		break;
168*4882a593Smuzhiyun 	case training_pattern_2:
169*4882a593Smuzhiyun 	case training_pattern_3:
170*4882a593Smuzhiyun 		reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ?
171*4882a593Smuzhiyun 			0x43434343 : 0x42424242;
172*4882a593Smuzhiyun 		tegra_sor_writel(sor, DP_TPG, reg_val);
173*4882a593Smuzhiyun 		break;
174*4882a593Smuzhiyun 	default:
175*4882a593Smuzhiyun 		tegra_sor_writel(sor, DP_TPG, 0x50505050);
176*4882a593Smuzhiyun 		break;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data * sor,int pu,int is_lvds)180*4882a593Smuzhiyun static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
181*4882a593Smuzhiyun 					      int pu, int is_lvds)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	u32 reg_val;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	/* SOR lane sequencer */
186*4882a593Smuzhiyun 	if (pu) {
187*4882a593Smuzhiyun 		reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
188*4882a593Smuzhiyun 			LANE_SEQ_CTL_SEQUENCE_DOWN |
189*4882a593Smuzhiyun 			LANE_SEQ_CTL_NEW_POWER_STATE_PU;
190*4882a593Smuzhiyun 	} else {
191*4882a593Smuzhiyun 		reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
192*4882a593Smuzhiyun 			LANE_SEQ_CTL_SEQUENCE_UP |
193*4882a593Smuzhiyun 			LANE_SEQ_CTL_NEW_POWER_STATE_PD;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	if (is_lvds)
197*4882a593Smuzhiyun 		reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT;
198*4882a593Smuzhiyun 	else
199*4882a593Smuzhiyun 		reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL,
204*4882a593Smuzhiyun 				       LANE_SEQ_CTL_SETTING_MASK,
205*4882a593Smuzhiyun 				       LANE_SEQ_CTL_SETTING_NEW_DONE,
206*4882a593Smuzhiyun 				       100, TEGRA_SOR_TIMEOUT_MS)) {
207*4882a593Smuzhiyun 		debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n");
208*4882a593Smuzhiyun 		return -1;
209*4882a593Smuzhiyun 	}
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	return 0;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun 
tegra_dc_sor_power_dplanes(struct udevice * dev,u32 lane_count,int pu)214*4882a593Smuzhiyun static int tegra_dc_sor_power_dplanes(struct udevice *dev,
215*4882a593Smuzhiyun 				      u32 lane_count, int pu)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
218*4882a593Smuzhiyun 	u32 reg_val;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	if (pu) {
223*4882a593Smuzhiyun 		switch (lane_count) {
224*4882a593Smuzhiyun 		case 4:
225*4882a593Smuzhiyun 			reg_val |= (DP_PADCTL_PD_TXD_3_NO |
226*4882a593Smuzhiyun 				DP_PADCTL_PD_TXD_2_NO);
227*4882a593Smuzhiyun 			/* fall through */
228*4882a593Smuzhiyun 		case 2:
229*4882a593Smuzhiyun 			reg_val |= DP_PADCTL_PD_TXD_1_NO;
230*4882a593Smuzhiyun 		case 1:
231*4882a593Smuzhiyun 			reg_val |= DP_PADCTL_PD_TXD_0_NO;
232*4882a593Smuzhiyun 			break;
233*4882a593Smuzhiyun 		default:
234*4882a593Smuzhiyun 			debug("dp: invalid lane number %d\n", lane_count);
235*4882a593Smuzhiyun 			return -1;
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 		tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
239*4882a593Smuzhiyun 		tegra_dc_sor_set_lane_count(dev, lane_count);
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
tegra_dc_sor_set_panel_power(struct udevice * dev,int power_up)245*4882a593Smuzhiyun void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
248*4882a593Smuzhiyun 	u32 reg_val;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	if (power_up)
253*4882a593Smuzhiyun 		reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP;
254*4882a593Smuzhiyun 	else
255*4882a593Smuzhiyun 		reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
tegra_dc_sor_config_pwm(struct tegra_dc_sor_data * sor,u32 pwm_div,u32 pwm_dutycycle)260*4882a593Smuzhiyun static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
261*4882a593Smuzhiyun 				    u32 pwm_dutycycle)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	tegra_sor_writel(sor, PWM_DIV, pwm_div);
264*4882a593Smuzhiyun 	tegra_sor_writel(sor, PWM_CTL,
265*4882a593Smuzhiyun 			 (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) |
266*4882a593Smuzhiyun 			 PWM_CTL_SETTING_NEW_TRIGGER);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, PWM_CTL,
269*4882a593Smuzhiyun 				       PWM_CTL_SETTING_NEW_SHIFT,
270*4882a593Smuzhiyun 				       PWM_CTL_SETTING_NEW_DONE,
271*4882a593Smuzhiyun 				       100, TEGRA_SOR_TIMEOUT_MS)) {
272*4882a593Smuzhiyun 		debug("dp: timeout while waiting for SOR PWM setting\n");
273*4882a593Smuzhiyun 	}
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun 
tegra_dc_sor_set_dp_mode(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)276*4882a593Smuzhiyun static void tegra_dc_sor_set_dp_mode(struct udevice *dev,
277*4882a593Smuzhiyun 				const struct tegra_dp_link_config *link_cfg)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
280*4882a593Smuzhiyun 	u32 reg_val;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	tegra_dc_sor_set_dp_linkctl(dev, 1, training_pattern_none, link_cfg);
285*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
286*4882a593Smuzhiyun 	reg_val &= ~DP_CONFIG_WATERMARK_MASK;
287*4882a593Smuzhiyun 	reg_val |= link_cfg->watermark;
288*4882a593Smuzhiyun 	reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK;
289*4882a593Smuzhiyun 	reg_val |= (link_cfg->active_count <<
290*4882a593Smuzhiyun 		DP_CONFIG_ACTIVESYM_COUNT_SHIFT);
291*4882a593Smuzhiyun 	reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK;
292*4882a593Smuzhiyun 	reg_val |= (link_cfg->active_frac <<
293*4882a593Smuzhiyun 		DP_CONFIG_ACTIVESYM_FRAC_SHIFT);
294*4882a593Smuzhiyun 	if (link_cfg->activepolarity)
295*4882a593Smuzhiyun 		reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
296*4882a593Smuzhiyun 	else
297*4882a593Smuzhiyun 		reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
298*4882a593Smuzhiyun 	reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE |
299*4882a593Smuzhiyun 		DP_CONFIG_RD_RESET_VAL_NEGATIVE);
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	/* program h/vblank sym */
304*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS,
305*4882a593Smuzhiyun 			      DP_AUDIO_HBLANK_SYMBOLS_MASK,
306*4882a593Smuzhiyun 			      link_cfg->hblank_sym);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS,
309*4882a593Smuzhiyun 			      DP_AUDIO_VBLANK_SYMBOLS_MASK,
310*4882a593Smuzhiyun 			      link_cfg->vblank_sym);
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
tegra_dc_sor_super_update(struct tegra_dc_sor_data * sor)313*4882a593Smuzhiyun static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE0, 0);
316*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE0, 1);
317*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE0, 0);
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
tegra_dc_sor_update(struct tegra_dc_sor_data * sor)320*4882a593Smuzhiyun static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	tegra_sor_writel(sor, STATE0, 0);
323*4882a593Smuzhiyun 	tegra_sor_writel(sor, STATE0, 1);
324*4882a593Smuzhiyun 	tegra_sor_writel(sor, STATE0, 0);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data * sor,int up)327*4882a593Smuzhiyun static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun 	u32 reg_val;
330*4882a593Smuzhiyun 	void *pmc_base = sor->pmc_base;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	if (up) {
333*4882a593Smuzhiyun 		writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE,
334*4882a593Smuzhiyun 		       pmc_base + APBDEV_PMC_DPD_SAMPLE);
335*4882a593Smuzhiyun 		writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM);
336*4882a593Smuzhiyun 	}
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ);
339*4882a593Smuzhiyun 	reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON ||
340*4882a593Smuzhiyun 			APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON |
343*4882a593Smuzhiyun 			APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF :
344*4882a593Smuzhiyun 			APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF |
345*4882a593Smuzhiyun 			APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	/* Polling */
350*4882a593Smuzhiyun 	u32 temp = 10 * 1000;
351*4882a593Smuzhiyun 	do {
352*4882a593Smuzhiyun 		udelay(20);
353*4882a593Smuzhiyun 		reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS);
354*4882a593Smuzhiyun 		if (temp > 20)
355*4882a593Smuzhiyun 			temp -= 20;
356*4882a593Smuzhiyun 		else
357*4882a593Smuzhiyun 			break;
358*4882a593Smuzhiyun 	} while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0);
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) {
361*4882a593Smuzhiyun 		debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val);
362*4882a593Smuzhiyun 		return -EIO;
363*4882a593Smuzhiyun 	}
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (up) {
366*4882a593Smuzhiyun 		writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE,
367*4882a593Smuzhiyun 		       pmc_base + APBDEV_PMC_DPD_SAMPLE);
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun 
tegra_dc_sor_set_internal_panel(struct udevice * dev,int is_int)373*4882a593Smuzhiyun void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
376*4882a593Smuzhiyun 	u32 reg_val;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
379*4882a593Smuzhiyun 	if (is_int)
380*4882a593Smuzhiyun 		reg_val |= DP_SPARE_PANEL_INTERNAL;
381*4882a593Smuzhiyun 	else
382*4882a593Smuzhiyun 		reg_val &= ~DP_SPARE_PANEL_INTERNAL;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK |
385*4882a593Smuzhiyun 		DP_SPARE_SEQ_ENABLE_YES;
386*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun 
tegra_dc_sor_read_link_config(struct udevice * dev,u8 * link_bw,u8 * lane_count)389*4882a593Smuzhiyun void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
390*4882a593Smuzhiyun 				   u8 *lane_count)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
393*4882a593Smuzhiyun 	u32 reg_val;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, CLK_CNTRL);
396*4882a593Smuzhiyun 	*link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK)
397*4882a593Smuzhiyun 		>> CLK_CNTRL_DP_LINK_SPEED_SHIFT;
398*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor,
399*4882a593Smuzhiyun 		DP_LINKCTL(sor->portnum));
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) {
402*4882a593Smuzhiyun 	case DP_LINKCTL_LANECOUNT_ZERO:
403*4882a593Smuzhiyun 		*lane_count = 0;
404*4882a593Smuzhiyun 		break;
405*4882a593Smuzhiyun 	case DP_LINKCTL_LANECOUNT_ONE:
406*4882a593Smuzhiyun 		*lane_count = 1;
407*4882a593Smuzhiyun 		break;
408*4882a593Smuzhiyun 	case DP_LINKCTL_LANECOUNT_TWO:
409*4882a593Smuzhiyun 		*lane_count = 2;
410*4882a593Smuzhiyun 		break;
411*4882a593Smuzhiyun 	case DP_LINKCTL_LANECOUNT_FOUR:
412*4882a593Smuzhiyun 		*lane_count = 4;
413*4882a593Smuzhiyun 		break;
414*4882a593Smuzhiyun 	default:
415*4882a593Smuzhiyun 		printf("Unknown lane count\n");
416*4882a593Smuzhiyun 	}
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun 
tegra_dc_sor_set_link_bandwidth(struct udevice * dev,u8 link_bw)419*4882a593Smuzhiyun void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	tegra_sor_write_field(sor, CLK_CNTRL,
424*4882a593Smuzhiyun 			      CLK_CNTRL_DP_LINK_SPEED_MASK,
425*4882a593Smuzhiyun 			      link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
tegra_dc_sor_set_lane_count(struct udevice * dev,u8 lane_count)428*4882a593Smuzhiyun void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
431*4882a593Smuzhiyun 	u32 reg_val;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
434*4882a593Smuzhiyun 	reg_val &= ~DP_LINKCTL_LANECOUNT_MASK;
435*4882a593Smuzhiyun 	switch (lane_count) {
436*4882a593Smuzhiyun 	case 0:
437*4882a593Smuzhiyun 		break;
438*4882a593Smuzhiyun 	case 1:
439*4882a593Smuzhiyun 		reg_val |= DP_LINKCTL_LANECOUNT_ONE;
440*4882a593Smuzhiyun 		break;
441*4882a593Smuzhiyun 	case 2:
442*4882a593Smuzhiyun 		reg_val |= DP_LINKCTL_LANECOUNT_TWO;
443*4882a593Smuzhiyun 		break;
444*4882a593Smuzhiyun 	case 4:
445*4882a593Smuzhiyun 		reg_val |= DP_LINKCTL_LANECOUNT_FOUR;
446*4882a593Smuzhiyun 		break;
447*4882a593Smuzhiyun 	default:
448*4882a593Smuzhiyun 		/* 0 should be handled earlier. */
449*4882a593Smuzhiyun 		printf("dp: Invalid lane count %d\n", lane_count);
450*4882a593Smuzhiyun 		return;
451*4882a593Smuzhiyun 	}
452*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun /*
456*4882a593Smuzhiyun  * The SOR power sequencer does not work for t124 so SW has to
457*4882a593Smuzhiyun  *  go through the power sequence manually
458*4882a593Smuzhiyun  * Power up steps from spec:
459*4882a593Smuzhiyun  * STEP	PDPORT	PDPLL	PDBG	PLLVCOD	PLLCAPD	E_DPD	PDCAL
460*4882a593Smuzhiyun  * 1	1	1	1	1	1	1	1
461*4882a593Smuzhiyun  * 2	1	1	1	1	1	0	1
462*4882a593Smuzhiyun  * 3	1	1	0	1	1	0	1
463*4882a593Smuzhiyun  * 4	1	0	0	0	0	0	1
464*4882a593Smuzhiyun  * 5	0	0	0	0	0	0	1
465*4882a593Smuzhiyun  */
tegra_dc_sor_power_up(struct udevice * dev,int is_lvds)466*4882a593Smuzhiyun static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
469*4882a593Smuzhiyun 	u32 reg;
470*4882a593Smuzhiyun 	int ret;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	if (sor->power_is_up)
473*4882a593Smuzhiyun 		return 0;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	/*
476*4882a593Smuzhiyun 	 * If for some reason it is already powered up, don't do it again.
477*4882a593Smuzhiyun 	 * This can happen if U-Boot is the secondary boot loader.
478*4882a593Smuzhiyun 	 */
479*4882a593Smuzhiyun 	reg = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
480*4882a593Smuzhiyun 	if (reg & DP_PADCTL_PD_TXD_0_NO)
481*4882a593Smuzhiyun 		return 0;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	/* Set link bw */
484*4882a593Smuzhiyun 	tegra_dc_sor_set_link_bandwidth(dev, is_lvds ?
485*4882a593Smuzhiyun 					CLK_CNTRL_DP_LINK_SPEED_LVDS :
486*4882a593Smuzhiyun 					CLK_CNTRL_DP_LINK_SPEED_G1_62);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/* step 1 */
489*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
490*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */
491*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */
492*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
493*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_ENABLE |
494*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE |
495*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE);
496*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */
497*4882a593Smuzhiyun 			      PLL0_VCOPD_MASK, /* PLLVCOPD */
498*4882a593Smuzhiyun 			      PLL0_PWR_OFF | PLL0_VCOPD_ASSERT);
499*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
500*4882a593Smuzhiyun 			      DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */
501*4882a593Smuzhiyun 			      DP_PADCTL_PAD_CAL_PD_POWERDOWN);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	/* step 2 */
504*4882a593Smuzhiyun 	ret = tegra_dc_sor_io_set_dpd(sor, 1);
505*4882a593Smuzhiyun 	if (ret)
506*4882a593Smuzhiyun 		return ret;
507*4882a593Smuzhiyun 	udelay(15);
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	/* step 3 */
510*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
511*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
512*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
513*4882a593Smuzhiyun 	udelay(25);
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	/* step 4 */
516*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL0,
517*4882a593Smuzhiyun 			      PLL0_PWR_MASK | /* PDPLL */
518*4882a593Smuzhiyun 			      PLL0_VCOPD_MASK, /* PLLVCOPD */
519*4882a593Smuzhiyun 			      PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
520*4882a593Smuzhiyun 	/* PLLCAPD */
521*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
522*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
523*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
524*4882a593Smuzhiyun 	udelay(225);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	/* step 5 PDPORT */
527*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
528*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_MASK,
529*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_DISABLE);
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	sor->power_is_up = 1;
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	return 0;
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun #if DEBUG_SOR
dump_sor_reg(struct tegra_dc_sor_data * sor)537*4882a593Smuzhiyun static void dump_sor_reg(struct tegra_dc_sor_data *sor)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun #define DUMP_REG(a) printk(BIOS_INFO, "%-32s  %03x  %08x\n",		\
540*4882a593Smuzhiyun 		#a, a, tegra_sor_readl(sor, a));
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	DUMP_REG(SUPER_STATE0);
543*4882a593Smuzhiyun 	DUMP_REG(SUPER_STATE1);
544*4882a593Smuzhiyun 	DUMP_REG(STATE0);
545*4882a593Smuzhiyun 	DUMP_REG(STATE1);
546*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE0(0));
547*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE0(1));
548*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE1(0));
549*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE1(1));
550*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE2(0));
551*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE2(1));
552*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE3(0));
553*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE3(1));
554*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE4(0));
555*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE4(1));
556*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE5(0));
557*4882a593Smuzhiyun 	DUMP_REG(NV_HEAD_STATE5(1));
558*4882a593Smuzhiyun 	DUMP_REG(CRC_CNTRL);
559*4882a593Smuzhiyun 	DUMP_REG(CLK_CNTRL);
560*4882a593Smuzhiyun 	DUMP_REG(CAP);
561*4882a593Smuzhiyun 	DUMP_REG(PWR);
562*4882a593Smuzhiyun 	DUMP_REG(TEST);
563*4882a593Smuzhiyun 	DUMP_REG(PLL0);
564*4882a593Smuzhiyun 	DUMP_REG(PLL1);
565*4882a593Smuzhiyun 	DUMP_REG(PLL2);
566*4882a593Smuzhiyun 	DUMP_REG(PLL3);
567*4882a593Smuzhiyun 	DUMP_REG(CSTM);
568*4882a593Smuzhiyun 	DUMP_REG(LVDS);
569*4882a593Smuzhiyun 	DUMP_REG(CRCA);
570*4882a593Smuzhiyun 	DUMP_REG(CRCB);
571*4882a593Smuzhiyun 	DUMP_REG(SEQ_CTL);
572*4882a593Smuzhiyun 	DUMP_REG(LANE_SEQ_CTL);
573*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(0));
574*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(1));
575*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(2));
576*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(3));
577*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(4));
578*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(5));
579*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(6));
580*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(7));
581*4882a593Smuzhiyun 	DUMP_REG(SEQ_INST(8));
582*4882a593Smuzhiyun 	DUMP_REG(PWM_DIV);
583*4882a593Smuzhiyun 	DUMP_REG(PWM_CTL);
584*4882a593Smuzhiyun 	DUMP_REG(MSCHECK);
585*4882a593Smuzhiyun 	DUMP_REG(XBAR_CTRL);
586*4882a593Smuzhiyun 	DUMP_REG(DP_LINKCTL(0));
587*4882a593Smuzhiyun 	DUMP_REG(DP_LINKCTL(1));
588*4882a593Smuzhiyun 	DUMP_REG(DC(0));
589*4882a593Smuzhiyun 	DUMP_REG(DC(1));
590*4882a593Smuzhiyun 	DUMP_REG(LANE_DRIVE_CURRENT(0));
591*4882a593Smuzhiyun 	DUMP_REG(PR(0));
592*4882a593Smuzhiyun 	DUMP_REG(LANE4_PREEMPHASIS(0));
593*4882a593Smuzhiyun 	DUMP_REG(POSTCURSOR(0));
594*4882a593Smuzhiyun 	DUMP_REG(DP_CONFIG(0));
595*4882a593Smuzhiyun 	DUMP_REG(DP_CONFIG(1));
596*4882a593Smuzhiyun 	DUMP_REG(DP_MN(0));
597*4882a593Smuzhiyun 	DUMP_REG(DP_MN(1));
598*4882a593Smuzhiyun 	DUMP_REG(DP_PADCTL(0));
599*4882a593Smuzhiyun 	DUMP_REG(DP_PADCTL(1));
600*4882a593Smuzhiyun 	DUMP_REG(DP_DEBUG(0));
601*4882a593Smuzhiyun 	DUMP_REG(DP_DEBUG(1));
602*4882a593Smuzhiyun 	DUMP_REG(DP_SPARE(0));
603*4882a593Smuzhiyun 	DUMP_REG(DP_SPARE(1));
604*4882a593Smuzhiyun 	DUMP_REG(DP_TPG);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun #endif
609*4882a593Smuzhiyun 
tegra_dc_sor_config_panel(struct tegra_dc_sor_data * sor,int is_lvds,const struct tegra_dp_link_config * link_cfg,const struct display_timing * timing)610*4882a593Smuzhiyun static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor,
611*4882a593Smuzhiyun 			int is_lvds,
612*4882a593Smuzhiyun 			const struct tegra_dp_link_config *link_cfg,
613*4882a593Smuzhiyun 			const struct display_timing *timing)
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun 	const int	head_num = 0;
616*4882a593Smuzhiyun 	u32		reg_val	 = STATE1_ASY_OWNER_HEAD0 << head_num;
617*4882a593Smuzhiyun 	u32		vtotal, htotal;
618*4882a593Smuzhiyun 	u32		vsync_end, hsync_end;
619*4882a593Smuzhiyun 	u32		vblank_end, hblank_end;
620*4882a593Smuzhiyun 	u32		vblank_start, hblank_start;
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM :
623*4882a593Smuzhiyun 		STATE1_ASY_PROTOCOL_DP_A;
624*4882a593Smuzhiyun 	reg_val |= STATE1_ASY_SUBOWNER_NONE |
625*4882a593Smuzhiyun 		STATE1_ASY_CRCMODE_COMPLETE_RASTER;
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE;
628*4882a593Smuzhiyun 	reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE;
629*4882a593Smuzhiyun 	reg_val |= (link_cfg->bits_per_pixel > 18) ?
630*4882a593Smuzhiyun 		STATE1_ASY_PIXELDEPTH_BPP_24_444 :
631*4882a593Smuzhiyun 		STATE1_ASY_PIXELDEPTH_BPP_18_444;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	tegra_sor_writel(sor, STATE1, reg_val);
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	/*
636*4882a593Smuzhiyun 	 * Skipping programming NV_HEAD_STATE0, assuming:
637*4882a593Smuzhiyun 	 * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB
638*4882a593Smuzhiyun 	 */
639*4882a593Smuzhiyun 	vtotal = timing->vsync_len.typ + timing->vback_porch.typ +
640*4882a593Smuzhiyun 		timing->vactive.typ + timing->vfront_porch.typ;
641*4882a593Smuzhiyun 	htotal = timing->hsync_len.typ + timing->hback_porch.typ +
642*4882a593Smuzhiyun 		timing->hactive.typ + timing->hfront_porch.typ;
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	tegra_sor_writel(sor, NV_HEAD_STATE1(head_num),
645*4882a593Smuzhiyun 			 vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT |
646*4882a593Smuzhiyun 			 htotal << NV_HEAD_STATE1_HTOTAL_SHIFT);
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	vsync_end = timing->vsync_len.typ - 1;
649*4882a593Smuzhiyun 	hsync_end = timing->hsync_len.typ - 1;
650*4882a593Smuzhiyun 	tegra_sor_writel(sor, NV_HEAD_STATE2(head_num),
651*4882a593Smuzhiyun 			 vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT |
652*4882a593Smuzhiyun 			 hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT);
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	vblank_end = vsync_end + timing->vback_porch.typ;
655*4882a593Smuzhiyun 	hblank_end = hsync_end + timing->hback_porch.typ;
656*4882a593Smuzhiyun 	tegra_sor_writel(sor, NV_HEAD_STATE3(head_num),
657*4882a593Smuzhiyun 			 vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT |
658*4882a593Smuzhiyun 			 hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT);
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	vblank_start = vblank_end + timing->vactive.typ;
661*4882a593Smuzhiyun 	hblank_start = hblank_end + timing->hactive.typ;
662*4882a593Smuzhiyun 	tegra_sor_writel(sor, NV_HEAD_STATE4(head_num),
663*4882a593Smuzhiyun 			 vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT |
664*4882a593Smuzhiyun 			 hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT);
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	/* TODO: adding interlace mode support */
667*4882a593Smuzhiyun 	tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1);
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	tegra_sor_write_field(sor, CSTM,
670*4882a593Smuzhiyun 			      CSTM_ROTCLK_DEFAULT_MASK |
671*4882a593Smuzhiyun 			      CSTM_LVDS_EN_ENABLE,
672*4882a593Smuzhiyun 			      2 << CSTM_ROTCLK_SHIFT |
673*4882a593Smuzhiyun 			      is_lvds ? CSTM_LVDS_EN_ENABLE :
674*4882a593Smuzhiyun 			      CSTM_LVDS_EN_DISABLE);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	 tegra_dc_sor_config_pwm(sor, 1024, 1024);
677*4882a593Smuzhiyun }
678*4882a593Smuzhiyun 
tegra_dc_sor_enable_dc(struct dc_ctlr * disp_ctrl)679*4882a593Smuzhiyun static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
680*4882a593Smuzhiyun {
681*4882a593Smuzhiyun 	u32 reg_val = readl(&disp_ctrl->cmd.state_access);
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
684*4882a593Smuzhiyun 	writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt);
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	/* Enable DC now - otherwise pure text console may not show. */
687*4882a593Smuzhiyun 	writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
688*4882a593Smuzhiyun 	       &disp_ctrl->cmd.disp_cmd);
689*4882a593Smuzhiyun 	writel(reg_val, &disp_ctrl->cmd.state_access);
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun 
tegra_dc_sor_enable_dp(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)692*4882a593Smuzhiyun int tegra_dc_sor_enable_dp(struct udevice *dev,
693*4882a593Smuzhiyun 			   const struct tegra_dp_link_config *link_cfg)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
696*4882a593Smuzhiyun 	int ret;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	tegra_sor_write_field(sor, CLK_CNTRL,
699*4882a593Smuzhiyun 			      CLK_CNTRL_DP_CLK_SEL_MASK,
700*4882a593Smuzhiyun 			      CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK);
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
703*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
704*4882a593Smuzhiyun 			      PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
705*4882a593Smuzhiyun 	udelay(25);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL3,
708*4882a593Smuzhiyun 			      PLL3_PLLVDD_MODE_MASK,
709*4882a593Smuzhiyun 			      PLL3_PLLVDD_MODE_V3_3);
710*4882a593Smuzhiyun 	tegra_sor_writel(sor, PLL0,
711*4882a593Smuzhiyun 			 0xf << PLL0_ICHPMP_SHFIT |
712*4882a593Smuzhiyun 			 0x3 << PLL0_VCOCAP_SHIFT |
713*4882a593Smuzhiyun 			 PLL0_PLLREG_LEVEL_V45 |
714*4882a593Smuzhiyun 			 PLL0_RESISTORSEL_EXT |
715*4882a593Smuzhiyun 			 PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
716*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2,
717*4882a593Smuzhiyun 			      PLL2_AUX1_SEQ_MASK |
718*4882a593Smuzhiyun 			      PLL2_AUX9_LVDSEN_OVERRIDE |
719*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
720*4882a593Smuzhiyun 			      PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE |
721*4882a593Smuzhiyun 			      PLL2_AUX9_LVDSEN_OVERRIDE |
722*4882a593Smuzhiyun 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
723*4882a593Smuzhiyun 	tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH |
724*4882a593Smuzhiyun 			 PLL1_TMDS_TERM_ENABLE);
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, PLL2,
727*4882a593Smuzhiyun 				       PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
728*4882a593Smuzhiyun 				       PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE,
729*4882a593Smuzhiyun 				       100, TEGRA_SOR_TIMEOUT_MS)) {
730*4882a593Smuzhiyun 		printf("DP failed to lock PLL\n");
731*4882a593Smuzhiyun 		return -EIO;
732*4882a593Smuzhiyun 	}
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK |
735*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_MASK,
736*4882a593Smuzhiyun 			      PLL2_AUX2_OVERRIDE_POWERDOWN |
737*4882a593Smuzhiyun 			      PLL2_AUX7_PORT_POWERDOWN_DISABLE);
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	ret = tegra_dc_sor_power_up(dev, 0);
740*4882a593Smuzhiyun 	if (ret) {
741*4882a593Smuzhiyun 		debug("DP failed to power up\n");
742*4882a593Smuzhiyun 		return ret;
743*4882a593Smuzhiyun 	}
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	/* re-enable SOR clock */
746*4882a593Smuzhiyun 	clock_sor_enable_edp_clock();
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	/* Power up lanes */
749*4882a593Smuzhiyun 	tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	tegra_dc_sor_set_dp_mode(dev, link_cfg);
752*4882a593Smuzhiyun 	debug("%s ret\n", __func__);
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 	return 0;
755*4882a593Smuzhiyun }
756*4882a593Smuzhiyun 
tegra_dc_sor_attach(struct udevice * dc_dev,struct udevice * dev,const struct tegra_dp_link_config * link_cfg,const struct display_timing * timing)757*4882a593Smuzhiyun int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev,
758*4882a593Smuzhiyun 			const struct tegra_dp_link_config *link_cfg,
759*4882a593Smuzhiyun 			const struct display_timing *timing)
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
762*4882a593Smuzhiyun 	struct dc_ctlr *disp_ctrl;
763*4882a593Smuzhiyun 	u32 reg_val;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	/* Use the first display controller */
766*4882a593Smuzhiyun 	debug("%s\n", __func__);
767*4882a593Smuzhiyun 	disp_ctrl = (struct dc_ctlr *)dev_read_addr(dc_dev);
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	tegra_dc_sor_enable_dc(disp_ctrl);
770*4882a593Smuzhiyun 	tegra_dc_sor_config_panel(sor, 0, link_cfg, timing);
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	writel(0x9f00, &disp_ctrl->cmd.state_ctrl);
773*4882a593Smuzhiyun 	writel(0x9f, &disp_ctrl->cmd.state_ctrl);
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
776*4882a593Smuzhiyun 	       PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
777*4882a593Smuzhiyun 	       &disp_ctrl->cmd.disp_pow_ctrl);
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	reg_val = tegra_sor_readl(sor, TEST);
780*4882a593Smuzhiyun 	if (reg_val & TEST_ATTACHED_TRUE)
781*4882a593Smuzhiyun 		return -EEXIST;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE1,
784*4882a593Smuzhiyun 			 SUPER_STATE1_ATTACHED_NO);
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	/*
787*4882a593Smuzhiyun 	 * Enable display2sor clock at least 2 cycles before DC start,
788*4882a593Smuzhiyun 	 * to clear sor internal valid signal.
789*4882a593Smuzhiyun 	 */
790*4882a593Smuzhiyun 	writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
791*4882a593Smuzhiyun 	writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
792*4882a593Smuzhiyun 	writel(0, &disp_ctrl->disp.disp_win_opt);
793*4882a593Smuzhiyun 	writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	/* Attach head */
796*4882a593Smuzhiyun 	tegra_dc_sor_update(sor);
797*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE1,
798*4882a593Smuzhiyun 			 SUPER_STATE1_ATTACHED_YES);
799*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE1,
800*4882a593Smuzhiyun 			 SUPER_STATE1_ATTACHED_YES |
801*4882a593Smuzhiyun 			 SUPER_STATE1_ASY_HEAD_OP_AWAKE |
802*4882a593Smuzhiyun 			 SUPER_STATE1_ASY_ORMODE_NORMAL);
803*4882a593Smuzhiyun 	tegra_dc_sor_super_update(sor);
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	/* Enable dc */
806*4882a593Smuzhiyun 	reg_val = readl(&disp_ctrl->cmd.state_access);
807*4882a593Smuzhiyun 	writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
808*4882a593Smuzhiyun 	writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
809*4882a593Smuzhiyun 	       &disp_ctrl->cmd.disp_cmd);
810*4882a593Smuzhiyun 	writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
811*4882a593Smuzhiyun 	writel(reg_val, &disp_ctrl->cmd.state_access);
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, TEST,
814*4882a593Smuzhiyun 				       TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
815*4882a593Smuzhiyun 				       TEST_ACT_HEAD_OPMODE_AWAKE,
816*4882a593Smuzhiyun 				       100,
817*4882a593Smuzhiyun 				       TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
818*4882a593Smuzhiyun 		printf("dc timeout waiting for OPMOD = AWAKE\n");
819*4882a593Smuzhiyun 		return -ETIMEDOUT;
820*4882a593Smuzhiyun 	} else {
821*4882a593Smuzhiyun 		debug("%s: sor is attached\n", __func__);
822*4882a593Smuzhiyun 	}
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun #if DEBUG_SOR
825*4882a593Smuzhiyun 	dump_sor_reg(sor);
826*4882a593Smuzhiyun #endif
827*4882a593Smuzhiyun 	debug("%s: ret=%d\n", __func__, 0);
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	return 0;
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun 
tegra_dc_sor_set_lane_parm(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)832*4882a593Smuzhiyun void tegra_dc_sor_set_lane_parm(struct udevice *dev,
833*4882a593Smuzhiyun 		const struct tegra_dp_link_config *link_cfg)
834*4882a593Smuzhiyun {
835*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
838*4882a593Smuzhiyun 			 link_cfg->drive_current);
839*4882a593Smuzhiyun 	tegra_sor_writel(sor, PR(sor->portnum),
840*4882a593Smuzhiyun 			 link_cfg->preemphasis);
841*4882a593Smuzhiyun 	tegra_sor_writel(sor, POSTCURSOR(sor->portnum),
842*4882a593Smuzhiyun 			 link_cfg->postcursor);
843*4882a593Smuzhiyun 	tegra_sor_writel(sor, LVDS, 0);
844*4882a593Smuzhiyun 
845*4882a593Smuzhiyun 	tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
846*4882a593Smuzhiyun 	tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count);
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
849*4882a593Smuzhiyun 			      DP_PADCTL_TX_PU_ENABLE |
850*4882a593Smuzhiyun 			      DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
851*4882a593Smuzhiyun 			      DP_PADCTL_TX_PU_ENABLE |
852*4882a593Smuzhiyun 			      2 << DP_PADCTL_TX_PU_VALUE_SHIFT);
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	/* Precharge */
855*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0);
856*4882a593Smuzhiyun 	udelay(20);
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
859*4882a593Smuzhiyun }
860*4882a593Smuzhiyun 
tegra_dc_sor_set_voltage_swing(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)861*4882a593Smuzhiyun int tegra_dc_sor_set_voltage_swing(struct udevice *dev,
862*4882a593Smuzhiyun 				    const struct tegra_dp_link_config *link_cfg)
863*4882a593Smuzhiyun {
864*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
865*4882a593Smuzhiyun 	u32 drive_current = 0;
866*4882a593Smuzhiyun 	u32 pre_emphasis = 0;
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 	/* Set to a known-good pre-calibrated setting */
869*4882a593Smuzhiyun 	switch (link_cfg->link_bw) {
870*4882a593Smuzhiyun 	case SOR_LINK_SPEED_G1_62:
871*4882a593Smuzhiyun 	case SOR_LINK_SPEED_G2_7:
872*4882a593Smuzhiyun 		drive_current = 0x13131313;
873*4882a593Smuzhiyun 		pre_emphasis = 0;
874*4882a593Smuzhiyun 		break;
875*4882a593Smuzhiyun 	case SOR_LINK_SPEED_G5_4:
876*4882a593Smuzhiyun 		debug("T124 does not support 5.4G link clock.\n");
877*4882a593Smuzhiyun 	default:
878*4882a593Smuzhiyun 		debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
879*4882a593Smuzhiyun 		return -ENOLINK;
880*4882a593Smuzhiyun 	}
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
883*4882a593Smuzhiyun 	tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 	return 0;
886*4882a593Smuzhiyun }
887*4882a593Smuzhiyun 
tegra_dc_sor_power_down_unused_lanes(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)888*4882a593Smuzhiyun void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev,
889*4882a593Smuzhiyun 			const struct tegra_dp_link_config *link_cfg)
890*4882a593Smuzhiyun {
891*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
892*4882a593Smuzhiyun 	u32 pad_ctrl = 0;
893*4882a593Smuzhiyun 	int err = 0;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	switch (link_cfg->lane_count) {
896*4882a593Smuzhiyun 	case 4:
897*4882a593Smuzhiyun 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
898*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_1_NO |
899*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_2_NO |
900*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_3_NO;
901*4882a593Smuzhiyun 		break;
902*4882a593Smuzhiyun 	case 2:
903*4882a593Smuzhiyun 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
904*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_1_NO |
905*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_2_YES |
906*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_3_YES;
907*4882a593Smuzhiyun 		break;
908*4882a593Smuzhiyun 	case 1:
909*4882a593Smuzhiyun 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
910*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_1_YES |
911*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_2_YES |
912*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_3_YES;
913*4882a593Smuzhiyun 		break;
914*4882a593Smuzhiyun 	default:
915*4882a593Smuzhiyun 		printf("Invalid sor lane count: %u\n", link_cfg->lane_count);
916*4882a593Smuzhiyun 		return;
917*4882a593Smuzhiyun 	}
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN;
920*4882a593Smuzhiyun 	tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl);
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0);
923*4882a593Smuzhiyun 	if (err) {
924*4882a593Smuzhiyun 		debug("Wait for lane power down failed: %d\n", err);
925*4882a593Smuzhiyun 		return;
926*4882a593Smuzhiyun 	}
927*4882a593Smuzhiyun }
928*4882a593Smuzhiyun 
tegra_sor_precharge_lanes(struct udevice * dev,const struct tegra_dp_link_config * cfg)929*4882a593Smuzhiyun int tegra_sor_precharge_lanes(struct udevice *dev,
930*4882a593Smuzhiyun 			      const struct tegra_dp_link_config *cfg)
931*4882a593Smuzhiyun {
932*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
933*4882a593Smuzhiyun 	u32 val = 0;
934*4882a593Smuzhiyun 
935*4882a593Smuzhiyun 	switch (cfg->lane_count) {
936*4882a593Smuzhiyun 	case 4:
937*4882a593Smuzhiyun 		val |= (DP_PADCTL_PD_TXD_3_NO |
938*4882a593Smuzhiyun 			DP_PADCTL_PD_TXD_2_NO);
939*4882a593Smuzhiyun 		/* fall through */
940*4882a593Smuzhiyun 	case 2:
941*4882a593Smuzhiyun 		val |= DP_PADCTL_PD_TXD_1_NO;
942*4882a593Smuzhiyun 		/* fall through */
943*4882a593Smuzhiyun 	case 1:
944*4882a593Smuzhiyun 		val |= DP_PADCTL_PD_TXD_0_NO;
945*4882a593Smuzhiyun 		break;
946*4882a593Smuzhiyun 	default:
947*4882a593Smuzhiyun 		debug("dp: invalid lane number %d\n", cfg->lane_count);
948*4882a593Smuzhiyun 		return -EINVAL;
949*4882a593Smuzhiyun 	}
950*4882a593Smuzhiyun 
951*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
952*4882a593Smuzhiyun 			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
953*4882a593Smuzhiyun 			      (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
954*4882a593Smuzhiyun 	udelay(100);
955*4882a593Smuzhiyun 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
956*4882a593Smuzhiyun 			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
957*4882a593Smuzhiyun 			      0);
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 	return 0;
960*4882a593Smuzhiyun }
961*4882a593Smuzhiyun 
tegra_dc_sor_enable_sor(struct dc_ctlr * disp_ctrl,bool enable)962*4882a593Smuzhiyun static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
963*4882a593Smuzhiyun {
964*4882a593Smuzhiyun 	u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
967*4882a593Smuzhiyun 	writel(reg_val, &disp_ctrl->disp.disp_win_opt);
968*4882a593Smuzhiyun }
969*4882a593Smuzhiyun 
tegra_dc_sor_detach(struct udevice * dc_dev,struct udevice * dev)970*4882a593Smuzhiyun int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev)
971*4882a593Smuzhiyun {
972*4882a593Smuzhiyun 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
973*4882a593Smuzhiyun 	int dc_reg_ctx[DC_REG_SAVE_SPACE];
974*4882a593Smuzhiyun 	struct dc_ctlr *disp_ctrl;
975*4882a593Smuzhiyun 	unsigned long dc_int_mask;
976*4882a593Smuzhiyun 	int ret;
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun 	debug("%s\n", __func__);
979*4882a593Smuzhiyun 	/* Use the first display controller */
980*4882a593Smuzhiyun 	disp_ctrl = (struct dc_ctlr *)dev_read_addr(dev);
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	/* Sleep mode */
983*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
984*4882a593Smuzhiyun 			 SUPER_STATE1_ASY_ORMODE_SAFE |
985*4882a593Smuzhiyun 			 SUPER_STATE1_ATTACHED_YES);
986*4882a593Smuzhiyun 	tegra_dc_sor_super_update(sor);
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	if (tegra_dc_sor_poll_register(sor, TEST,
991*4882a593Smuzhiyun 				       TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
992*4882a593Smuzhiyun 				       TEST_ACT_HEAD_OPMODE_SLEEP, 100,
993*4882a593Smuzhiyun 				       TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
994*4882a593Smuzhiyun 		debug("dc timeout waiting for OPMOD = SLEEP\n");
995*4882a593Smuzhiyun 		ret = -ETIMEDOUT;
996*4882a593Smuzhiyun 		goto err;
997*4882a593Smuzhiyun 	}
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
1000*4882a593Smuzhiyun 			 SUPER_STATE1_ASY_ORMODE_SAFE |
1001*4882a593Smuzhiyun 			 SUPER_STATE1_ATTACHED_NO);
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun 	/* Mask DC interrupts during the 2 dummy frames required for detach */
1004*4882a593Smuzhiyun 	dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
1005*4882a593Smuzhiyun 	writel(0, &disp_ctrl->cmd.int_mask);
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun 	/* Stop DC->SOR path */
1008*4882a593Smuzhiyun 	tegra_dc_sor_enable_sor(disp_ctrl, false);
1009*4882a593Smuzhiyun 	ret = tegra_dc_sor_general_act(disp_ctrl);
1010*4882a593Smuzhiyun 	if (ret)
1011*4882a593Smuzhiyun 		goto err;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	/* Stop DC */
1014*4882a593Smuzhiyun 	writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
1015*4882a593Smuzhiyun 	ret = tegra_dc_sor_general_act(disp_ctrl);
1016*4882a593Smuzhiyun 	if (ret)
1017*4882a593Smuzhiyun 		goto err;
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun 	writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	return 0;
1024*4882a593Smuzhiyun err:
1025*4882a593Smuzhiyun 	debug("%s: ret=%d\n", __func__, ret);
1026*4882a593Smuzhiyun 
1027*4882a593Smuzhiyun 	return ret;
1028*4882a593Smuzhiyun }
1029*4882a593Smuzhiyun 
tegra_sor_set_backlight(struct udevice * dev,int percent)1030*4882a593Smuzhiyun static int tegra_sor_set_backlight(struct udevice *dev, int percent)
1031*4882a593Smuzhiyun {
1032*4882a593Smuzhiyun 	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
1033*4882a593Smuzhiyun 	int ret;
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	ret = panel_enable_backlight(priv->panel);
1036*4882a593Smuzhiyun 	if (ret) {
1037*4882a593Smuzhiyun 		debug("sor: Cannot enable panel backlight\n");
1038*4882a593Smuzhiyun 		return ret;
1039*4882a593Smuzhiyun 	}
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	return 0;
1042*4882a593Smuzhiyun }
1043*4882a593Smuzhiyun 
tegra_sor_ofdata_to_platdata(struct udevice * dev)1044*4882a593Smuzhiyun static int tegra_sor_ofdata_to_platdata(struct udevice *dev)
1045*4882a593Smuzhiyun {
1046*4882a593Smuzhiyun 	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
1047*4882a593Smuzhiyun 	int ret;
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 	priv->base = (void *)dev_read_addr(dev);
1050*4882a593Smuzhiyun 
1051*4882a593Smuzhiyun 	priv->pmc_base = (void *)syscon_get_first_range(TEGRA_SYSCON_PMC);
1052*4882a593Smuzhiyun 	if (IS_ERR(priv->pmc_base))
1053*4882a593Smuzhiyun 		return PTR_ERR(priv->pmc_base);
1054*4882a593Smuzhiyun 
1055*4882a593Smuzhiyun 	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel",
1056*4882a593Smuzhiyun 					   &priv->panel);
1057*4882a593Smuzhiyun 	if (ret) {
1058*4882a593Smuzhiyun 		debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
1059*4882a593Smuzhiyun 		      dev->name, ret);
1060*4882a593Smuzhiyun 		return ret;
1061*4882a593Smuzhiyun 	}
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 	return 0;
1064*4882a593Smuzhiyun }
1065*4882a593Smuzhiyun 
1066*4882a593Smuzhiyun static const struct video_bridge_ops tegra_sor_ops = {
1067*4882a593Smuzhiyun 	.set_backlight	= tegra_sor_set_backlight,
1068*4882a593Smuzhiyun };
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun static const struct udevice_id tegra_sor_ids[] = {
1071*4882a593Smuzhiyun 	{ .compatible = "nvidia,tegra124-sor" },
1072*4882a593Smuzhiyun 	{ }
1073*4882a593Smuzhiyun };
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun U_BOOT_DRIVER(sor_tegra) = {
1076*4882a593Smuzhiyun 	.name	= "sor_tegra",
1077*4882a593Smuzhiyun 	.id	= UCLASS_VIDEO_BRIDGE,
1078*4882a593Smuzhiyun 	.of_match = tegra_sor_ids,
1079*4882a593Smuzhiyun 	.ofdata_to_platdata = tegra_sor_ofdata_to_platdata,
1080*4882a593Smuzhiyun 	.ops	= &tegra_sor_ops,
1081*4882a593Smuzhiyun 	.priv_auto_alloc_size = sizeof(struct tegra_dc_sor_data),
1082*4882a593Smuzhiyun };
1083