xref: /openbmc/linux/drivers/gpu/drm/tegra/sor.c (revision 6b6b604215c64666fbf0fed939a5c312cc7b12fe)
1*6b6b6042SThierry Reding /*
2*6b6b6042SThierry Reding  * Copyright (C) 2013 NVIDIA Corporation
3*6b6b6042SThierry Reding  *
4*6b6b6042SThierry Reding  * This program is free software; you can redistribute it and/or modify
5*6b6b6042SThierry Reding  * it under the terms of the GNU General Public License version 2 as
6*6b6b6042SThierry Reding  * published by the Free Software Foundation.
7*6b6b6042SThierry Reding  */
8*6b6b6042SThierry Reding 
9*6b6b6042SThierry Reding #include <linux/clk.h>
10*6b6b6042SThierry Reding #include <linux/io.h>
11*6b6b6042SThierry Reding #include <linux/platform_device.h>
12*6b6b6042SThierry Reding #include <linux/reset.h>
13*6b6b6042SThierry Reding #include <linux/tegra-powergate.h>
14*6b6b6042SThierry Reding 
15*6b6b6042SThierry Reding #include <drm/drm_dp_helper.h>
16*6b6b6042SThierry Reding 
17*6b6b6042SThierry Reding #include "dc.h"
18*6b6b6042SThierry Reding #include "drm.h"
19*6b6b6042SThierry Reding #include "sor.h"
20*6b6b6042SThierry Reding 
21*6b6b6042SThierry Reding struct tegra_sor {
22*6b6b6042SThierry Reding 	struct host1x_client client;
23*6b6b6042SThierry Reding 	struct tegra_output output;
24*6b6b6042SThierry Reding 	struct device *dev;
25*6b6b6042SThierry Reding 
26*6b6b6042SThierry Reding 	void __iomem *regs;
27*6b6b6042SThierry Reding 
28*6b6b6042SThierry Reding 	struct reset_control *rst;
29*6b6b6042SThierry Reding 	struct clk *clk_parent;
30*6b6b6042SThierry Reding 	struct clk *clk_safe;
31*6b6b6042SThierry Reding 	struct clk *clk_dp;
32*6b6b6042SThierry Reding 	struct clk *clk;
33*6b6b6042SThierry Reding 
34*6b6b6042SThierry Reding 	struct tegra_dpaux *dpaux;
35*6b6b6042SThierry Reding 
36*6b6b6042SThierry Reding 	bool enabled;
37*6b6b6042SThierry Reding };
38*6b6b6042SThierry Reding 
39*6b6b6042SThierry Reding static inline struct tegra_sor *
40*6b6b6042SThierry Reding host1x_client_to_sor(struct host1x_client *client)
41*6b6b6042SThierry Reding {
42*6b6b6042SThierry Reding 	return container_of(client, struct tegra_sor, client);
43*6b6b6042SThierry Reding }
44*6b6b6042SThierry Reding 
45*6b6b6042SThierry Reding static inline struct tegra_sor *to_sor(struct tegra_output *output)
46*6b6b6042SThierry Reding {
47*6b6b6042SThierry Reding 	return container_of(output, struct tegra_sor, output);
48*6b6b6042SThierry Reding }
49*6b6b6042SThierry Reding 
50*6b6b6042SThierry Reding static inline unsigned long tegra_sor_readl(struct tegra_sor *sor,
51*6b6b6042SThierry Reding 					    unsigned long offset)
52*6b6b6042SThierry Reding {
53*6b6b6042SThierry Reding 	return readl(sor->regs + (offset << 2));
54*6b6b6042SThierry Reding }
55*6b6b6042SThierry Reding 
56*6b6b6042SThierry Reding static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value,
57*6b6b6042SThierry Reding 				    unsigned long offset)
58*6b6b6042SThierry Reding {
59*6b6b6042SThierry Reding 	writel(value, sor->regs + (offset << 2));
60*6b6b6042SThierry Reding }
61*6b6b6042SThierry Reding 
62*6b6b6042SThierry Reding static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
63*6b6b6042SThierry Reding 				   struct drm_dp_link *link)
64*6b6b6042SThierry Reding {
65*6b6b6042SThierry Reding 	unsigned long value;
66*6b6b6042SThierry Reding 	unsigned int i;
67*6b6b6042SThierry Reding 	u8 pattern;
68*6b6b6042SThierry Reding 	int err;
69*6b6b6042SThierry Reding 
70*6b6b6042SThierry Reding 	/* setup lane parameters */
71*6b6b6042SThierry Reding 	value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) |
72*6b6b6042SThierry Reding 		SOR_LANE_DRIVE_CURRENT_LANE2(0x40) |
73*6b6b6042SThierry Reding 		SOR_LANE_DRIVE_CURRENT_LANE1(0x40) |
74*6b6b6042SThierry Reding 		SOR_LANE_DRIVE_CURRENT_LANE0(0x40);
75*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0);
76*6b6b6042SThierry Reding 
77*6b6b6042SThierry Reding 	value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) |
78*6b6b6042SThierry Reding 		SOR_LANE_PREEMPHASIS_LANE2(0x0f) |
79*6b6b6042SThierry Reding 		SOR_LANE_PREEMPHASIS_LANE1(0x0f) |
80*6b6b6042SThierry Reding 		SOR_LANE_PREEMPHASIS_LANE0(0x0f);
81*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0);
82*6b6b6042SThierry Reding 
83*6b6b6042SThierry Reding 	value = SOR_LANE_POST_CURSOR_LANE3(0x00) |
84*6b6b6042SThierry Reding 		SOR_LANE_POST_CURSOR_LANE2(0x00) |
85*6b6b6042SThierry Reding 		SOR_LANE_POST_CURSOR_LANE1(0x00) |
86*6b6b6042SThierry Reding 		SOR_LANE_POST_CURSOR_LANE0(0x00);
87*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0);
88*6b6b6042SThierry Reding 
89*6b6b6042SThierry Reding 	/* disable LVDS mode */
90*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_LVDS);
91*6b6b6042SThierry Reding 
92*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
93*6b6b6042SThierry Reding 	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
94*6b6b6042SThierry Reding 	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
95*6b6b6042SThierry Reding 	value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */
96*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
97*6b6b6042SThierry Reding 
98*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
99*6b6b6042SThierry Reding 	value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
100*6b6b6042SThierry Reding 		 SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0;
101*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
102*6b6b6042SThierry Reding 
103*6b6b6042SThierry Reding 	usleep_range(10, 100);
104*6b6b6042SThierry Reding 
105*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
106*6b6b6042SThierry Reding 	value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
107*6b6b6042SThierry Reding 		   SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
108*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
109*6b6b6042SThierry Reding 
110*6b6b6042SThierry Reding 	err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B);
111*6b6b6042SThierry Reding 	if (err < 0)
112*6b6b6042SThierry Reding 		return err;
113*6b6b6042SThierry Reding 
114*6b6b6042SThierry Reding 	for (i = 0, value = 0; i < link->num_lanes; i++) {
115*6b6b6042SThierry Reding 		unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
116*6b6b6042SThierry Reding 				     SOR_DP_TPG_SCRAMBLER_NONE |
117*6b6b6042SThierry Reding 				     SOR_DP_TPG_PATTERN_TRAIN1;
118*6b6b6042SThierry Reding 		value = (value << 8) | lane;
119*6b6b6042SThierry Reding 	}
120*6b6b6042SThierry Reding 
121*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_TPG);
122*6b6b6042SThierry Reding 
123*6b6b6042SThierry Reding 	pattern = DP_TRAINING_PATTERN_1;
124*6b6b6042SThierry Reding 
125*6b6b6042SThierry Reding 	err = tegra_dpaux_train(sor->dpaux, link, pattern);
126*6b6b6042SThierry Reding 	if (err < 0)
127*6b6b6042SThierry Reding 		return err;
128*6b6b6042SThierry Reding 
129*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_SPARE_0);
130*6b6b6042SThierry Reding 	value |= SOR_DP_SPARE_SEQ_ENABLE;
131*6b6b6042SThierry Reding 	value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
132*6b6b6042SThierry Reding 	value |= SOR_DP_SPARE_MACRO_SOR_CLK;
133*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_SPARE_0);
134*6b6b6042SThierry Reding 
135*6b6b6042SThierry Reding 	for (i = 0, value = 0; i < link->num_lanes; i++) {
136*6b6b6042SThierry Reding 		unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
137*6b6b6042SThierry Reding 				     SOR_DP_TPG_SCRAMBLER_NONE |
138*6b6b6042SThierry Reding 				     SOR_DP_TPG_PATTERN_TRAIN2;
139*6b6b6042SThierry Reding 		value = (value << 8) | lane;
140*6b6b6042SThierry Reding 	}
141*6b6b6042SThierry Reding 
142*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_TPG);
143*6b6b6042SThierry Reding 
144*6b6b6042SThierry Reding 	pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2;
145*6b6b6042SThierry Reding 
146*6b6b6042SThierry Reding 	err = tegra_dpaux_train(sor->dpaux, link, pattern);
147*6b6b6042SThierry Reding 	if (err < 0)
148*6b6b6042SThierry Reding 		return err;
149*6b6b6042SThierry Reding 
150*6b6b6042SThierry Reding 	for (i = 0, value = 0; i < link->num_lanes; i++) {
151*6b6b6042SThierry Reding 		unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
152*6b6b6042SThierry Reding 				     SOR_DP_TPG_SCRAMBLER_GALIOS |
153*6b6b6042SThierry Reding 				     SOR_DP_TPG_PATTERN_NONE;
154*6b6b6042SThierry Reding 		value = (value << 8) | lane;
155*6b6b6042SThierry Reding 	}
156*6b6b6042SThierry Reding 
157*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_TPG);
158*6b6b6042SThierry Reding 
159*6b6b6042SThierry Reding 	pattern = DP_TRAINING_PATTERN_DISABLE;
160*6b6b6042SThierry Reding 
161*6b6b6042SThierry Reding 	err = tegra_dpaux_train(sor->dpaux, link, pattern);
162*6b6b6042SThierry Reding 	if (err < 0)
163*6b6b6042SThierry Reding 		return err;
164*6b6b6042SThierry Reding 
165*6b6b6042SThierry Reding 	return 0;
166*6b6b6042SThierry Reding }
167*6b6b6042SThierry Reding 
168*6b6b6042SThierry Reding static void tegra_sor_super_update(struct tegra_sor *sor)
169*6b6b6042SThierry Reding {
170*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0);
171*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0);
172*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0);
173*6b6b6042SThierry Reding }
174*6b6b6042SThierry Reding 
175*6b6b6042SThierry Reding static void tegra_sor_update(struct tegra_sor *sor)
176*6b6b6042SThierry Reding {
177*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_STATE_0);
178*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 1, SOR_STATE_0);
179*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_STATE_0);
180*6b6b6042SThierry Reding }
181*6b6b6042SThierry Reding 
182*6b6b6042SThierry Reding static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout)
183*6b6b6042SThierry Reding {
184*6b6b6042SThierry Reding 	unsigned long value;
185*6b6b6042SThierry Reding 
186*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PWM_DIV);
187*6b6b6042SThierry Reding 	value &= ~SOR_PWM_DIV_MASK;
188*6b6b6042SThierry Reding 	value |= 0x400; /* period */
189*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PWM_DIV);
190*6b6b6042SThierry Reding 
191*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PWM_CTL);
192*6b6b6042SThierry Reding 	value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK;
193*6b6b6042SThierry Reding 	value |= 0x400; /* duty cycle */
194*6b6b6042SThierry Reding 	value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */
195*6b6b6042SThierry Reding 	value |= SOR_PWM_CTL_TRIGGER;
196*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PWM_CTL);
197*6b6b6042SThierry Reding 
198*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
199*6b6b6042SThierry Reding 
200*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
201*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_PWM_CTL);
202*6b6b6042SThierry Reding 		if ((value & SOR_PWM_CTL_TRIGGER) == 0)
203*6b6b6042SThierry Reding 			return 0;
204*6b6b6042SThierry Reding 
205*6b6b6042SThierry Reding 		usleep_range(25, 100);
206*6b6b6042SThierry Reding 	}
207*6b6b6042SThierry Reding 
208*6b6b6042SThierry Reding 	return -ETIMEDOUT;
209*6b6b6042SThierry Reding }
210*6b6b6042SThierry Reding 
211*6b6b6042SThierry Reding static int tegra_sor_attach(struct tegra_sor *sor)
212*6b6b6042SThierry Reding {
213*6b6b6042SThierry Reding 	unsigned long value, timeout;
214*6b6b6042SThierry Reding 
215*6b6b6042SThierry Reding 	/* wake up in normal mode */
216*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
217*6b6b6042SThierry Reding 	value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE;
218*6b6b6042SThierry Reding 	value |= SOR_SUPER_STATE_MODE_NORMAL;
219*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
220*6b6b6042SThierry Reding 	tegra_sor_super_update(sor);
221*6b6b6042SThierry Reding 
222*6b6b6042SThierry Reding 	/* attach */
223*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
224*6b6b6042SThierry Reding 	value |= SOR_SUPER_STATE_ATTACHED;
225*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
226*6b6b6042SThierry Reding 	tegra_sor_super_update(sor);
227*6b6b6042SThierry Reding 
228*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
229*6b6b6042SThierry Reding 
230*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
231*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_TEST);
232*6b6b6042SThierry Reding 		if ((value & SOR_TEST_ATTACHED) != 0)
233*6b6b6042SThierry Reding 			return 0;
234*6b6b6042SThierry Reding 
235*6b6b6042SThierry Reding 		usleep_range(25, 100);
236*6b6b6042SThierry Reding 	}
237*6b6b6042SThierry Reding 
238*6b6b6042SThierry Reding 	return -ETIMEDOUT;
239*6b6b6042SThierry Reding }
240*6b6b6042SThierry Reding 
241*6b6b6042SThierry Reding static int tegra_sor_wakeup(struct tegra_sor *sor)
242*6b6b6042SThierry Reding {
243*6b6b6042SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
244*6b6b6042SThierry Reding 	unsigned long value, timeout;
245*6b6b6042SThierry Reding 
246*6b6b6042SThierry Reding 	/* enable display controller outputs */
247*6b6b6042SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
248*6b6b6042SThierry Reding 	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
249*6b6b6042SThierry Reding 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
250*6b6b6042SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
251*6b6b6042SThierry Reding 
252*6b6b6042SThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
253*6b6b6042SThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
254*6b6b6042SThierry Reding 
255*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
256*6b6b6042SThierry Reding 
257*6b6b6042SThierry Reding 	/* wait for head to wake up */
258*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
259*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_TEST);
260*6b6b6042SThierry Reding 		value &= SOR_TEST_HEAD_MODE_MASK;
261*6b6b6042SThierry Reding 
262*6b6b6042SThierry Reding 		if (value == SOR_TEST_HEAD_MODE_AWAKE)
263*6b6b6042SThierry Reding 			return 0;
264*6b6b6042SThierry Reding 
265*6b6b6042SThierry Reding 		usleep_range(25, 100);
266*6b6b6042SThierry Reding 	}
267*6b6b6042SThierry Reding 
268*6b6b6042SThierry Reding 	return -ETIMEDOUT;
269*6b6b6042SThierry Reding }
270*6b6b6042SThierry Reding 
271*6b6b6042SThierry Reding static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
272*6b6b6042SThierry Reding {
273*6b6b6042SThierry Reding 	unsigned long value;
274*6b6b6042SThierry Reding 
275*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PWR);
276*6b6b6042SThierry Reding 	value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
277*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PWR);
278*6b6b6042SThierry Reding 
279*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
280*6b6b6042SThierry Reding 
281*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
282*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_PWR);
283*6b6b6042SThierry Reding 		if ((value & SOR_PWR_TRIGGER) == 0)
284*6b6b6042SThierry Reding 			return 0;
285*6b6b6042SThierry Reding 
286*6b6b6042SThierry Reding 		usleep_range(25, 100);
287*6b6b6042SThierry Reding 	}
288*6b6b6042SThierry Reding 
289*6b6b6042SThierry Reding 	return -ETIMEDOUT;
290*6b6b6042SThierry Reding }
291*6b6b6042SThierry Reding 
292*6b6b6042SThierry Reding static int tegra_output_sor_enable(struct tegra_output *output)
293*6b6b6042SThierry Reding {
294*6b6b6042SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
295*6b6b6042SThierry Reding 	struct drm_display_mode *mode = &dc->base.mode;
296*6b6b6042SThierry Reding 	unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
297*6b6b6042SThierry Reding 	struct tegra_sor *sor = to_sor(output);
298*6b6b6042SThierry Reding 	unsigned long value;
299*6b6b6042SThierry Reding 	int err;
300*6b6b6042SThierry Reding 
301*6b6b6042SThierry Reding 	if (sor->enabled)
302*6b6b6042SThierry Reding 		return 0;
303*6b6b6042SThierry Reding 
304*6b6b6042SThierry Reding 	err = clk_prepare_enable(sor->clk);
305*6b6b6042SThierry Reding 	if (err < 0)
306*6b6b6042SThierry Reding 		return err;
307*6b6b6042SThierry Reding 
308*6b6b6042SThierry Reding 	reset_control_deassert(sor->rst);
309*6b6b6042SThierry Reding 
310*6b6b6042SThierry Reding 	if (sor->dpaux) {
311*6b6b6042SThierry Reding 		err = tegra_dpaux_enable(sor->dpaux);
312*6b6b6042SThierry Reding 		if (err < 0)
313*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to enable DP: %d\n", err);
314*6b6b6042SThierry Reding 	}
315*6b6b6042SThierry Reding 
316*6b6b6042SThierry Reding 	err = clk_set_parent(sor->clk, sor->clk_safe);
317*6b6b6042SThierry Reding 	if (err < 0)
318*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
319*6b6b6042SThierry Reding 
320*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
321*6b6b6042SThierry Reding 	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
322*6b6b6042SThierry Reding 	value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
323*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
324*6b6b6042SThierry Reding 
325*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
326*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_BANDGAP_POWERDOWN;
327*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
328*6b6b6042SThierry Reding 	usleep_range(20, 100);
329*6b6b6042SThierry Reding 
330*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_3);
331*6b6b6042SThierry Reding 	value |= SOR_PLL_3_PLL_VDD_MODE_V3_3;
332*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_3);
333*6b6b6042SThierry Reding 
334*6b6b6042SThierry Reding 	value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST |
335*6b6b6042SThierry Reding 		SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT;
336*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_0);
337*6b6b6042SThierry Reding 
338*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
339*6b6b6042SThierry Reding 	value |= SOR_PLL_2_SEQ_PLLCAPPD;
340*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
341*6b6b6042SThierry Reding 	value |= SOR_PLL_2_LVDS_ENABLE;
342*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
343*6b6b6042SThierry Reding 
344*6b6b6042SThierry Reding 	value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM;
345*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_1);
346*6b6b6042SThierry Reding 
347*6b6b6042SThierry Reding 	while (true) {
348*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_PLL_2);
349*6b6b6042SThierry Reding 		if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0)
350*6b6b6042SThierry Reding 			break;
351*6b6b6042SThierry Reding 
352*6b6b6042SThierry Reding 		usleep_range(250, 1000);
353*6b6b6042SThierry Reding 	}
354*6b6b6042SThierry Reding 
355*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
356*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE;
357*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_PORT_POWERDOWN;
358*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
359*6b6b6042SThierry Reding 
360*6b6b6042SThierry Reding 	/*
361*6b6b6042SThierry Reding 	 * power up
362*6b6b6042SThierry Reding 	 */
363*6b6b6042SThierry Reding 
364*6b6b6042SThierry Reding 	/* set safe link bandwidth (1.62 Gbps) */
365*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
366*6b6b6042SThierry Reding 	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
367*6b6b6042SThierry Reding 	value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62;
368*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
369*6b6b6042SThierry Reding 
370*6b6b6042SThierry Reding 	/* step 1 */
371*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
372*6b6b6042SThierry Reding 	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN |
373*6b6b6042SThierry Reding 		 SOR_PLL_2_BANDGAP_POWERDOWN;
374*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
375*6b6b6042SThierry Reding 
376*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_0);
377*6b6b6042SThierry Reding 	value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF;
378*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_0);
379*6b6b6042SThierry Reding 
380*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
381*6b6b6042SThierry Reding 	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
382*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
383*6b6b6042SThierry Reding 
384*6b6b6042SThierry Reding 	/* step 2 */
385*6b6b6042SThierry Reding 	err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
386*6b6b6042SThierry Reding 	if (err < 0) {
387*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
388*6b6b6042SThierry Reding 		return err;
389*6b6b6042SThierry Reding 	}
390*6b6b6042SThierry Reding 
391*6b6b6042SThierry Reding 	usleep_range(5, 100);
392*6b6b6042SThierry Reding 
393*6b6b6042SThierry Reding 	/* step 3 */
394*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
395*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_BANDGAP_POWERDOWN;
396*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
397*6b6b6042SThierry Reding 
398*6b6b6042SThierry Reding 	usleep_range(20, 100);
399*6b6b6042SThierry Reding 
400*6b6b6042SThierry Reding 	/* step 4 */
401*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_0);
402*6b6b6042SThierry Reding 	value &= ~SOR_PLL_0_POWER_OFF;
403*6b6b6042SThierry Reding 	value &= ~SOR_PLL_0_VCOPD;
404*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_0);
405*6b6b6042SThierry Reding 
406*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
407*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
408*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
409*6b6b6042SThierry Reding 
410*6b6b6042SThierry Reding 	usleep_range(200, 1000);
411*6b6b6042SThierry Reding 
412*6b6b6042SThierry Reding 	/* step 5 */
413*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
414*6b6b6042SThierry Reding 	value &= ~SOR_PLL_2_PORT_POWERDOWN;
415*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
416*6b6b6042SThierry Reding 
417*6b6b6042SThierry Reding 	/* switch to DP clock */
418*6b6b6042SThierry Reding 	err = clk_set_parent(sor->clk, sor->clk_dp);
419*6b6b6042SThierry Reding 	if (err < 0)
420*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to set DP parent clock: %d\n", err);
421*6b6b6042SThierry Reding 
422*6b6b6042SThierry Reding 	/* power dplanes (XXX parameterize based on link?) */
423*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
424*6b6b6042SThierry Reding 	value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
425*6b6b6042SThierry Reding 		 SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
426*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
427*6b6b6042SThierry Reding 
428*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
429*6b6b6042SThierry Reding 	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
430*6b6b6042SThierry Reding 	value |= SOR_DP_LINKCTL_LANE_COUNT(4);
431*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
432*6b6b6042SThierry Reding 
433*6b6b6042SThierry Reding 	/* start lane sequencer */
434*6b6b6042SThierry Reding 	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
435*6b6b6042SThierry Reding 		SOR_LANE_SEQ_CTL_POWER_STATE_UP;
436*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
437*6b6b6042SThierry Reding 
438*6b6b6042SThierry Reding 	while (true) {
439*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
440*6b6b6042SThierry Reding 		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
441*6b6b6042SThierry Reding 			break;
442*6b6b6042SThierry Reding 
443*6b6b6042SThierry Reding 		usleep_range(250, 1000);
444*6b6b6042SThierry Reding 	}
445*6b6b6042SThierry Reding 
446*6b6b6042SThierry Reding 	/* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */
447*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
448*6b6b6042SThierry Reding 	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
449*6b6b6042SThierry Reding 	value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
450*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
451*6b6b6042SThierry Reding 
452*6b6b6042SThierry Reding 	/* set linkctl */
453*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
454*6b6b6042SThierry Reding 	value |= SOR_DP_LINKCTL_ENABLE;
455*6b6b6042SThierry Reding 
456*6b6b6042SThierry Reding 	value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
457*6b6b6042SThierry Reding 	value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */
458*6b6b6042SThierry Reding 
459*6b6b6042SThierry Reding 	value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
460*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
461*6b6b6042SThierry Reding 
462*6b6b6042SThierry Reding 	for (i = 0, value = 0; i < 4; i++) {
463*6b6b6042SThierry Reding 		unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
464*6b6b6042SThierry Reding 				     SOR_DP_TPG_SCRAMBLER_GALIOS |
465*6b6b6042SThierry Reding 				     SOR_DP_TPG_PATTERN_NONE;
466*6b6b6042SThierry Reding 		value = (value << 8) | lane;
467*6b6b6042SThierry Reding 	}
468*6b6b6042SThierry Reding 
469*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_TPG);
470*6b6b6042SThierry Reding 
471*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_CONFIG_0);
472*6b6b6042SThierry Reding 	value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
473*6b6b6042SThierry Reding 	value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */
474*6b6b6042SThierry Reding 
475*6b6b6042SThierry Reding 	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
476*6b6b6042SThierry Reding 	value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */
477*6b6b6042SThierry Reding 
478*6b6b6042SThierry Reding 	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
479*6b6b6042SThierry Reding 	value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */
480*6b6b6042SThierry Reding 
481*6b6b6042SThierry Reding 	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */
482*6b6b6042SThierry Reding 
483*6b6b6042SThierry Reding 	value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
484*6b6b6042SThierry Reding 	value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */
485*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_CONFIG_0);
486*6b6b6042SThierry Reding 
487*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
488*6b6b6042SThierry Reding 	value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
489*6b6b6042SThierry Reding 	value |= 137; /* XXX: don't hardcode? */
490*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
491*6b6b6042SThierry Reding 
492*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
493*6b6b6042SThierry Reding 	value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
494*6b6b6042SThierry Reding 	value |= 2368; /* XXX: don't hardcode? */
495*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
496*6b6b6042SThierry Reding 
497*6b6b6042SThierry Reding 	/* enable pad calibration logic */
498*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
499*6b6b6042SThierry Reding 	value |= SOR_DP_PADCTL_PAD_CAL_PD;
500*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
501*6b6b6042SThierry Reding 
502*6b6b6042SThierry Reding 	if (sor->dpaux) {
503*6b6b6042SThierry Reding 		/* FIXME: properly convert to struct drm_dp_aux */
504*6b6b6042SThierry Reding 		struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux;
505*6b6b6042SThierry Reding 		struct drm_dp_link link;
506*6b6b6042SThierry Reding 		u8 rate, lanes;
507*6b6b6042SThierry Reding 
508*6b6b6042SThierry Reding 		err = drm_dp_link_probe(aux, &link);
509*6b6b6042SThierry Reding 		if (err < 0) {
510*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to probe eDP link: %d\n",
511*6b6b6042SThierry Reding 				err);
512*6b6b6042SThierry Reding 			return err;
513*6b6b6042SThierry Reding 		}
514*6b6b6042SThierry Reding 
515*6b6b6042SThierry Reding 		err = drm_dp_link_power_up(aux, &link);
516*6b6b6042SThierry Reding 		if (err < 0) {
517*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to power up eDP link: %d\n",
518*6b6b6042SThierry Reding 				err);
519*6b6b6042SThierry Reding 			return err;
520*6b6b6042SThierry Reding 		}
521*6b6b6042SThierry Reding 
522*6b6b6042SThierry Reding 		err = drm_dp_link_configure(aux, &link);
523*6b6b6042SThierry Reding 		if (err < 0) {
524*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to configure eDP link: %d\n",
525*6b6b6042SThierry Reding 				err);
526*6b6b6042SThierry Reding 			return err;
527*6b6b6042SThierry Reding 		}
528*6b6b6042SThierry Reding 
529*6b6b6042SThierry Reding 		rate = drm_dp_link_rate_to_bw_code(link.rate);
530*6b6b6042SThierry Reding 		lanes = link.num_lanes;
531*6b6b6042SThierry Reding 
532*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
533*6b6b6042SThierry Reding 		value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
534*6b6b6042SThierry Reding 		value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate);
535*6b6b6042SThierry Reding 		tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
536*6b6b6042SThierry Reding 
537*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
538*6b6b6042SThierry Reding 		value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
539*6b6b6042SThierry Reding 		value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
540*6b6b6042SThierry Reding 
541*6b6b6042SThierry Reding 		if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
542*6b6b6042SThierry Reding 			value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
543*6b6b6042SThierry Reding 
544*6b6b6042SThierry Reding 		tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
545*6b6b6042SThierry Reding 
546*6b6b6042SThierry Reding 		/* disable training pattern generator */
547*6b6b6042SThierry Reding 
548*6b6b6042SThierry Reding 		for (i = 0; i < link.num_lanes; i++) {
549*6b6b6042SThierry Reding 			unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
550*6b6b6042SThierry Reding 					     SOR_DP_TPG_SCRAMBLER_GALIOS |
551*6b6b6042SThierry Reding 					     SOR_DP_TPG_PATTERN_NONE;
552*6b6b6042SThierry Reding 			value = (value << 8) | lane;
553*6b6b6042SThierry Reding 		}
554*6b6b6042SThierry Reding 
555*6b6b6042SThierry Reding 		tegra_sor_writel(sor, value, SOR_DP_TPG);
556*6b6b6042SThierry Reding 
557*6b6b6042SThierry Reding 		err = tegra_sor_dp_train_fast(sor, &link);
558*6b6b6042SThierry Reding 		if (err < 0) {
559*6b6b6042SThierry Reding 			dev_err(sor->dev, "DP fast link training failed: %d\n",
560*6b6b6042SThierry Reding 				err);
561*6b6b6042SThierry Reding 			return err;
562*6b6b6042SThierry Reding 		}
563*6b6b6042SThierry Reding 
564*6b6b6042SThierry Reding 		dev_dbg(sor->dev, "fast link training succeeded\n");
565*6b6b6042SThierry Reding 	}
566*6b6b6042SThierry Reding 
567*6b6b6042SThierry Reding 	err = tegra_sor_power_up(sor, 250);
568*6b6b6042SThierry Reding 	if (err < 0) {
569*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
570*6b6b6042SThierry Reding 		return err;
571*6b6b6042SThierry Reding 	}
572*6b6b6042SThierry Reding 
573*6b6b6042SThierry Reding 	/* start display controller in continuous mode */
574*6b6b6042SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
575*6b6b6042SThierry Reding 	value |= WRITE_MUX;
576*6b6b6042SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
577*6b6b6042SThierry Reding 
578*6b6b6042SThierry Reding 	tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS);
579*6b6b6042SThierry Reding 	tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
580*6b6b6042SThierry Reding 
581*6b6b6042SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
582*6b6b6042SThierry Reding 	value &= ~WRITE_MUX;
583*6b6b6042SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
584*6b6b6042SThierry Reding 
585*6b6b6042SThierry Reding 	/*
586*6b6b6042SThierry Reding 	 * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
587*6b6b6042SThierry Reding 	 * raster, associate with display controller)
588*6b6b6042SThierry Reding 	 */
589*6b6b6042SThierry Reding 	value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 |
590*6b6b6042SThierry Reding 		SOR_STATE_ASY_VSYNCPOL |
591*6b6b6042SThierry Reding 		SOR_STATE_ASY_HSYNCPOL |
592*6b6b6042SThierry Reding 		SOR_STATE_ASY_PROTOCOL_DP_A |
593*6b6b6042SThierry Reding 		SOR_STATE_ASY_CRC_MODE_COMPLETE |
594*6b6b6042SThierry Reding 		SOR_STATE_ASY_OWNER(dc->pipe + 1);
595*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_STATE_1);
596*6b6b6042SThierry Reding 
597*6b6b6042SThierry Reding 	/*
598*6b6b6042SThierry Reding 	 * TODO: The video timing programming below doesn't seem to match the
599*6b6b6042SThierry Reding 	 * register definitions.
600*6b6b6042SThierry Reding 	 */
601*6b6b6042SThierry Reding 
602*6b6b6042SThierry Reding 	value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
603*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0));
604*6b6b6042SThierry Reding 
605*6b6b6042SThierry Reding 	vse = mode->vsync_end - mode->vsync_start - 1;
606*6b6b6042SThierry Reding 	hse = mode->hsync_end - mode->hsync_start - 1;
607*6b6b6042SThierry Reding 
608*6b6b6042SThierry Reding 	value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
609*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0));
610*6b6b6042SThierry Reding 
611*6b6b6042SThierry Reding 	vbe = vse + (mode->vsync_start - mode->vdisplay);
612*6b6b6042SThierry Reding 	hbe = hse + (mode->hsync_start - mode->hdisplay);
613*6b6b6042SThierry Reding 
614*6b6b6042SThierry Reding 	value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
615*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0));
616*6b6b6042SThierry Reding 
617*6b6b6042SThierry Reding 	vbs = vbe + mode->vdisplay;
618*6b6b6042SThierry Reding 	hbs = hbe + mode->hdisplay;
619*6b6b6042SThierry Reding 
620*6b6b6042SThierry Reding 	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
621*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
622*6b6b6042SThierry Reding 
623*6b6b6042SThierry Reding 	/* XXX interlaced mode */
624*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0));
625*6b6b6042SThierry Reding 
626*6b6b6042SThierry Reding 	/* CSTM (LVDS, link A/B, upper) */
627*6b6b6042SThierry Reding 	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B |
628*6b6b6042SThierry Reding 		SOR_CSTM_UPPER;
629*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_CSTM);
630*6b6b6042SThierry Reding 
631*6b6b6042SThierry Reding 	/* PWM setup */
632*6b6b6042SThierry Reding 	err = tegra_sor_setup_pwm(sor, 250);
633*6b6b6042SThierry Reding 	if (err < 0) {
634*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
635*6b6b6042SThierry Reding 		return err;
636*6b6b6042SThierry Reding 	}
637*6b6b6042SThierry Reding 
638*6b6b6042SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
639*6b6b6042SThierry Reding 	value |= SOR_ENABLE;
640*6b6b6042SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
641*6b6b6042SThierry Reding 
642*6b6b6042SThierry Reding 	tegra_sor_update(sor);
643*6b6b6042SThierry Reding 
644*6b6b6042SThierry Reding 	err = tegra_sor_attach(sor);
645*6b6b6042SThierry Reding 	if (err < 0) {
646*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
647*6b6b6042SThierry Reding 		return err;
648*6b6b6042SThierry Reding 	}
649*6b6b6042SThierry Reding 
650*6b6b6042SThierry Reding 	err = tegra_sor_wakeup(sor);
651*6b6b6042SThierry Reding 	if (err < 0) {
652*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to enable DC: %d\n", err);
653*6b6b6042SThierry Reding 		return err;
654*6b6b6042SThierry Reding 	}
655*6b6b6042SThierry Reding 
656*6b6b6042SThierry Reding 	sor->enabled = true;
657*6b6b6042SThierry Reding 
658*6b6b6042SThierry Reding 	return 0;
659*6b6b6042SThierry Reding }
660*6b6b6042SThierry Reding 
661*6b6b6042SThierry Reding static int tegra_sor_detach(struct tegra_sor *sor)
662*6b6b6042SThierry Reding {
663*6b6b6042SThierry Reding 	unsigned long value, timeout;
664*6b6b6042SThierry Reding 
665*6b6b6042SThierry Reding 	/* switch to safe mode */
666*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
667*6b6b6042SThierry Reding 	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
668*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
669*6b6b6042SThierry Reding 	tegra_sor_super_update(sor);
670*6b6b6042SThierry Reding 
671*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
672*6b6b6042SThierry Reding 
673*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
674*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_PWR);
675*6b6b6042SThierry Reding 		if (value & SOR_PWR_MODE_SAFE)
676*6b6b6042SThierry Reding 			break;
677*6b6b6042SThierry Reding 	}
678*6b6b6042SThierry Reding 
679*6b6b6042SThierry Reding 	if ((value & SOR_PWR_MODE_SAFE) == 0)
680*6b6b6042SThierry Reding 		return -ETIMEDOUT;
681*6b6b6042SThierry Reding 
682*6b6b6042SThierry Reding 	/* go to sleep */
683*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
684*6b6b6042SThierry Reding 	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
685*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
686*6b6b6042SThierry Reding 	tegra_sor_super_update(sor);
687*6b6b6042SThierry Reding 
688*6b6b6042SThierry Reding 	/* detach */
689*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
690*6b6b6042SThierry Reding 	value &= ~SOR_SUPER_STATE_ATTACHED;
691*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
692*6b6b6042SThierry Reding 	tegra_sor_super_update(sor);
693*6b6b6042SThierry Reding 
694*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
695*6b6b6042SThierry Reding 
696*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
697*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_TEST);
698*6b6b6042SThierry Reding 		if ((value & SOR_TEST_ATTACHED) == 0)
699*6b6b6042SThierry Reding 			break;
700*6b6b6042SThierry Reding 
701*6b6b6042SThierry Reding 		usleep_range(25, 100);
702*6b6b6042SThierry Reding 	}
703*6b6b6042SThierry Reding 
704*6b6b6042SThierry Reding 	if ((value & SOR_TEST_ATTACHED) != 0)
705*6b6b6042SThierry Reding 		return -ETIMEDOUT;
706*6b6b6042SThierry Reding 
707*6b6b6042SThierry Reding 	return 0;
708*6b6b6042SThierry Reding }
709*6b6b6042SThierry Reding 
710*6b6b6042SThierry Reding static int tegra_sor_power_down(struct tegra_sor *sor)
711*6b6b6042SThierry Reding {
712*6b6b6042SThierry Reding 	unsigned long value, timeout;
713*6b6b6042SThierry Reding 	int err;
714*6b6b6042SThierry Reding 
715*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PWR);
716*6b6b6042SThierry Reding 	value &= ~SOR_PWR_NORMAL_STATE_PU;
717*6b6b6042SThierry Reding 	value |= SOR_PWR_TRIGGER;
718*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PWR);
719*6b6b6042SThierry Reding 
720*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
721*6b6b6042SThierry Reding 
722*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
723*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_PWR);
724*6b6b6042SThierry Reding 		if ((value & SOR_PWR_TRIGGER) == 0)
725*6b6b6042SThierry Reding 			return 0;
726*6b6b6042SThierry Reding 
727*6b6b6042SThierry Reding 		usleep_range(25, 100);
728*6b6b6042SThierry Reding 	}
729*6b6b6042SThierry Reding 
730*6b6b6042SThierry Reding 	if ((value & SOR_PWR_TRIGGER) != 0)
731*6b6b6042SThierry Reding 		return -ETIMEDOUT;
732*6b6b6042SThierry Reding 
733*6b6b6042SThierry Reding 	err = clk_set_parent(sor->clk, sor->clk_safe);
734*6b6b6042SThierry Reding 	if (err < 0)
735*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
736*6b6b6042SThierry Reding 
737*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
738*6b6b6042SThierry Reding 	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
739*6b6b6042SThierry Reding 		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
740*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
741*6b6b6042SThierry Reding 
742*6b6b6042SThierry Reding 	/* stop lane sequencer */
743*6b6b6042SThierry Reding 	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
744*6b6b6042SThierry Reding 		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
745*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
746*6b6b6042SThierry Reding 
747*6b6b6042SThierry Reding 	timeout = jiffies + msecs_to_jiffies(250);
748*6b6b6042SThierry Reding 
749*6b6b6042SThierry Reding 	while (time_before(jiffies, timeout)) {
750*6b6b6042SThierry Reding 		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
751*6b6b6042SThierry Reding 		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
752*6b6b6042SThierry Reding 			break;
753*6b6b6042SThierry Reding 
754*6b6b6042SThierry Reding 		usleep_range(25, 100);
755*6b6b6042SThierry Reding 	}
756*6b6b6042SThierry Reding 
757*6b6b6042SThierry Reding 	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
758*6b6b6042SThierry Reding 		return -ETIMEDOUT;
759*6b6b6042SThierry Reding 
760*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
761*6b6b6042SThierry Reding 	value |= SOR_PLL_2_PORT_POWERDOWN;
762*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
763*6b6b6042SThierry Reding 
764*6b6b6042SThierry Reding 	usleep_range(20, 100);
765*6b6b6042SThierry Reding 
766*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_0);
767*6b6b6042SThierry Reding 	value |= SOR_PLL_0_POWER_OFF;
768*6b6b6042SThierry Reding 	value |= SOR_PLL_0_VCOPD;
769*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_0);
770*6b6b6042SThierry Reding 
771*6b6b6042SThierry Reding 	value = tegra_sor_readl(sor, SOR_PLL_2);
772*6b6b6042SThierry Reding 	value |= SOR_PLL_2_SEQ_PLLCAPPD;
773*6b6b6042SThierry Reding 	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
774*6b6b6042SThierry Reding 	tegra_sor_writel(sor, value, SOR_PLL_2);
775*6b6b6042SThierry Reding 
776*6b6b6042SThierry Reding 	usleep_range(20, 100);
777*6b6b6042SThierry Reding 
778*6b6b6042SThierry Reding 	return 0;
779*6b6b6042SThierry Reding }
780*6b6b6042SThierry Reding 
781*6b6b6042SThierry Reding static int tegra_output_sor_disable(struct tegra_output *output)
782*6b6b6042SThierry Reding {
783*6b6b6042SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
784*6b6b6042SThierry Reding 	struct tegra_sor *sor = to_sor(output);
785*6b6b6042SThierry Reding 	unsigned long value;
786*6b6b6042SThierry Reding 	int err;
787*6b6b6042SThierry Reding 
788*6b6b6042SThierry Reding 	if (!sor->enabled)
789*6b6b6042SThierry Reding 		return 0;
790*6b6b6042SThierry Reding 
791*6b6b6042SThierry Reding 	err = tegra_sor_detach(sor);
792*6b6b6042SThierry Reding 	if (err < 0) {
793*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
794*6b6b6042SThierry Reding 		return err;
795*6b6b6042SThierry Reding 	}
796*6b6b6042SThierry Reding 
797*6b6b6042SThierry Reding 	tegra_sor_writel(sor, 0, SOR_STATE_1);
798*6b6b6042SThierry Reding 	tegra_sor_update(sor);
799*6b6b6042SThierry Reding 
800*6b6b6042SThierry Reding 	/*
801*6b6b6042SThierry Reding 	 * The following accesses registers of the display controller, so make
802*6b6b6042SThierry Reding 	 * sure it's only executed when the output is attached to one.
803*6b6b6042SThierry Reding 	 */
804*6b6b6042SThierry Reding 	if (dc) {
805*6b6b6042SThierry Reding 		/*
806*6b6b6042SThierry Reding 		 * XXX: We can't do this here because it causes the SOR to go
807*6b6b6042SThierry Reding 		 * into an erroneous state and the output will look scrambled
808*6b6b6042SThierry Reding 		 * the next time it is enabled. Presumably this is because we
809*6b6b6042SThierry Reding 		 * should be doing this only on the next VBLANK. A possible
810*6b6b6042SThierry Reding 		 * solution would be to queue a "power-off" event to trigger
811*6b6b6042SThierry Reding 		 * this code to be run during the next VBLANK.
812*6b6b6042SThierry Reding 		 */
813*6b6b6042SThierry Reding 		/*
814*6b6b6042SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
815*6b6b6042SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
816*6b6b6042SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
817*6b6b6042SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
818*6b6b6042SThierry Reding 		*/
819*6b6b6042SThierry Reding 
820*6b6b6042SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
821*6b6b6042SThierry Reding 		value &= ~DISP_CTRL_MODE_MASK;
822*6b6b6042SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
823*6b6b6042SThierry Reding 
824*6b6b6042SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
825*6b6b6042SThierry Reding 		value &= ~SOR_ENABLE;
826*6b6b6042SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
827*6b6b6042SThierry Reding 
828*6b6b6042SThierry Reding 		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
829*6b6b6042SThierry Reding 		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
830*6b6b6042SThierry Reding 	}
831*6b6b6042SThierry Reding 
832*6b6b6042SThierry Reding 	err = tegra_sor_power_down(sor);
833*6b6b6042SThierry Reding 	if (err < 0) {
834*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to power down SOR: %d\n", err);
835*6b6b6042SThierry Reding 		return err;
836*6b6b6042SThierry Reding 	}
837*6b6b6042SThierry Reding 
838*6b6b6042SThierry Reding 	if (sor->dpaux) {
839*6b6b6042SThierry Reding 		err = tegra_dpaux_disable(sor->dpaux);
840*6b6b6042SThierry Reding 		if (err < 0) {
841*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to disable DP: %d\n", err);
842*6b6b6042SThierry Reding 			return err;
843*6b6b6042SThierry Reding 		}
844*6b6b6042SThierry Reding 	}
845*6b6b6042SThierry Reding 
846*6b6b6042SThierry Reding 	err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
847*6b6b6042SThierry Reding 	if (err < 0) {
848*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
849*6b6b6042SThierry Reding 		return err;
850*6b6b6042SThierry Reding 	}
851*6b6b6042SThierry Reding 
852*6b6b6042SThierry Reding 	reset_control_assert(sor->rst);
853*6b6b6042SThierry Reding 	clk_disable_unprepare(sor->clk);
854*6b6b6042SThierry Reding 
855*6b6b6042SThierry Reding 	sor->enabled = false;
856*6b6b6042SThierry Reding 
857*6b6b6042SThierry Reding 	return 0;
858*6b6b6042SThierry Reding }
859*6b6b6042SThierry Reding 
860*6b6b6042SThierry Reding static int tegra_output_sor_setup_clock(struct tegra_output *output,
861*6b6b6042SThierry Reding 					struct clk *clk, unsigned long pclk)
862*6b6b6042SThierry Reding {
863*6b6b6042SThierry Reding 	struct tegra_sor *sor = to_sor(output);
864*6b6b6042SThierry Reding 	int err;
865*6b6b6042SThierry Reding 
866*6b6b6042SThierry Reding 	/* round to next MHz */
867*6b6b6042SThierry Reding 	pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000;
868*6b6b6042SThierry Reding 
869*6b6b6042SThierry Reding 	err = clk_set_parent(clk, sor->clk_parent);
870*6b6b6042SThierry Reding 	if (err < 0) {
871*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to set parent clock: %d\n", err);
872*6b6b6042SThierry Reding 		return err;
873*6b6b6042SThierry Reding 	}
874*6b6b6042SThierry Reding 
875*6b6b6042SThierry Reding 	err = clk_set_rate(sor->clk_parent, pclk);
876*6b6b6042SThierry Reding 	if (err < 0) {
877*6b6b6042SThierry Reding 		dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n",
878*6b6b6042SThierry Reding 			pclk * 2);
879*6b6b6042SThierry Reding 		return err;
880*6b6b6042SThierry Reding 	}
881*6b6b6042SThierry Reding 
882*6b6b6042SThierry Reding 	return 0;
883*6b6b6042SThierry Reding }
884*6b6b6042SThierry Reding 
885*6b6b6042SThierry Reding static int tegra_output_sor_check_mode(struct tegra_output *output,
886*6b6b6042SThierry Reding 				       struct drm_display_mode *mode,
887*6b6b6042SThierry Reding 				       enum drm_mode_status *status)
888*6b6b6042SThierry Reding {
889*6b6b6042SThierry Reding 	/*
890*6b6b6042SThierry Reding 	 * FIXME: For now, always assume that the mode is okay.
891*6b6b6042SThierry Reding 	 */
892*6b6b6042SThierry Reding 
893*6b6b6042SThierry Reding 	*status = MODE_OK;
894*6b6b6042SThierry Reding 
895*6b6b6042SThierry Reding 	return 0;
896*6b6b6042SThierry Reding }
897*6b6b6042SThierry Reding 
898*6b6b6042SThierry Reding static enum drm_connector_status
899*6b6b6042SThierry Reding tegra_output_sor_detect(struct tegra_output *output)
900*6b6b6042SThierry Reding {
901*6b6b6042SThierry Reding 	struct tegra_sor *sor = to_sor(output);
902*6b6b6042SThierry Reding 
903*6b6b6042SThierry Reding 	if (sor->dpaux)
904*6b6b6042SThierry Reding 		return tegra_dpaux_detect(sor->dpaux);
905*6b6b6042SThierry Reding 
906*6b6b6042SThierry Reding 	return connector_status_unknown;
907*6b6b6042SThierry Reding }
908*6b6b6042SThierry Reding 
909*6b6b6042SThierry Reding static const struct tegra_output_ops sor_ops = {
910*6b6b6042SThierry Reding 	.enable = tegra_output_sor_enable,
911*6b6b6042SThierry Reding 	.disable = tegra_output_sor_disable,
912*6b6b6042SThierry Reding 	.setup_clock = tegra_output_sor_setup_clock,
913*6b6b6042SThierry Reding 	.check_mode = tegra_output_sor_check_mode,
914*6b6b6042SThierry Reding 	.detect = tegra_output_sor_detect,
915*6b6b6042SThierry Reding };
916*6b6b6042SThierry Reding 
917*6b6b6042SThierry Reding static int tegra_sor_init(struct host1x_client *client)
918*6b6b6042SThierry Reding {
919*6b6b6042SThierry Reding 	struct tegra_drm *tegra = dev_get_drvdata(client->parent);
920*6b6b6042SThierry Reding 	struct tegra_sor *sor = host1x_client_to_sor(client);
921*6b6b6042SThierry Reding 	int err;
922*6b6b6042SThierry Reding 
923*6b6b6042SThierry Reding 	if (!sor->dpaux)
924*6b6b6042SThierry Reding 		return -ENODEV;
925*6b6b6042SThierry Reding 
926*6b6b6042SThierry Reding 	sor->output.type = TEGRA_OUTPUT_EDP;
927*6b6b6042SThierry Reding 
928*6b6b6042SThierry Reding 	sor->output.dev = sor->dev;
929*6b6b6042SThierry Reding 	sor->output.ops = &sor_ops;
930*6b6b6042SThierry Reding 
931*6b6b6042SThierry Reding 	err = tegra_output_init(tegra->drm, &sor->output);
932*6b6b6042SThierry Reding 	if (err < 0) {
933*6b6b6042SThierry Reding 		dev_err(sor->dev, "output setup failed: %d\n", err);
934*6b6b6042SThierry Reding 		return err;
935*6b6b6042SThierry Reding 	}
936*6b6b6042SThierry Reding 
937*6b6b6042SThierry Reding 	if (sor->dpaux) {
938*6b6b6042SThierry Reding 		err = tegra_dpaux_attach(sor->dpaux, &sor->output);
939*6b6b6042SThierry Reding 		if (err < 0) {
940*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to attach DP: %d\n", err);
941*6b6b6042SThierry Reding 			return err;
942*6b6b6042SThierry Reding 		}
943*6b6b6042SThierry Reding 	}
944*6b6b6042SThierry Reding 
945*6b6b6042SThierry Reding 	return 0;
946*6b6b6042SThierry Reding }
947*6b6b6042SThierry Reding 
948*6b6b6042SThierry Reding static int tegra_sor_exit(struct host1x_client *client)
949*6b6b6042SThierry Reding {
950*6b6b6042SThierry Reding 	struct tegra_sor *sor = host1x_client_to_sor(client);
951*6b6b6042SThierry Reding 	int err;
952*6b6b6042SThierry Reding 
953*6b6b6042SThierry Reding 	err = tegra_output_disable(&sor->output);
954*6b6b6042SThierry Reding 	if (err < 0) {
955*6b6b6042SThierry Reding 		dev_err(sor->dev, "output failed to disable: %d\n", err);
956*6b6b6042SThierry Reding 		return err;
957*6b6b6042SThierry Reding 	}
958*6b6b6042SThierry Reding 
959*6b6b6042SThierry Reding 	if (sor->dpaux) {
960*6b6b6042SThierry Reding 		err = tegra_dpaux_detach(sor->dpaux);
961*6b6b6042SThierry Reding 		if (err < 0) {
962*6b6b6042SThierry Reding 			dev_err(sor->dev, "failed to detach DP: %d\n", err);
963*6b6b6042SThierry Reding 			return err;
964*6b6b6042SThierry Reding 		}
965*6b6b6042SThierry Reding 	}
966*6b6b6042SThierry Reding 
967*6b6b6042SThierry Reding 	err = tegra_output_exit(&sor->output);
968*6b6b6042SThierry Reding 	if (err < 0) {
969*6b6b6042SThierry Reding 		dev_err(sor->dev, "output cleanup failed: %d\n", err);
970*6b6b6042SThierry Reding 		return err;
971*6b6b6042SThierry Reding 	}
972*6b6b6042SThierry Reding 
973*6b6b6042SThierry Reding 	return 0;
974*6b6b6042SThierry Reding }
975*6b6b6042SThierry Reding 
976*6b6b6042SThierry Reding static const struct host1x_client_ops sor_client_ops = {
977*6b6b6042SThierry Reding 	.init = tegra_sor_init,
978*6b6b6042SThierry Reding 	.exit = tegra_sor_exit,
979*6b6b6042SThierry Reding };
980*6b6b6042SThierry Reding 
981*6b6b6042SThierry Reding static int tegra_sor_probe(struct platform_device *pdev)
982*6b6b6042SThierry Reding {
983*6b6b6042SThierry Reding 	struct device_node *np;
984*6b6b6042SThierry Reding 	struct tegra_sor *sor;
985*6b6b6042SThierry Reding 	struct resource *regs;
986*6b6b6042SThierry Reding 	int err;
987*6b6b6042SThierry Reding 
988*6b6b6042SThierry Reding 	sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL);
989*6b6b6042SThierry Reding 	if (!sor)
990*6b6b6042SThierry Reding 		return -ENOMEM;
991*6b6b6042SThierry Reding 
992*6b6b6042SThierry Reding 	sor->output.dev = sor->dev = &pdev->dev;
993*6b6b6042SThierry Reding 
994*6b6b6042SThierry Reding 	np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
995*6b6b6042SThierry Reding 	if (np) {
996*6b6b6042SThierry Reding 		sor->dpaux = tegra_dpaux_find_by_of_node(np);
997*6b6b6042SThierry Reding 		of_node_put(np);
998*6b6b6042SThierry Reding 
999*6b6b6042SThierry Reding 		if (!sor->dpaux)
1000*6b6b6042SThierry Reding 			return -EPROBE_DEFER;
1001*6b6b6042SThierry Reding 	}
1002*6b6b6042SThierry Reding 
1003*6b6b6042SThierry Reding 	err = tegra_output_probe(&sor->output);
1004*6b6b6042SThierry Reding 	if (err < 0)
1005*6b6b6042SThierry Reding 		return err;
1006*6b6b6042SThierry Reding 
1007*6b6b6042SThierry Reding 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1008*6b6b6042SThierry Reding 	sor->regs = devm_ioremap_resource(&pdev->dev, regs);
1009*6b6b6042SThierry Reding 	if (IS_ERR(sor->regs))
1010*6b6b6042SThierry Reding 		return PTR_ERR(sor->regs);
1011*6b6b6042SThierry Reding 
1012*6b6b6042SThierry Reding 	sor->rst = devm_reset_control_get(&pdev->dev, "sor");
1013*6b6b6042SThierry Reding 	if (IS_ERR(sor->rst))
1014*6b6b6042SThierry Reding 		return PTR_ERR(sor->rst);
1015*6b6b6042SThierry Reding 
1016*6b6b6042SThierry Reding 	sor->clk = devm_clk_get(&pdev->dev, NULL);
1017*6b6b6042SThierry Reding 	if (IS_ERR(sor->clk))
1018*6b6b6042SThierry Reding 		return PTR_ERR(sor->clk);
1019*6b6b6042SThierry Reding 
1020*6b6b6042SThierry Reding 	sor->clk_parent = devm_clk_get(&pdev->dev, "parent");
1021*6b6b6042SThierry Reding 	if (IS_ERR(sor->clk_parent))
1022*6b6b6042SThierry Reding 		return PTR_ERR(sor->clk_parent);
1023*6b6b6042SThierry Reding 
1024*6b6b6042SThierry Reding 	err = clk_prepare_enable(sor->clk_parent);
1025*6b6b6042SThierry Reding 	if (err < 0)
1026*6b6b6042SThierry Reding 		return err;
1027*6b6b6042SThierry Reding 
1028*6b6b6042SThierry Reding 	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
1029*6b6b6042SThierry Reding 	if (IS_ERR(sor->clk_safe))
1030*6b6b6042SThierry Reding 		return PTR_ERR(sor->clk_safe);
1031*6b6b6042SThierry Reding 
1032*6b6b6042SThierry Reding 	err = clk_prepare_enable(sor->clk_safe);
1033*6b6b6042SThierry Reding 	if (err < 0)
1034*6b6b6042SThierry Reding 		return err;
1035*6b6b6042SThierry Reding 
1036*6b6b6042SThierry Reding 	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
1037*6b6b6042SThierry Reding 	if (IS_ERR(sor->clk_dp))
1038*6b6b6042SThierry Reding 		return PTR_ERR(sor->clk_dp);
1039*6b6b6042SThierry Reding 
1040*6b6b6042SThierry Reding 	err = clk_prepare_enable(sor->clk_dp);
1041*6b6b6042SThierry Reding 	if (err < 0)
1042*6b6b6042SThierry Reding 		return err;
1043*6b6b6042SThierry Reding 
1044*6b6b6042SThierry Reding 	INIT_LIST_HEAD(&sor->client.list);
1045*6b6b6042SThierry Reding 	sor->client.ops = &sor_client_ops;
1046*6b6b6042SThierry Reding 	sor->client.dev = &pdev->dev;
1047*6b6b6042SThierry Reding 
1048*6b6b6042SThierry Reding 	err = host1x_client_register(&sor->client);
1049*6b6b6042SThierry Reding 	if (err < 0) {
1050*6b6b6042SThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
1051*6b6b6042SThierry Reding 			err);
1052*6b6b6042SThierry Reding 		return err;
1053*6b6b6042SThierry Reding 	}
1054*6b6b6042SThierry Reding 
1055*6b6b6042SThierry Reding 	platform_set_drvdata(pdev, sor);
1056*6b6b6042SThierry Reding 
1057*6b6b6042SThierry Reding 	return 0;
1058*6b6b6042SThierry Reding }
1059*6b6b6042SThierry Reding 
1060*6b6b6042SThierry Reding static int tegra_sor_remove(struct platform_device *pdev)
1061*6b6b6042SThierry Reding {
1062*6b6b6042SThierry Reding 	struct tegra_sor *sor = platform_get_drvdata(pdev);
1063*6b6b6042SThierry Reding 	int err;
1064*6b6b6042SThierry Reding 
1065*6b6b6042SThierry Reding 	err = host1x_client_unregister(&sor->client);
1066*6b6b6042SThierry Reding 	if (err < 0) {
1067*6b6b6042SThierry Reding 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
1068*6b6b6042SThierry Reding 			err);
1069*6b6b6042SThierry Reding 		return err;
1070*6b6b6042SThierry Reding 	}
1071*6b6b6042SThierry Reding 
1072*6b6b6042SThierry Reding 	clk_disable_unprepare(sor->clk_parent);
1073*6b6b6042SThierry Reding 	clk_disable_unprepare(sor->clk_safe);
1074*6b6b6042SThierry Reding 	clk_disable_unprepare(sor->clk_dp);
1075*6b6b6042SThierry Reding 	clk_disable_unprepare(sor->clk);
1076*6b6b6042SThierry Reding 
1077*6b6b6042SThierry Reding 	return 0;
1078*6b6b6042SThierry Reding }
1079*6b6b6042SThierry Reding 
1080*6b6b6042SThierry Reding static const struct of_device_id tegra_sor_of_match[] = {
1081*6b6b6042SThierry Reding 	{ .compatible = "nvidia,tegra124-sor", },
1082*6b6b6042SThierry Reding 	{ },
1083*6b6b6042SThierry Reding };
1084*6b6b6042SThierry Reding 
1085*6b6b6042SThierry Reding struct platform_driver tegra_sor_driver = {
1086*6b6b6042SThierry Reding 	.driver = {
1087*6b6b6042SThierry Reding 		.name = "tegra-sor",
1088*6b6b6042SThierry Reding 		.of_match_table = tegra_sor_of_match,
1089*6b6b6042SThierry Reding 	},
1090*6b6b6042SThierry Reding 	.probe = tegra_sor_probe,
1091*6b6b6042SThierry Reding 	.remove = tegra_sor_remove,
1092*6b6b6042SThierry Reding };
1093