xref: /openbmc/u-boot/drivers/video/tegra124/sor.c (revision 3d5ced9e22d32112a20f9dc0f5fb1f22ef088079)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0
200f37327SSimon Glass /*
300f37327SSimon Glass  * Copyright (c) 2011-2013, NVIDIA Corporation.
400f37327SSimon Glass  */
500f37327SSimon Glass 
600f37327SSimon Glass #include <common.h>
7d7659212SSimon Glass #include <dm.h>
800f37327SSimon Glass #include <errno.h>
900f37327SSimon Glass #include <malloc.h>
10d7659212SSimon Glass #include <panel.h>
11079ff3b9SSimon Glass #include <syscon.h>
12d7659212SSimon Glass #include <video_bridge.h>
1300f37327SSimon Glass #include <asm/io.h>
1400f37327SSimon Glass #include <asm/arch/clock.h>
1500f37327SSimon Glass #include <asm/arch-tegra/dc.h>
1600f37327SSimon Glass #include "displayport.h"
1700f37327SSimon Glass #include "sor.h"
1800f37327SSimon Glass 
1900f37327SSimon Glass #define DEBUG_SOR 0
2000f37327SSimon Glass 
2100f37327SSimon Glass #define APBDEV_PMC_DPD_SAMPLE				0x20
2200f37327SSimon Glass #define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE		0
2300f37327SSimon Glass #define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE			1
2400f37327SSimon Glass #define APBDEV_PMC_SEL_DPD_TIM				0x1c8
2500f37327SSimon Glass #define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT	0x7f
2600f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ				0x1c0
2700f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT		25
2800f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF			(0 << 25)
2900f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON			(1 << 25)
3000f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT               30
3100f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK        (0x3 << 30)
3200f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE                (0 << 30)
3300f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF             (1 << 30)
3400f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON              (2 << 30)
3500f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_STATUS			0x1c4
3600f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT		25
3700f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF		(0 << 25)
3800f37327SSimon Glass #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON		(1 << 25)
3900f37327SSimon Glass 
40d7659212SSimon Glass struct tegra_dc_sor_data {
41d7659212SSimon Glass 	void *base;
42d7659212SSimon Glass 	void *pmc_base;
43d7659212SSimon Glass 	u8 portnum;	/* 0 or 1 */
44d7659212SSimon Glass 	int power_is_up;
45d7659212SSimon Glass 	struct udevice *panel;
46d7659212SSimon Glass };
47d7659212SSimon Glass 
tegra_sor_readl(struct tegra_dc_sor_data * sor,u32 reg)4800f37327SSimon Glass static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
4900f37327SSimon Glass {
5000f37327SSimon Glass 	return readl((u32 *)sor->base + reg);
5100f37327SSimon Glass }
5200f37327SSimon Glass 
tegra_sor_writel(struct tegra_dc_sor_data * sor,u32 reg,u32 val)5300f37327SSimon Glass static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg,
5400f37327SSimon Glass 				    u32 val)
5500f37327SSimon Glass {
5600f37327SSimon Glass 	writel(val, (u32 *)sor->base + reg);
5700f37327SSimon Glass }
5800f37327SSimon Glass 
tegra_sor_write_field(struct tegra_dc_sor_data * sor,u32 reg,u32 mask,u32 val)5900f37327SSimon Glass static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
6000f37327SSimon Glass 	u32 reg, u32 mask, u32 val)
6100f37327SSimon Glass {
6200f37327SSimon Glass 	u32 reg_val = tegra_sor_readl(sor, reg);
6300f37327SSimon Glass 	reg_val &= ~mask;
6400f37327SSimon Glass 	reg_val |= val;
6500f37327SSimon Glass 	tegra_sor_writel(sor, reg, reg_val);
6600f37327SSimon Glass }
6700f37327SSimon Glass 
tegra_dp_disable_tx_pu(struct udevice * dev)68d7659212SSimon Glass void tegra_dp_disable_tx_pu(struct udevice *dev)
69dedc44b4SSimon Glass {
70d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
71d7659212SSimon Glass 
72dedc44b4SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
73dedc44b4SSimon Glass 			      DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
74dedc44b4SSimon Glass }
75dedc44b4SSimon Glass 
tegra_dp_set_pe_vs_pc(struct udevice * dev,u32 mask,u32 pe_reg,u32 vs_reg,u32 pc_reg,u8 pc_supported)76d7659212SSimon Glass void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
77dedc44b4SSimon Glass 			   u32 vs_reg, u32 pc_reg, u8 pc_supported)
78dedc44b4SSimon Glass {
79d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
80d7659212SSimon Glass 
81dedc44b4SSimon Glass 	tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
82dedc44b4SSimon Glass 	tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
83dedc44b4SSimon Glass 	if (pc_supported) {
84dedc44b4SSimon Glass 		tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
85dedc44b4SSimon Glass 				      pc_reg);
86dedc44b4SSimon Glass 	}
87dedc44b4SSimon Glass }
88dedc44b4SSimon Glass 
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)8900f37327SSimon Glass static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
9000f37327SSimon Glass 				      u32 mask, u32 exp_val,
9100f37327SSimon Glass 				      int poll_interval_us, int timeout_ms)
9200f37327SSimon Glass {
9300f37327SSimon Glass 	u32 reg_val = 0;
9400f37327SSimon Glass 	ulong start;
9500f37327SSimon Glass 
9600f37327SSimon Glass 	start = get_timer(0);
9700f37327SSimon Glass 	do {
9800f37327SSimon Glass 		reg_val = tegra_sor_readl(sor, reg);
9900f37327SSimon Glass 		if (((reg_val & mask) == exp_val))
10000f37327SSimon Glass 			return 0;
10100f37327SSimon Glass 		udelay(poll_interval_us);
10200f37327SSimon Glass 	} while (get_timer(start) < timeout_ms);
10300f37327SSimon Glass 
10400f37327SSimon Glass 	debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
10500f37327SSimon Glass 	      reg, reg_val, mask, exp_val);
10600f37327SSimon Glass 
10700f37327SSimon Glass 	return -ETIMEDOUT;
10800f37327SSimon Glass }
10900f37327SSimon Glass 
tegra_dc_sor_set_power_state(struct udevice * dev,int pu_pd)110d7659212SSimon Glass int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd)
11100f37327SSimon Glass {
112d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
11300f37327SSimon Glass 	u32 reg_val;
11400f37327SSimon Glass 	u32 orig_val;
11500f37327SSimon Glass 
11600f37327SSimon Glass 	orig_val = tegra_sor_readl(sor, PWR);
11700f37327SSimon Glass 
11800f37327SSimon Glass 	reg_val = pu_pd ? PWR_NORMAL_STATE_PU :
11900f37327SSimon Glass 		PWR_NORMAL_STATE_PD; /* normal state only */
12000f37327SSimon Glass 
12100f37327SSimon Glass 	if (reg_val == orig_val)
12200f37327SSimon Glass 		return 0;	/* No update needed */
12300f37327SSimon Glass 
12400f37327SSimon Glass 	reg_val |= PWR_SETTING_NEW_TRIGGER;
12500f37327SSimon Glass 	tegra_sor_writel(sor, PWR, reg_val);
12600f37327SSimon Glass 
12700f37327SSimon Glass 	/* Poll to confirm it is done */
12800f37327SSimon Glass 	if (tegra_dc_sor_poll_register(sor, PWR,
12900f37327SSimon Glass 				       PWR_SETTING_NEW_DEFAULT_MASK,
13000f37327SSimon Glass 				       PWR_SETTING_NEW_DONE,
13100f37327SSimon Glass 				       100, TEGRA_SOR_TIMEOUT_MS)) {
13200f37327SSimon Glass 		debug("dc timeout waiting for SOR_PWR = NEW_DONE\n");
13300f37327SSimon Glass 		return -EFAULT;
13400f37327SSimon Glass 	}
13500f37327SSimon Glass 
13600f37327SSimon Glass 	return 0;
13700f37327SSimon Glass }
13800f37327SSimon Glass 
tegra_dc_sor_set_dp_linkctl(struct udevice * dev,int ena,u8 training_pattern,const struct tegra_dp_link_config * link_cfg)139d7659212SSimon Glass void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
14000f37327SSimon Glass 				 u8 training_pattern,
14100f37327SSimon Glass 				 const struct tegra_dp_link_config *link_cfg)
14200f37327SSimon Glass {
143d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
14400f37327SSimon Glass 	u32 reg_val;
14500f37327SSimon Glass 
14600f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
14700f37327SSimon Glass 
14800f37327SSimon Glass 	if (ena)
14900f37327SSimon Glass 		reg_val |= DP_LINKCTL_ENABLE_YES;
15000f37327SSimon Glass 	else
15100f37327SSimon Glass 		reg_val &= DP_LINKCTL_ENABLE_NO;
15200f37327SSimon Glass 
15300f37327SSimon Glass 	reg_val &= ~DP_LINKCTL_TUSIZE_MASK;
15400f37327SSimon Glass 	reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT);
15500f37327SSimon Glass 
15600f37327SSimon Glass 	if (link_cfg->enhanced_framing)
15700f37327SSimon Glass 		reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE;
15800f37327SSimon Glass 
15900f37327SSimon Glass 	tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
16000f37327SSimon Glass 
16100f37327SSimon Glass 	switch (training_pattern) {
16200f37327SSimon Glass 	case training_pattern_1:
16300f37327SSimon Glass 		tegra_sor_writel(sor, DP_TPG, 0x41414141);
16400f37327SSimon Glass 		break;
16500f37327SSimon Glass 	case training_pattern_2:
16600f37327SSimon Glass 	case training_pattern_3:
16700f37327SSimon Glass 		reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ?
16800f37327SSimon Glass 			0x43434343 : 0x42424242;
16900f37327SSimon Glass 		tegra_sor_writel(sor, DP_TPG, reg_val);
17000f37327SSimon Glass 		break;
17100f37327SSimon Glass 	default:
17200f37327SSimon Glass 		tegra_sor_writel(sor, DP_TPG, 0x50505050);
17300f37327SSimon Glass 		break;
17400f37327SSimon Glass 	}
17500f37327SSimon Glass }
17600f37327SSimon Glass 
tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data * sor,int pu,int is_lvds)17700f37327SSimon Glass static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
17800f37327SSimon Glass 					      int pu, int is_lvds)
17900f37327SSimon Glass {
18000f37327SSimon Glass 	u32 reg_val;
18100f37327SSimon Glass 
18200f37327SSimon Glass 	/* SOR lane sequencer */
18300f37327SSimon Glass 	if (pu) {
18400f37327SSimon Glass 		reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
18500f37327SSimon Glass 			LANE_SEQ_CTL_SEQUENCE_DOWN |
18600f37327SSimon Glass 			LANE_SEQ_CTL_NEW_POWER_STATE_PU;
18700f37327SSimon Glass 	} else {
18800f37327SSimon Glass 		reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
18900f37327SSimon Glass 			LANE_SEQ_CTL_SEQUENCE_UP |
19000f37327SSimon Glass 			LANE_SEQ_CTL_NEW_POWER_STATE_PD;
19100f37327SSimon Glass 	}
19200f37327SSimon Glass 
19300f37327SSimon Glass 	if (is_lvds)
19400f37327SSimon Glass 		reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT;
19500f37327SSimon Glass 	else
19600f37327SSimon Glass 		reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT;
19700f37327SSimon Glass 
19800f37327SSimon Glass 	tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val);
19900f37327SSimon Glass 
20000f37327SSimon Glass 	if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL,
20100f37327SSimon Glass 				       LANE_SEQ_CTL_SETTING_MASK,
20200f37327SSimon Glass 				       LANE_SEQ_CTL_SETTING_NEW_DONE,
20300f37327SSimon Glass 				       100, TEGRA_SOR_TIMEOUT_MS)) {
20400f37327SSimon Glass 		debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n");
20500f37327SSimon Glass 		return -1;
20600f37327SSimon Glass 	}
20700f37327SSimon Glass 
20800f37327SSimon Glass 	return 0;
20900f37327SSimon Glass }
21000f37327SSimon Glass 
tegra_dc_sor_power_dplanes(struct udevice * dev,u32 lane_count,int pu)211d7659212SSimon Glass static int tegra_dc_sor_power_dplanes(struct udevice *dev,
21200f37327SSimon Glass 				      u32 lane_count, int pu)
21300f37327SSimon Glass {
214d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
21500f37327SSimon Glass 	u32 reg_val;
21600f37327SSimon Glass 
21700f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
21800f37327SSimon Glass 
21900f37327SSimon Glass 	if (pu) {
22000f37327SSimon Glass 		switch (lane_count) {
22100f37327SSimon Glass 		case 4:
22200f37327SSimon Glass 			reg_val |= (DP_PADCTL_PD_TXD_3_NO |
22300f37327SSimon Glass 				DP_PADCTL_PD_TXD_2_NO);
22400f37327SSimon Glass 			/* fall through */
22500f37327SSimon Glass 		case 2:
22600f37327SSimon Glass 			reg_val |= DP_PADCTL_PD_TXD_1_NO;
22700f37327SSimon Glass 		case 1:
22800f37327SSimon Glass 			reg_val |= DP_PADCTL_PD_TXD_0_NO;
22900f37327SSimon Glass 			break;
23000f37327SSimon Glass 		default:
23100f37327SSimon Glass 			debug("dp: invalid lane number %d\n", lane_count);
23200f37327SSimon Glass 			return -1;
23300f37327SSimon Glass 		}
23400f37327SSimon Glass 
23500f37327SSimon Glass 		tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
236d7659212SSimon Glass 		tegra_dc_sor_set_lane_count(dev, lane_count);
23700f37327SSimon Glass 	}
23800f37327SSimon Glass 
23900f37327SSimon Glass 	return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
24000f37327SSimon Glass }
24100f37327SSimon Glass 
tegra_dc_sor_set_panel_power(struct udevice * dev,int power_up)242d7659212SSimon Glass void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up)
24300f37327SSimon Glass {
244d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
24500f37327SSimon Glass 	u32 reg_val;
24600f37327SSimon Glass 
24700f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
24800f37327SSimon Glass 
24900f37327SSimon Glass 	if (power_up)
25000f37327SSimon Glass 		reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP;
25100f37327SSimon Glass 	else
25200f37327SSimon Glass 		reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP;
25300f37327SSimon Glass 
25400f37327SSimon Glass 	tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
25500f37327SSimon Glass }
25600f37327SSimon Glass 
tegra_dc_sor_config_pwm(struct tegra_dc_sor_data * sor,u32 pwm_div,u32 pwm_dutycycle)25700f37327SSimon Glass static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
25800f37327SSimon Glass 				    u32 pwm_dutycycle)
25900f37327SSimon Glass {
26000f37327SSimon Glass 	tegra_sor_writel(sor, PWM_DIV, pwm_div);
26100f37327SSimon Glass 	tegra_sor_writel(sor, PWM_CTL,
26200f37327SSimon Glass 			 (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) |
26300f37327SSimon Glass 			 PWM_CTL_SETTING_NEW_TRIGGER);
26400f37327SSimon Glass 
26500f37327SSimon Glass 	if (tegra_dc_sor_poll_register(sor, PWM_CTL,
26600f37327SSimon Glass 				       PWM_CTL_SETTING_NEW_SHIFT,
26700f37327SSimon Glass 				       PWM_CTL_SETTING_NEW_DONE,
26800f37327SSimon Glass 				       100, TEGRA_SOR_TIMEOUT_MS)) {
26900f37327SSimon Glass 		debug("dp: timeout while waiting for SOR PWM setting\n");
27000f37327SSimon Glass 	}
27100f37327SSimon Glass }
27200f37327SSimon Glass 
tegra_dc_sor_set_dp_mode(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)273d7659212SSimon Glass static void tegra_dc_sor_set_dp_mode(struct udevice *dev,
27400f37327SSimon Glass 				const struct tegra_dp_link_config *link_cfg)
27500f37327SSimon Glass {
276d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
27700f37327SSimon Glass 	u32 reg_val;
27800f37327SSimon Glass 
279d7659212SSimon Glass 	tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
28000f37327SSimon Glass 
281d7659212SSimon Glass 	tegra_dc_sor_set_dp_linkctl(dev, 1, training_pattern_none, link_cfg);
28200f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
28300f37327SSimon Glass 	reg_val &= ~DP_CONFIG_WATERMARK_MASK;
28400f37327SSimon Glass 	reg_val |= link_cfg->watermark;
28500f37327SSimon Glass 	reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK;
28600f37327SSimon Glass 	reg_val |= (link_cfg->active_count <<
28700f37327SSimon Glass 		DP_CONFIG_ACTIVESYM_COUNT_SHIFT);
28800f37327SSimon Glass 	reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK;
28900f37327SSimon Glass 	reg_val |= (link_cfg->active_frac <<
29000f37327SSimon Glass 		DP_CONFIG_ACTIVESYM_FRAC_SHIFT);
29100f37327SSimon Glass 	if (link_cfg->activepolarity)
29200f37327SSimon Glass 		reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
29300f37327SSimon Glass 	else
29400f37327SSimon Glass 		reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
29500f37327SSimon Glass 	reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE |
29600f37327SSimon Glass 		DP_CONFIG_RD_RESET_VAL_NEGATIVE);
29700f37327SSimon Glass 
29800f37327SSimon Glass 	tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val);
29900f37327SSimon Glass 
30000f37327SSimon Glass 	/* program h/vblank sym */
30100f37327SSimon Glass 	tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS,
30200f37327SSimon Glass 			      DP_AUDIO_HBLANK_SYMBOLS_MASK,
30300f37327SSimon Glass 			      link_cfg->hblank_sym);
30400f37327SSimon Glass 
30500f37327SSimon Glass 	tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS,
30600f37327SSimon Glass 			      DP_AUDIO_VBLANK_SYMBOLS_MASK,
30700f37327SSimon Glass 			      link_cfg->vblank_sym);
30800f37327SSimon Glass }
30900f37327SSimon Glass 
tegra_dc_sor_super_update(struct tegra_dc_sor_data * sor)31000f37327SSimon Glass static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor)
31100f37327SSimon Glass {
31200f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE0, 0);
31300f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE0, 1);
31400f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE0, 0);
31500f37327SSimon Glass }
31600f37327SSimon Glass 
tegra_dc_sor_update(struct tegra_dc_sor_data * sor)31700f37327SSimon Glass static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor)
31800f37327SSimon Glass {
31900f37327SSimon Glass 	tegra_sor_writel(sor, STATE0, 0);
32000f37327SSimon Glass 	tegra_sor_writel(sor, STATE0, 1);
32100f37327SSimon Glass 	tegra_sor_writel(sor, STATE0, 0);
32200f37327SSimon Glass }
32300f37327SSimon Glass 
tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data * sor,int up)32400f37327SSimon Glass static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
32500f37327SSimon Glass {
32600f37327SSimon Glass 	u32 reg_val;
32700f37327SSimon Glass 	void *pmc_base = sor->pmc_base;
32800f37327SSimon Glass 
32900f37327SSimon Glass 	if (up) {
33000f37327SSimon Glass 		writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE,
33100f37327SSimon Glass 		       pmc_base + APBDEV_PMC_DPD_SAMPLE);
33200f37327SSimon Glass 		writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM);
33300f37327SSimon Glass 	}
33400f37327SSimon Glass 
33500f37327SSimon Glass 	reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ);
33600f37327SSimon Glass 	reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON ||
33700f37327SSimon Glass 			APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK);
33800f37327SSimon Glass 
33900f37327SSimon Glass 	reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON |
34000f37327SSimon Glass 			APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF :
34100f37327SSimon Glass 			APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF |
34200f37327SSimon Glass 			APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON;
34300f37327SSimon Glass 
34400f37327SSimon Glass 	writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ);
34500f37327SSimon Glass 
34600f37327SSimon Glass 	/* Polling */
34700f37327SSimon Glass 	u32 temp = 10 * 1000;
34800f37327SSimon Glass 	do {
34900f37327SSimon Glass 		udelay(20);
35000f37327SSimon Glass 		reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS);
35100f37327SSimon Glass 		if (temp > 20)
35200f37327SSimon Glass 			temp -= 20;
35300f37327SSimon Glass 		else
35400f37327SSimon Glass 			break;
35500f37327SSimon Glass 	} while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0);
35600f37327SSimon Glass 
35700f37327SSimon Glass 	if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) {
35800f37327SSimon Glass 		debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val);
35900f37327SSimon Glass 		return -EIO;
36000f37327SSimon Glass 	}
36100f37327SSimon Glass 
36200f37327SSimon Glass 	if (up) {
36300f37327SSimon Glass 		writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE,
36400f37327SSimon Glass 		       pmc_base + APBDEV_PMC_DPD_SAMPLE);
36500f37327SSimon Glass 	}
36600f37327SSimon Glass 
36700f37327SSimon Glass 	return 0;
36800f37327SSimon Glass }
36900f37327SSimon Glass 
tegra_dc_sor_set_internal_panel(struct udevice * dev,int is_int)370d7659212SSimon Glass void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int)
37100f37327SSimon Glass {
372d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
37300f37327SSimon Glass 	u32 reg_val;
37400f37327SSimon Glass 
37500f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
37600f37327SSimon Glass 	if (is_int)
37700f37327SSimon Glass 		reg_val |= DP_SPARE_PANEL_INTERNAL;
37800f37327SSimon Glass 	else
37900f37327SSimon Glass 		reg_val &= ~DP_SPARE_PANEL_INTERNAL;
38000f37327SSimon Glass 
38100f37327SSimon Glass 	reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK |
38200f37327SSimon Glass 		DP_SPARE_SEQ_ENABLE_YES;
38300f37327SSimon Glass 	tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
38400f37327SSimon Glass }
38500f37327SSimon Glass 
tegra_dc_sor_read_link_config(struct udevice * dev,u8 * link_bw,u8 * lane_count)386d7659212SSimon Glass void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
38700f37327SSimon Glass 				   u8 *lane_count)
38800f37327SSimon Glass {
389d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
39000f37327SSimon Glass 	u32 reg_val;
39100f37327SSimon Glass 
39200f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, CLK_CNTRL);
39300f37327SSimon Glass 	*link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK)
39400f37327SSimon Glass 		>> CLK_CNTRL_DP_LINK_SPEED_SHIFT;
39500f37327SSimon Glass 	reg_val = tegra_sor_readl(sor,
39600f37327SSimon Glass 		DP_LINKCTL(sor->portnum));
39700f37327SSimon Glass 
39800f37327SSimon Glass 	switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) {
39900f37327SSimon Glass 	case DP_LINKCTL_LANECOUNT_ZERO:
40000f37327SSimon Glass 		*lane_count = 0;
40100f37327SSimon Glass 		break;
40200f37327SSimon Glass 	case DP_LINKCTL_LANECOUNT_ONE:
40300f37327SSimon Glass 		*lane_count = 1;
40400f37327SSimon Glass 		break;
40500f37327SSimon Glass 	case DP_LINKCTL_LANECOUNT_TWO:
40600f37327SSimon Glass 		*lane_count = 2;
40700f37327SSimon Glass 		break;
40800f37327SSimon Glass 	case DP_LINKCTL_LANECOUNT_FOUR:
40900f37327SSimon Glass 		*lane_count = 4;
41000f37327SSimon Glass 		break;
41100f37327SSimon Glass 	default:
41200f37327SSimon Glass 		printf("Unknown lane count\n");
41300f37327SSimon Glass 	}
41400f37327SSimon Glass }
41500f37327SSimon Glass 
tegra_dc_sor_set_link_bandwidth(struct udevice * dev,u8 link_bw)416d7659212SSimon Glass void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw)
41700f37327SSimon Glass {
418d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
419d7659212SSimon Glass 
42000f37327SSimon Glass 	tegra_sor_write_field(sor, CLK_CNTRL,
42100f37327SSimon Glass 			      CLK_CNTRL_DP_LINK_SPEED_MASK,
42200f37327SSimon Glass 			      link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
42300f37327SSimon Glass }
42400f37327SSimon Glass 
tegra_dc_sor_set_lane_count(struct udevice * dev,u8 lane_count)425d7659212SSimon Glass void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count)
42600f37327SSimon Glass {
427d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
42800f37327SSimon Glass 	u32 reg_val;
42900f37327SSimon Glass 
43000f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
43100f37327SSimon Glass 	reg_val &= ~DP_LINKCTL_LANECOUNT_MASK;
43200f37327SSimon Glass 	switch (lane_count) {
43300f37327SSimon Glass 	case 0:
43400f37327SSimon Glass 		break;
43500f37327SSimon Glass 	case 1:
43600f37327SSimon Glass 		reg_val |= DP_LINKCTL_LANECOUNT_ONE;
43700f37327SSimon Glass 		break;
43800f37327SSimon Glass 	case 2:
43900f37327SSimon Glass 		reg_val |= DP_LINKCTL_LANECOUNT_TWO;
44000f37327SSimon Glass 		break;
44100f37327SSimon Glass 	case 4:
44200f37327SSimon Glass 		reg_val |= DP_LINKCTL_LANECOUNT_FOUR;
44300f37327SSimon Glass 		break;
44400f37327SSimon Glass 	default:
44500f37327SSimon Glass 		/* 0 should be handled earlier. */
44600f37327SSimon Glass 		printf("dp: Invalid lane count %d\n", lane_count);
44700f37327SSimon Glass 		return;
44800f37327SSimon Glass 	}
44900f37327SSimon Glass 	tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
45000f37327SSimon Glass }
45100f37327SSimon Glass 
45200f37327SSimon Glass /*
45300f37327SSimon Glass  * The SOR power sequencer does not work for t124 so SW has to
45400f37327SSimon Glass  *  go through the power sequence manually
45500f37327SSimon Glass  * Power up steps from spec:
45600f37327SSimon Glass  * STEP	PDPORT	PDPLL	PDBG	PLLVCOD	PLLCAPD	E_DPD	PDCAL
45700f37327SSimon Glass  * 1	1	1	1	1	1	1	1
45800f37327SSimon Glass  * 2	1	1	1	1	1	0	1
45900f37327SSimon Glass  * 3	1	1	0	1	1	0	1
46000f37327SSimon Glass  * 4	1	0	0	0	0	0	1
46100f37327SSimon Glass  * 5	0	0	0	0	0	0	1
46200f37327SSimon Glass  */
tegra_dc_sor_power_up(struct udevice * dev,int is_lvds)463d7659212SSimon Glass static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds)
46400f37327SSimon Glass {
465d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
466505907a4SSimon Glass 	u32 reg;
46700f37327SSimon Glass 	int ret;
46800f37327SSimon Glass 
46900f37327SSimon Glass 	if (sor->power_is_up)
47000f37327SSimon Glass 		return 0;
47100f37327SSimon Glass 
472505907a4SSimon Glass 	/*
473505907a4SSimon Glass 	 * If for some reason it is already powered up, don't do it again.
474505907a4SSimon Glass 	 * This can happen if U-Boot is the secondary boot loader.
475505907a4SSimon Glass 	 */
476505907a4SSimon Glass 	reg = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
477505907a4SSimon Glass 	if (reg & DP_PADCTL_PD_TXD_0_NO)
478505907a4SSimon Glass 		return 0;
479505907a4SSimon Glass 
48000f37327SSimon Glass 	/* Set link bw */
481d7659212SSimon Glass 	tegra_dc_sor_set_link_bandwidth(dev, is_lvds ?
48200f37327SSimon Glass 					CLK_CNTRL_DP_LINK_SPEED_LVDS :
48300f37327SSimon Glass 					CLK_CNTRL_DP_LINK_SPEED_G1_62);
48400f37327SSimon Glass 
48500f37327SSimon Glass 	/* step 1 */
48600f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
48700f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */
48800f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */
48900f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
49000f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_ENABLE |
49100f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE |
49200f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE);
49300f37327SSimon Glass 	tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */
49400f37327SSimon Glass 			      PLL0_VCOPD_MASK, /* PLLVCOPD */
49500f37327SSimon Glass 			      PLL0_PWR_OFF | PLL0_VCOPD_ASSERT);
49600f37327SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
49700f37327SSimon Glass 			      DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */
49800f37327SSimon Glass 			      DP_PADCTL_PAD_CAL_PD_POWERDOWN);
49900f37327SSimon Glass 
50000f37327SSimon Glass 	/* step 2 */
50100f37327SSimon Glass 	ret = tegra_dc_sor_io_set_dpd(sor, 1);
50200f37327SSimon Glass 	if (ret)
50300f37327SSimon Glass 		return ret;
50400f37327SSimon Glass 	udelay(15);
50500f37327SSimon Glass 
50600f37327SSimon Glass 	/* step 3 */
50700f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
50800f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
50900f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
51000f37327SSimon Glass 	udelay(25);
51100f37327SSimon Glass 
51200f37327SSimon Glass 	/* step 4 */
51300f37327SSimon Glass 	tegra_sor_write_field(sor, PLL0,
51400f37327SSimon Glass 			      PLL0_PWR_MASK | /* PDPLL */
51500f37327SSimon Glass 			      PLL0_VCOPD_MASK, /* PLLVCOPD */
51600f37327SSimon Glass 			      PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
51700f37327SSimon Glass 	/* PLLCAPD */
51800f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
51900f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
52000f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
52100f37327SSimon Glass 	udelay(225);
52200f37327SSimon Glass 
52300f37327SSimon Glass 	/* step 5 PDPORT */
52400f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
52500f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_MASK,
52600f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_DISABLE);
52700f37327SSimon Glass 
52800f37327SSimon Glass 	sor->power_is_up = 1;
52900f37327SSimon Glass 
53000f37327SSimon Glass 	return 0;
53100f37327SSimon Glass }
53200f37327SSimon Glass 
53300f37327SSimon Glass #if DEBUG_SOR
dump_sor_reg(struct tegra_dc_sor_data * sor)53400f37327SSimon Glass static void dump_sor_reg(struct tegra_dc_sor_data *sor)
53500f37327SSimon Glass {
536*07bc873cSSimon Glass #define DUMP_REG(a) printk(BIOS_INFO, \
537*07bc873cSSimon Glass 		"%-32s  %03x  %08x\n",		\
53800f37327SSimon Glass 		#a, a, tegra_sor_readl(sor, a));
53900f37327SSimon Glass 
54000f37327SSimon Glass 	DUMP_REG(SUPER_STATE0);
54100f37327SSimon Glass 	DUMP_REG(SUPER_STATE1);
54200f37327SSimon Glass 	DUMP_REG(STATE0);
54300f37327SSimon Glass 	DUMP_REG(STATE1);
54400f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE0(0));
54500f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE0(1));
54600f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE1(0));
54700f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE1(1));
54800f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE2(0));
54900f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE2(1));
55000f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE3(0));
55100f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE3(1));
55200f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE4(0));
55300f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE4(1));
55400f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE5(0));
55500f37327SSimon Glass 	DUMP_REG(NV_HEAD_STATE5(1));
55600f37327SSimon Glass 	DUMP_REG(CRC_CNTRL);
55700f37327SSimon Glass 	DUMP_REG(CLK_CNTRL);
55800f37327SSimon Glass 	DUMP_REG(CAP);
55900f37327SSimon Glass 	DUMP_REG(PWR);
56000f37327SSimon Glass 	DUMP_REG(TEST);
56100f37327SSimon Glass 	DUMP_REG(PLL0);
56200f37327SSimon Glass 	DUMP_REG(PLL1);
56300f37327SSimon Glass 	DUMP_REG(PLL2);
56400f37327SSimon Glass 	DUMP_REG(PLL3);
56500f37327SSimon Glass 	DUMP_REG(CSTM);
56600f37327SSimon Glass 	DUMP_REG(LVDS);
56700f37327SSimon Glass 	DUMP_REG(CRCA);
56800f37327SSimon Glass 	DUMP_REG(CRCB);
56900f37327SSimon Glass 	DUMP_REG(SEQ_CTL);
57000f37327SSimon Glass 	DUMP_REG(LANE_SEQ_CTL);
57100f37327SSimon Glass 	DUMP_REG(SEQ_INST(0));
57200f37327SSimon Glass 	DUMP_REG(SEQ_INST(1));
57300f37327SSimon Glass 	DUMP_REG(SEQ_INST(2));
57400f37327SSimon Glass 	DUMP_REG(SEQ_INST(3));
57500f37327SSimon Glass 	DUMP_REG(SEQ_INST(4));
57600f37327SSimon Glass 	DUMP_REG(SEQ_INST(5));
57700f37327SSimon Glass 	DUMP_REG(SEQ_INST(6));
57800f37327SSimon Glass 	DUMP_REG(SEQ_INST(7));
57900f37327SSimon Glass 	DUMP_REG(SEQ_INST(8));
58000f37327SSimon Glass 	DUMP_REG(PWM_DIV);
58100f37327SSimon Glass 	DUMP_REG(PWM_CTL);
58200f37327SSimon Glass 	DUMP_REG(MSCHECK);
58300f37327SSimon Glass 	DUMP_REG(XBAR_CTRL);
58400f37327SSimon Glass 	DUMP_REG(DP_LINKCTL(0));
58500f37327SSimon Glass 	DUMP_REG(DP_LINKCTL(1));
58600f37327SSimon Glass 	DUMP_REG(DC(0));
58700f37327SSimon Glass 	DUMP_REG(DC(1));
58800f37327SSimon Glass 	DUMP_REG(LANE_DRIVE_CURRENT(0));
58900f37327SSimon Glass 	DUMP_REG(PR(0));
59000f37327SSimon Glass 	DUMP_REG(LANE4_PREEMPHASIS(0));
59100f37327SSimon Glass 	DUMP_REG(POSTCURSOR(0));
59200f37327SSimon Glass 	DUMP_REG(DP_CONFIG(0));
59300f37327SSimon Glass 	DUMP_REG(DP_CONFIG(1));
59400f37327SSimon Glass 	DUMP_REG(DP_MN(0));
59500f37327SSimon Glass 	DUMP_REG(DP_MN(1));
59600f37327SSimon Glass 	DUMP_REG(DP_PADCTL(0));
59700f37327SSimon Glass 	DUMP_REG(DP_PADCTL(1));
59800f37327SSimon Glass 	DUMP_REG(DP_DEBUG(0));
59900f37327SSimon Glass 	DUMP_REG(DP_DEBUG(1));
60000f37327SSimon Glass 	DUMP_REG(DP_SPARE(0));
60100f37327SSimon Glass 	DUMP_REG(DP_SPARE(1));
60200f37327SSimon Glass 	DUMP_REG(DP_TPG);
60300f37327SSimon Glass 
60400f37327SSimon Glass 	return;
60500f37327SSimon Glass }
60600f37327SSimon Glass #endif
60700f37327SSimon Glass 
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)60800f37327SSimon Glass static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor,
60900f37327SSimon Glass 			int is_lvds,
61000f37327SSimon Glass 			const struct tegra_dp_link_config *link_cfg,
61100f37327SSimon Glass 			const struct display_timing *timing)
61200f37327SSimon Glass {
61300f37327SSimon Glass 	const int	head_num = 0;
61400f37327SSimon Glass 	u32		reg_val	 = STATE1_ASY_OWNER_HEAD0 << head_num;
61500f37327SSimon Glass 	u32		vtotal, htotal;
61600f37327SSimon Glass 	u32		vsync_end, hsync_end;
61700f37327SSimon Glass 	u32		vblank_end, hblank_end;
61800f37327SSimon Glass 	u32		vblank_start, hblank_start;
61900f37327SSimon Glass 
62000f37327SSimon Glass 	reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM :
62100f37327SSimon Glass 		STATE1_ASY_PROTOCOL_DP_A;
62200f37327SSimon Glass 	reg_val |= STATE1_ASY_SUBOWNER_NONE |
62300f37327SSimon Glass 		STATE1_ASY_CRCMODE_COMPLETE_RASTER;
62400f37327SSimon Glass 
62500f37327SSimon Glass 	reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE;
62600f37327SSimon Glass 	reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE;
62700f37327SSimon Glass 	reg_val |= (link_cfg->bits_per_pixel > 18) ?
62800f37327SSimon Glass 		STATE1_ASY_PIXELDEPTH_BPP_24_444 :
62900f37327SSimon Glass 		STATE1_ASY_PIXELDEPTH_BPP_18_444;
63000f37327SSimon Glass 
63100f37327SSimon Glass 	tegra_sor_writel(sor, STATE1, reg_val);
63200f37327SSimon Glass 
63300f37327SSimon Glass 	/*
63400f37327SSimon Glass 	 * Skipping programming NV_HEAD_STATE0, assuming:
63500f37327SSimon Glass 	 * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB
63600f37327SSimon Glass 	 */
63700f37327SSimon Glass 	vtotal = timing->vsync_len.typ + timing->vback_porch.typ +
63800f37327SSimon Glass 		timing->vactive.typ + timing->vfront_porch.typ;
63900f37327SSimon Glass 	htotal = timing->hsync_len.typ + timing->hback_porch.typ +
64000f37327SSimon Glass 		timing->hactive.typ + timing->hfront_porch.typ;
64100f37327SSimon Glass 
64200f37327SSimon Glass 	tegra_sor_writel(sor, NV_HEAD_STATE1(head_num),
64300f37327SSimon Glass 			 vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT |
64400f37327SSimon Glass 			 htotal << NV_HEAD_STATE1_HTOTAL_SHIFT);
64500f37327SSimon Glass 
64600f37327SSimon Glass 	vsync_end = timing->vsync_len.typ - 1;
64700f37327SSimon Glass 	hsync_end = timing->hsync_len.typ - 1;
64800f37327SSimon Glass 	tegra_sor_writel(sor, NV_HEAD_STATE2(head_num),
64900f37327SSimon Glass 			 vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT |
65000f37327SSimon Glass 			 hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT);
65100f37327SSimon Glass 
65200f37327SSimon Glass 	vblank_end = vsync_end + timing->vback_porch.typ;
65300f37327SSimon Glass 	hblank_end = hsync_end + timing->hback_porch.typ;
65400f37327SSimon Glass 	tegra_sor_writel(sor, NV_HEAD_STATE3(head_num),
65500f37327SSimon Glass 			 vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT |
65600f37327SSimon Glass 			 hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT);
65700f37327SSimon Glass 
65800f37327SSimon Glass 	vblank_start = vblank_end + timing->vactive.typ;
65900f37327SSimon Glass 	hblank_start = hblank_end + timing->hactive.typ;
66000f37327SSimon Glass 	tegra_sor_writel(sor, NV_HEAD_STATE4(head_num),
66100f37327SSimon Glass 			 vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT |
66200f37327SSimon Glass 			 hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT);
66300f37327SSimon Glass 
66400f37327SSimon Glass 	/* TODO: adding interlace mode support */
66500f37327SSimon Glass 	tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1);
66600f37327SSimon Glass 
66700f37327SSimon Glass 	tegra_sor_write_field(sor, CSTM,
66800f37327SSimon Glass 			      CSTM_ROTCLK_DEFAULT_MASK |
66900f37327SSimon Glass 			      CSTM_LVDS_EN_ENABLE,
67000f37327SSimon Glass 			      2 << CSTM_ROTCLK_SHIFT |
67100f37327SSimon Glass 			      is_lvds ? CSTM_LVDS_EN_ENABLE :
67200f37327SSimon Glass 			      CSTM_LVDS_EN_DISABLE);
67300f37327SSimon Glass 
67400f37327SSimon Glass 	 tegra_dc_sor_config_pwm(sor, 1024, 1024);
67500f37327SSimon Glass }
67600f37327SSimon Glass 
tegra_dc_sor_enable_dc(struct dc_ctlr * disp_ctrl)67700f37327SSimon Glass static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
67800f37327SSimon Glass {
67900f37327SSimon Glass 	u32 reg_val = readl(&disp_ctrl->cmd.state_access);
68000f37327SSimon Glass 
68100f37327SSimon Glass 	writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
68200f37327SSimon Glass 	writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt);
68300f37327SSimon Glass 
68400f37327SSimon Glass 	/* Enable DC now - otherwise pure text console may not show. */
68500f37327SSimon Glass 	writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
68600f37327SSimon Glass 	       &disp_ctrl->cmd.disp_cmd);
68700f37327SSimon Glass 	writel(reg_val, &disp_ctrl->cmd.state_access);
68800f37327SSimon Glass }
68900f37327SSimon Glass 
tegra_dc_sor_enable_dp(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)690d7659212SSimon Glass int tegra_dc_sor_enable_dp(struct udevice *dev,
69100f37327SSimon Glass 			   const struct tegra_dp_link_config *link_cfg)
69200f37327SSimon Glass {
693d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
69400f37327SSimon Glass 	int ret;
69500f37327SSimon Glass 
69600f37327SSimon Glass 	tegra_sor_write_field(sor, CLK_CNTRL,
69700f37327SSimon Glass 			      CLK_CNTRL_DP_CLK_SEL_MASK,
69800f37327SSimon Glass 			      CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK);
69900f37327SSimon Glass 
70000f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
70100f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
70200f37327SSimon Glass 			      PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
70300f37327SSimon Glass 	udelay(25);
70400f37327SSimon Glass 
70500f37327SSimon Glass 	tegra_sor_write_field(sor, PLL3,
70600f37327SSimon Glass 			      PLL3_PLLVDD_MODE_MASK,
70700f37327SSimon Glass 			      PLL3_PLLVDD_MODE_V3_3);
70800f37327SSimon Glass 	tegra_sor_writel(sor, PLL0,
70900f37327SSimon Glass 			 0xf << PLL0_ICHPMP_SHFIT |
71000f37327SSimon Glass 			 0x3 << PLL0_VCOCAP_SHIFT |
71100f37327SSimon Glass 			 PLL0_PLLREG_LEVEL_V45 |
71200f37327SSimon Glass 			 PLL0_RESISTORSEL_EXT |
71300f37327SSimon Glass 			 PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
71400f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2,
71500f37327SSimon Glass 			      PLL2_AUX1_SEQ_MASK |
71600f37327SSimon Glass 			      PLL2_AUX9_LVDSEN_OVERRIDE |
71700f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
71800f37327SSimon Glass 			      PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE |
71900f37327SSimon Glass 			      PLL2_AUX9_LVDSEN_OVERRIDE |
72000f37327SSimon Glass 			      PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
72100f37327SSimon Glass 	tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH |
72200f37327SSimon Glass 			 PLL1_TMDS_TERM_ENABLE);
72300f37327SSimon Glass 
72400f37327SSimon Glass 	if (tegra_dc_sor_poll_register(sor, PLL2,
72500f37327SSimon Glass 				       PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
72600f37327SSimon Glass 				       PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE,
72700f37327SSimon Glass 				       100, TEGRA_SOR_TIMEOUT_MS)) {
72800f37327SSimon Glass 		printf("DP failed to lock PLL\n");
72900f37327SSimon Glass 		return -EIO;
73000f37327SSimon Glass 	}
73100f37327SSimon Glass 
73200f37327SSimon Glass 	tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK |
73300f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_MASK,
73400f37327SSimon Glass 			      PLL2_AUX2_OVERRIDE_POWERDOWN |
73500f37327SSimon Glass 			      PLL2_AUX7_PORT_POWERDOWN_DISABLE);
73600f37327SSimon Glass 
737d7659212SSimon Glass 	ret = tegra_dc_sor_power_up(dev, 0);
73800f37327SSimon Glass 	if (ret) {
73900f37327SSimon Glass 		debug("DP failed to power up\n");
74000f37327SSimon Glass 		return ret;
74100f37327SSimon Glass 	}
74200f37327SSimon Glass 
74300f37327SSimon Glass 	/* re-enable SOR clock */
74400f37327SSimon Glass 	clock_sor_enable_edp_clock();
74500f37327SSimon Glass 
74600f37327SSimon Glass 	/* Power up lanes */
747d7659212SSimon Glass 	tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1);
74800f37327SSimon Glass 
749d7659212SSimon Glass 	tegra_dc_sor_set_dp_mode(dev, link_cfg);
75000f37327SSimon Glass 	debug("%s ret\n", __func__);
75100f37327SSimon Glass 
75200f37327SSimon Glass 	return 0;
75300f37327SSimon Glass }
75400f37327SSimon Glass 
tegra_dc_sor_attach(struct udevice * dc_dev,struct udevice * dev,const struct tegra_dp_link_config * link_cfg,const struct display_timing * timing)755d7659212SSimon Glass int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev,
75600f37327SSimon Glass 			const struct tegra_dp_link_config *link_cfg,
75700f37327SSimon Glass 			const struct display_timing *timing)
75800f37327SSimon Glass {
759d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
76000f37327SSimon Glass 	struct dc_ctlr *disp_ctrl;
76100f37327SSimon Glass 	u32 reg_val;
76200f37327SSimon Glass 
76300f37327SSimon Glass 	/* Use the first display controller */
76400f37327SSimon Glass 	debug("%s\n", __func__);
765079ff3b9SSimon Glass 	disp_ctrl = (struct dc_ctlr *)dev_read_addr(dc_dev);
76600f37327SSimon Glass 
76700f37327SSimon Glass 	tegra_dc_sor_enable_dc(disp_ctrl);
76800f37327SSimon Glass 	tegra_dc_sor_config_panel(sor, 0, link_cfg, timing);
76900f37327SSimon Glass 
77000f37327SSimon Glass 	writel(0x9f00, &disp_ctrl->cmd.state_ctrl);
77100f37327SSimon Glass 	writel(0x9f, &disp_ctrl->cmd.state_ctrl);
77200f37327SSimon Glass 
77300f37327SSimon Glass 	writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
77400f37327SSimon Glass 	       PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
77500f37327SSimon Glass 	       &disp_ctrl->cmd.disp_pow_ctrl);
77600f37327SSimon Glass 
77700f37327SSimon Glass 	reg_val = tegra_sor_readl(sor, TEST);
77800f37327SSimon Glass 	if (reg_val & TEST_ATTACHED_TRUE)
77900f37327SSimon Glass 		return -EEXIST;
78000f37327SSimon Glass 
78100f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE1,
78200f37327SSimon Glass 			 SUPER_STATE1_ATTACHED_NO);
78300f37327SSimon Glass 
78400f37327SSimon Glass 	/*
78500f37327SSimon Glass 	 * Enable display2sor clock at least 2 cycles before DC start,
78600f37327SSimon Glass 	 * to clear sor internal valid signal.
78700f37327SSimon Glass 	 */
78800f37327SSimon Glass 	writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
78900f37327SSimon Glass 	writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
79000f37327SSimon Glass 	writel(0, &disp_ctrl->disp.disp_win_opt);
79100f37327SSimon Glass 	writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
79200f37327SSimon Glass 
79300f37327SSimon Glass 	/* Attach head */
79400f37327SSimon Glass 	tegra_dc_sor_update(sor);
79500f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE1,
79600f37327SSimon Glass 			 SUPER_STATE1_ATTACHED_YES);
79700f37327SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE1,
79800f37327SSimon Glass 			 SUPER_STATE1_ATTACHED_YES |
79900f37327SSimon Glass 			 SUPER_STATE1_ASY_HEAD_OP_AWAKE |
80000f37327SSimon Glass 			 SUPER_STATE1_ASY_ORMODE_NORMAL);
80100f37327SSimon Glass 	tegra_dc_sor_super_update(sor);
80200f37327SSimon Glass 
80300f37327SSimon Glass 	/* Enable dc */
80400f37327SSimon Glass 	reg_val = readl(&disp_ctrl->cmd.state_access);
80500f37327SSimon Glass 	writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
80600f37327SSimon Glass 	writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
80700f37327SSimon Glass 	       &disp_ctrl->cmd.disp_cmd);
80800f37327SSimon Glass 	writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
80900f37327SSimon Glass 	writel(reg_val, &disp_ctrl->cmd.state_access);
81000f37327SSimon Glass 
81100f37327SSimon Glass 	if (tegra_dc_sor_poll_register(sor, TEST,
81200f37327SSimon Glass 				       TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
81300f37327SSimon Glass 				       TEST_ACT_HEAD_OPMODE_AWAKE,
81400f37327SSimon Glass 				       100,
81500f37327SSimon Glass 				       TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
81600f37327SSimon Glass 		printf("dc timeout waiting for OPMOD = AWAKE\n");
81700f37327SSimon Glass 		return -ETIMEDOUT;
81800f37327SSimon Glass 	} else {
81900f37327SSimon Glass 		debug("%s: sor is attached\n", __func__);
82000f37327SSimon Glass 	}
82100f37327SSimon Glass 
82200f37327SSimon Glass #if DEBUG_SOR
82300f37327SSimon Glass 	dump_sor_reg(sor);
82400f37327SSimon Glass #endif
82500f37327SSimon Glass 	debug("%s: ret=%d\n", __func__, 0);
82600f37327SSimon Glass 
82700f37327SSimon Glass 	return 0;
82800f37327SSimon Glass }
82900f37327SSimon Glass 
tegra_dc_sor_set_lane_parm(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)830d7659212SSimon Glass void tegra_dc_sor_set_lane_parm(struct udevice *dev,
83100f37327SSimon Glass 		const struct tegra_dp_link_config *link_cfg)
83200f37327SSimon Glass {
833d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
834d7659212SSimon Glass 
83500f37327SSimon Glass 	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
83600f37327SSimon Glass 			 link_cfg->drive_current);
83700f37327SSimon Glass 	tegra_sor_writel(sor, PR(sor->portnum),
83800f37327SSimon Glass 			 link_cfg->preemphasis);
83900f37327SSimon Glass 	tegra_sor_writel(sor, POSTCURSOR(sor->portnum),
84000f37327SSimon Glass 			 link_cfg->postcursor);
84100f37327SSimon Glass 	tegra_sor_writel(sor, LVDS, 0);
84200f37327SSimon Glass 
843d7659212SSimon Glass 	tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
844d7659212SSimon Glass 	tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count);
84500f37327SSimon Glass 
84600f37327SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
84700f37327SSimon Glass 			      DP_PADCTL_TX_PU_ENABLE |
84800f37327SSimon Glass 			      DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
84900f37327SSimon Glass 			      DP_PADCTL_TX_PU_ENABLE |
85000f37327SSimon Glass 			      2 << DP_PADCTL_TX_PU_VALUE_SHIFT);
85100f37327SSimon Glass 
85200f37327SSimon Glass 	/* Precharge */
85300f37327SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0);
85400f37327SSimon Glass 	udelay(20);
85500f37327SSimon Glass 
85600f37327SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
85700f37327SSimon Glass }
85800f37327SSimon Glass 
tegra_dc_sor_set_voltage_swing(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)859d7659212SSimon Glass int tegra_dc_sor_set_voltage_swing(struct udevice *dev,
860dedc44b4SSimon Glass 				    const struct tegra_dp_link_config *link_cfg)
861dedc44b4SSimon Glass {
862d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
863dedc44b4SSimon Glass 	u32 drive_current = 0;
864dedc44b4SSimon Glass 	u32 pre_emphasis = 0;
865dedc44b4SSimon Glass 
866dedc44b4SSimon Glass 	/* Set to a known-good pre-calibrated setting */
867dedc44b4SSimon Glass 	switch (link_cfg->link_bw) {
868dedc44b4SSimon Glass 	case SOR_LINK_SPEED_G1_62:
869dedc44b4SSimon Glass 	case SOR_LINK_SPEED_G2_7:
870dedc44b4SSimon Glass 		drive_current = 0x13131313;
871dedc44b4SSimon Glass 		pre_emphasis = 0;
872dedc44b4SSimon Glass 		break;
873dedc44b4SSimon Glass 	case SOR_LINK_SPEED_G5_4:
874dedc44b4SSimon Glass 		debug("T124 does not support 5.4G link clock.\n");
875dedc44b4SSimon Glass 	default:
876dedc44b4SSimon Glass 		debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
877dedc44b4SSimon Glass 		return -ENOLINK;
878dedc44b4SSimon Glass 	}
879dedc44b4SSimon Glass 
880dedc44b4SSimon Glass 	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
881dedc44b4SSimon Glass 	tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
882dedc44b4SSimon Glass 
883dedc44b4SSimon Glass 	return 0;
884dedc44b4SSimon Glass }
885dedc44b4SSimon Glass 
tegra_dc_sor_power_down_unused_lanes(struct udevice * dev,const struct tegra_dp_link_config * link_cfg)886d7659212SSimon Glass void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev,
88700f37327SSimon Glass 			const struct tegra_dp_link_config *link_cfg)
88800f37327SSimon Glass {
889d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
89000f37327SSimon Glass 	u32 pad_ctrl = 0;
89100f37327SSimon Glass 	int err = 0;
89200f37327SSimon Glass 
89300f37327SSimon Glass 	switch (link_cfg->lane_count) {
89400f37327SSimon Glass 	case 4:
89500f37327SSimon Glass 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
89600f37327SSimon Glass 			DP_PADCTL_PD_TXD_1_NO |
89700f37327SSimon Glass 			DP_PADCTL_PD_TXD_2_NO |
89800f37327SSimon Glass 			DP_PADCTL_PD_TXD_3_NO;
89900f37327SSimon Glass 		break;
90000f37327SSimon Glass 	case 2:
90100f37327SSimon Glass 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
90200f37327SSimon Glass 			DP_PADCTL_PD_TXD_1_NO |
90300f37327SSimon Glass 			DP_PADCTL_PD_TXD_2_YES |
90400f37327SSimon Glass 			DP_PADCTL_PD_TXD_3_YES;
90500f37327SSimon Glass 		break;
90600f37327SSimon Glass 	case 1:
90700f37327SSimon Glass 		pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
90800f37327SSimon Glass 			DP_PADCTL_PD_TXD_1_YES |
90900f37327SSimon Glass 			DP_PADCTL_PD_TXD_2_YES |
91000f37327SSimon Glass 			DP_PADCTL_PD_TXD_3_YES;
91100f37327SSimon Glass 		break;
91200f37327SSimon Glass 	default:
91300f37327SSimon Glass 		printf("Invalid sor lane count: %u\n", link_cfg->lane_count);
91400f37327SSimon Glass 		return;
91500f37327SSimon Glass 	}
91600f37327SSimon Glass 
91700f37327SSimon Glass 	pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN;
91800f37327SSimon Glass 	tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl);
91900f37327SSimon Glass 
92000f37327SSimon Glass 	err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0);
92100f37327SSimon Glass 	if (err) {
92200f37327SSimon Glass 		debug("Wait for lane power down failed: %d\n", err);
92300f37327SSimon Glass 		return;
92400f37327SSimon Glass 	}
92500f37327SSimon Glass }
92600f37327SSimon Glass 
tegra_sor_precharge_lanes(struct udevice * dev,const struct tegra_dp_link_config * cfg)927d7659212SSimon Glass int tegra_sor_precharge_lanes(struct udevice *dev,
928dedc44b4SSimon Glass 			      const struct tegra_dp_link_config *cfg)
929dedc44b4SSimon Glass {
930d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
931dedc44b4SSimon Glass 	u32 val = 0;
932dedc44b4SSimon Glass 
933dedc44b4SSimon Glass 	switch (cfg->lane_count) {
934dedc44b4SSimon Glass 	case 4:
935dedc44b4SSimon Glass 		val |= (DP_PADCTL_PD_TXD_3_NO |
936dedc44b4SSimon Glass 			DP_PADCTL_PD_TXD_2_NO);
937dedc44b4SSimon Glass 		/* fall through */
938dedc44b4SSimon Glass 	case 2:
939dedc44b4SSimon Glass 		val |= DP_PADCTL_PD_TXD_1_NO;
940dedc44b4SSimon Glass 		/* fall through */
941dedc44b4SSimon Glass 	case 1:
942dedc44b4SSimon Glass 		val |= DP_PADCTL_PD_TXD_0_NO;
943dedc44b4SSimon Glass 		break;
944dedc44b4SSimon Glass 	default:
945dedc44b4SSimon Glass 		debug("dp: invalid lane number %d\n", cfg->lane_count);
946dedc44b4SSimon Glass 		return -EINVAL;
947dedc44b4SSimon Glass 	}
948dedc44b4SSimon Glass 
949dedc44b4SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
950dedc44b4SSimon Glass 			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
951dedc44b4SSimon Glass 			      (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
952dedc44b4SSimon Glass 	udelay(100);
953dedc44b4SSimon Glass 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
954dedc44b4SSimon Glass 			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
955dedc44b4SSimon Glass 			      0);
956dedc44b4SSimon Glass 
957dedc44b4SSimon Glass 	return 0;
958dedc44b4SSimon Glass }
959dedc44b4SSimon Glass 
tegra_dc_sor_enable_sor(struct dc_ctlr * disp_ctrl,bool enable)960dedc44b4SSimon Glass static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
961dedc44b4SSimon Glass {
962dedc44b4SSimon Glass 	u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
963dedc44b4SSimon Glass 
964dedc44b4SSimon Glass 	reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
965dedc44b4SSimon Glass 	writel(reg_val, &disp_ctrl->disp.disp_win_opt);
966dedc44b4SSimon Glass }
967dedc44b4SSimon Glass 
tegra_dc_sor_detach(struct udevice * dc_dev,struct udevice * dev)968d7659212SSimon Glass int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev)
969dedc44b4SSimon Glass {
970d7659212SSimon Glass 	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
971dedc44b4SSimon Glass 	int dc_reg_ctx[DC_REG_SAVE_SPACE];
972dedc44b4SSimon Glass 	struct dc_ctlr *disp_ctrl;
973dedc44b4SSimon Glass 	unsigned long dc_int_mask;
974dedc44b4SSimon Glass 	int ret;
975dedc44b4SSimon Glass 
976dedc44b4SSimon Glass 	debug("%s\n", __func__);
977dedc44b4SSimon Glass 	/* Use the first display controller */
978079ff3b9SSimon Glass 	disp_ctrl = (struct dc_ctlr *)dev_read_addr(dev);
979dedc44b4SSimon Glass 
980dedc44b4SSimon Glass 	/* Sleep mode */
981dedc44b4SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
982dedc44b4SSimon Glass 			 SUPER_STATE1_ASY_ORMODE_SAFE |
983dedc44b4SSimon Glass 			 SUPER_STATE1_ATTACHED_YES);
984dedc44b4SSimon Glass 	tegra_dc_sor_super_update(sor);
985dedc44b4SSimon Glass 
986dedc44b4SSimon Glass 	tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
987dedc44b4SSimon Glass 
988dedc44b4SSimon Glass 	if (tegra_dc_sor_poll_register(sor, TEST,
989dedc44b4SSimon Glass 				       TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
990dedc44b4SSimon Glass 				       TEST_ACT_HEAD_OPMODE_SLEEP, 100,
991dedc44b4SSimon Glass 				       TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
992dedc44b4SSimon Glass 		debug("dc timeout waiting for OPMOD = SLEEP\n");
993dedc44b4SSimon Glass 		ret = -ETIMEDOUT;
994dedc44b4SSimon Glass 		goto err;
995dedc44b4SSimon Glass 	}
996dedc44b4SSimon Glass 
997dedc44b4SSimon Glass 	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
998dedc44b4SSimon Glass 			 SUPER_STATE1_ASY_ORMODE_SAFE |
999dedc44b4SSimon Glass 			 SUPER_STATE1_ATTACHED_NO);
1000dedc44b4SSimon Glass 
1001dedc44b4SSimon Glass 	/* Mask DC interrupts during the 2 dummy frames required for detach */
1002dedc44b4SSimon Glass 	dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
1003dedc44b4SSimon Glass 	writel(0, &disp_ctrl->cmd.int_mask);
1004dedc44b4SSimon Glass 
1005dedc44b4SSimon Glass 	/* Stop DC->SOR path */
1006dedc44b4SSimon Glass 	tegra_dc_sor_enable_sor(disp_ctrl, false);
1007dedc44b4SSimon Glass 	ret = tegra_dc_sor_general_act(disp_ctrl);
1008dedc44b4SSimon Glass 	if (ret)
1009dedc44b4SSimon Glass 		goto err;
1010dedc44b4SSimon Glass 
1011dedc44b4SSimon Glass 	/* Stop DC */
1012dedc44b4SSimon Glass 	writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
1013dedc44b4SSimon Glass 	ret = tegra_dc_sor_general_act(disp_ctrl);
1014dedc44b4SSimon Glass 	if (ret)
1015dedc44b4SSimon Glass 		goto err;
1016dedc44b4SSimon Glass 
1017dedc44b4SSimon Glass 	tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
1018dedc44b4SSimon Glass 
1019dedc44b4SSimon Glass 	writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
1020dedc44b4SSimon Glass 
1021dedc44b4SSimon Glass 	return 0;
1022dedc44b4SSimon Glass err:
1023dedc44b4SSimon Glass 	debug("%s: ret=%d\n", __func__, ret);
1024dedc44b4SSimon Glass 
1025dedc44b4SSimon Glass 	return ret;
102600f37327SSimon Glass }
102700f37327SSimon Glass 
tegra_sor_set_backlight(struct udevice * dev,int percent)1028d7659212SSimon Glass static int tegra_sor_set_backlight(struct udevice *dev, int percent)
102900f37327SSimon Glass {
1030d7659212SSimon Glass 	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
1031d7659212SSimon Glass 	int ret;
103200f37327SSimon Glass 
1033d7659212SSimon Glass 	ret = panel_enable_backlight(priv->panel);
1034d7659212SSimon Glass 	if (ret) {
1035d7659212SSimon Glass 		debug("sor: Cannot enable panel backlight\n");
1036d7659212SSimon Glass 		return ret;
1037d7659212SSimon Glass 	}
103800f37327SSimon Glass 
103900f37327SSimon Glass 	return 0;
104000f37327SSimon Glass }
1041d7659212SSimon Glass 
tegra_sor_ofdata_to_platdata(struct udevice * dev)1042d7659212SSimon Glass static int tegra_sor_ofdata_to_platdata(struct udevice *dev)
1043d7659212SSimon Glass {
1044d7659212SSimon Glass 	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
1045d7659212SSimon Glass 	int ret;
1046d7659212SSimon Glass 
1047079ff3b9SSimon Glass 	priv->base = (void *)dev_read_addr(dev);
1048d7659212SSimon Glass 
1049079ff3b9SSimon Glass 	priv->pmc_base = (void *)syscon_get_first_range(TEGRA_SYSCON_PMC);
1050079ff3b9SSimon Glass 	if (IS_ERR(priv->pmc_base))
1051079ff3b9SSimon Glass 		return PTR_ERR(priv->pmc_base);
1052d7659212SSimon Glass 
1053d7659212SSimon Glass 	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel",
1054d7659212SSimon Glass 					   &priv->panel);
1055d7659212SSimon Glass 	if (ret) {
1056d7659212SSimon Glass 		debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
1057d7659212SSimon Glass 		      dev->name, ret);
1058d7659212SSimon Glass 		return ret;
1059d7659212SSimon Glass 	}
1060d7659212SSimon Glass 
1061d7659212SSimon Glass 	return 0;
1062d7659212SSimon Glass }
1063d7659212SSimon Glass 
1064d7659212SSimon Glass static const struct video_bridge_ops tegra_sor_ops = {
1065d7659212SSimon Glass 	.set_backlight	= tegra_sor_set_backlight,
1066d7659212SSimon Glass };
1067d7659212SSimon Glass 
1068d7659212SSimon Glass static const struct udevice_id tegra_sor_ids[] = {
1069d7659212SSimon Glass 	{ .compatible = "nvidia,tegra124-sor" },
1070d7659212SSimon Glass 	{ }
1071d7659212SSimon Glass };
1072d7659212SSimon Glass 
1073d7659212SSimon Glass U_BOOT_DRIVER(sor_tegra) = {
1074d7659212SSimon Glass 	.name	= "sor_tegra",
1075d7659212SSimon Glass 	.id	= UCLASS_VIDEO_BRIDGE,
1076d7659212SSimon Glass 	.of_match = tegra_sor_ids,
1077d7659212SSimon Glass 	.ofdata_to_platdata = tegra_sor_ofdata_to_platdata,
1078d7659212SSimon Glass 	.ops	= &tegra_sor_ops,
1079d7659212SSimon Glass 	.priv_auto_alloc_size = sizeof(struct tegra_dc_sor_data),
1080d7659212SSimon Glass };
1081