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