Skip to content

Commit 54063d8

Browse files
Baihan Lilumag
authored andcommitted
drm/hisilicon/hibmc: add dp link moduel in hibmc drivers
Add link training process functions in this moduel. Signed-off-by: Baihan Li <[email protected]> Signed-off-by: Yongbang Shi <[email protected]> Reviewed-by: Dmitry Baryshkov <[email protected]> Reviewed-by: Tian Tao <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] Signed-off-by: Dmitry Baryshkov <[email protected]>
1 parent 057e779 commit 54063d8

File tree

4 files changed

+362
-1
lines changed

4 files changed

+362
-1
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \
3-
dp/dp_aux.o
3+
dp/dp_aux.o dp/dp_link.o
44

55
obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o

drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,31 @@
1313
#include <linux/io.h>
1414
#include <drm/display/drm_dp_helper.h>
1515

16+
#define HIBMC_DP_LANE_NUM_MAX 2
17+
18+
struct hibmc_link_status {
19+
bool clock_recovered;
20+
bool channel_equalized;
21+
};
22+
23+
struct hibmc_link_cap {
24+
u8 link_rate;
25+
u8 lanes;
26+
};
27+
28+
struct hibmc_dp_link {
29+
struct hibmc_link_status status;
30+
u8 train_set[HIBMC_DP_LANE_NUM_MAX];
31+
struct hibmc_link_cap cap;
32+
};
33+
1634
struct hibmc_dp_dev {
1735
struct drm_dp_aux aux;
1836
struct drm_device *dev;
1937
void __iomem *base;
2038
struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */
39+
struct hibmc_dp_link link;
40+
u8 dpcd[DP_RECEIVER_CAP_SIZE];
2141
};
2242

2343
#define dp_field_modify(reg_value, mask, val) \
@@ -38,5 +58,6 @@ struct hibmc_dp_dev {
3858
} while (0)
3959

4060
void hibmc_dp_aux_init(struct hibmc_dp_dev *dp);
61+
int hibmc_dp_link_training(struct hibmc_dp_dev *dp);
4162

4263
#endif
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (c) 2024 Hisilicon Limited.
3+
4+
#include <linux/delay.h>
5+
#include <drm/drm_device.h>
6+
#include <drm/drm_print.h>
7+
#include "dp_comm.h"
8+
#include "dp_reg.h"
9+
10+
#define HIBMC_EQ_MAX_RETRY 5
11+
12+
static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp)
13+
{
14+
u8 buf[2];
15+
int ret;
16+
17+
/* DP 2 lane */
18+
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_LANE_DATA_EN,
19+
dp->link.cap.lanes == 0x2 ? 0x3 : 0x1);
20+
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_GCTL0, HIBMC_DP_CFG_PHY_LANE_NUM,
21+
dp->link.cap.lanes == 0x2 ? 0x1 : 0);
22+
23+
/* enhanced frame */
24+
hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_FRAME_MODE, 0x1);
25+
26+
/* set rate and lane count */
27+
buf[0] = dp->link.cap.link_rate;
28+
buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes;
29+
ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, sizeof(buf));
30+
if (ret != sizeof(buf)) {
31+
drm_dbg_dp(dp->dev, "dp aux write link rate and lanes failed, ret: %d\n", ret);
32+
return ret >= 0 ? -EIO : ret;
33+
}
34+
35+
/* set 8b/10b and downspread */
36+
buf[0] = DP_SPREAD_AMP_0_5;
37+
buf[1] = DP_SET_ANSI_8B10B;
38+
ret = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf));
39+
if (ret != sizeof(buf)) {
40+
drm_dbg_dp(dp->dev, "dp aux write 8b/10b and downspread failed, ret: %d\n", ret);
41+
return ret >= 0 ? -EIO : ret;
42+
}
43+
44+
ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd);
45+
if (ret)
46+
drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret);
47+
48+
return ret;
49+
}
50+
51+
static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern)
52+
{
53+
int ret;
54+
u8 val;
55+
u8 buf;
56+
57+
buf = (u8)pattern;
58+
if (pattern != DP_TRAINING_PATTERN_DISABLE && pattern != DP_TRAINING_PATTERN_4) {
59+
buf |= DP_LINK_SCRAMBLING_DISABLE;
60+
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0x1);
61+
} else {
62+
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0);
63+
}
64+
65+
switch (pattern) {
66+
case DP_TRAINING_PATTERN_DISABLE:
67+
val = 0;
68+
break;
69+
case DP_TRAINING_PATTERN_1:
70+
val = 1;
71+
break;
72+
case DP_TRAINING_PATTERN_2:
73+
val = 2;
74+
break;
75+
case DP_TRAINING_PATTERN_3:
76+
val = 3;
77+
break;
78+
case DP_TRAINING_PATTERN_4:
79+
val = 4;
80+
break;
81+
default:
82+
return -EINVAL;
83+
}
84+
85+
hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_PAT_SEL, val);
86+
87+
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf));
88+
if (ret != sizeof(buf)) {
89+
drm_dbg_dp(dp->dev, "dp aux write training pattern set failed\n");
90+
return ret >= 0 ? -EIO : ret;
91+
}
92+
93+
return 0;
94+
}
95+
96+
static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp)
97+
{
98+
u8 *train_set = dp->link.train_set;
99+
int ret;
100+
u8 i;
101+
102+
ret = hibmc_dp_link_training_configure(dp);
103+
if (ret)
104+
return ret;
105+
106+
ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_1);
107+
if (ret)
108+
return ret;
109+
110+
for (i = 0; i < dp->link.cap.lanes; i++)
111+
train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
112+
113+
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes);
114+
if (ret != dp->link.cap.lanes) {
115+
drm_dbg_dp(dp->dev, "dp aux write training lane set failed\n");
116+
return ret >= 0 ? -EIO : ret;
117+
}
118+
119+
return 0;
120+
}
121+
122+
static bool hibmc_dp_link_get_adjust_train(struct hibmc_dp_dev *dp,
123+
u8 lane_status[DP_LINK_STATUS_SIZE])
124+
{
125+
u8 train_set[HIBMC_DP_LANE_NUM_MAX] = {0};
126+
u8 lane;
127+
128+
for (lane = 0; lane < dp->link.cap.lanes; lane++)
129+
train_set[lane] = drm_dp_get_adjust_request_voltage(lane_status, lane) |
130+
drm_dp_get_adjust_request_pre_emphasis(lane_status, lane);
131+
132+
if (memcmp(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX)) {
133+
memcpy(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX);
134+
return true;
135+
}
136+
137+
return false;
138+
}
139+
140+
static inline int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp)
141+
{
142+
switch (dp->link.cap.link_rate) {
143+
case DP_LINK_BW_2_7:
144+
dp->link.cap.link_rate = DP_LINK_BW_1_62;
145+
return 0;
146+
case DP_LINK_BW_5_4:
147+
dp->link.cap.link_rate = DP_LINK_BW_2_7;
148+
return 0;
149+
case DP_LINK_BW_8_1:
150+
dp->link.cap.link_rate = DP_LINK_BW_5_4;
151+
return 0;
152+
default:
153+
return -EINVAL;
154+
}
155+
}
156+
157+
static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp)
158+
{
159+
switch (dp->link.cap.lanes) {
160+
case 0x2:
161+
dp->link.cap.lanes--;
162+
break;
163+
case 0x1:
164+
drm_err(dp->dev, "dp link training reduce lane failed, already reach minimum\n");
165+
return -EIO;
166+
default:
167+
return -EINVAL;
168+
}
169+
170+
return 0;
171+
}
172+
173+
static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp)
174+
{
175+
u8 lane_status[DP_LINK_STATUS_SIZE] = {0};
176+
bool level_changed;
177+
u32 voltage_tries;
178+
u32 cr_tries;
179+
int ret;
180+
181+
/*
182+
* DP 1.4 spec define 10 for maxtries value, for pre DP 1.4 version set a limit of 80
183+
* (4 voltage levels x 4 preemphasis levels x 5 identical voltage retries)
184+
*/
185+
186+
voltage_tries = 1;
187+
for (cr_tries = 0; cr_tries < 80; cr_tries++) {
188+
drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd);
189+
190+
ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status);
191+
if (ret != DP_LINK_STATUS_SIZE) {
192+
drm_err(dp->dev, "Get lane status failed\n");
193+
return ret;
194+
}
195+
196+
if (drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) {
197+
drm_dbg_dp(dp->dev, "dp link training cr done\n");
198+
dp->link.status.clock_recovered = true;
199+
return 0;
200+
}
201+
202+
if (voltage_tries == 5) {
203+
drm_dbg_dp(dp->dev, "same voltage tries 5 times\n");
204+
dp->link.status.clock_recovered = false;
205+
return 0;
206+
}
207+
208+
level_changed = hibmc_dp_link_get_adjust_train(dp, lane_status);
209+
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set,
210+
dp->link.cap.lanes);
211+
if (ret != dp->link.cap.lanes) {
212+
drm_dbg_dp(dp->dev, "Update link training failed\n");
213+
return ret >= 0 ? -EIO : ret;
214+
}
215+
216+
voltage_tries = level_changed ? 1 : voltage_tries + 1;
217+
}
218+
219+
drm_err(dp->dev, "dp link training clock recovery 80 times failed\n");
220+
dp->link.status.clock_recovered = false;
221+
222+
return 0;
223+
}
224+
225+
static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp)
226+
{
227+
u8 lane_status[DP_LINK_STATUS_SIZE] = {0};
228+
u8 eq_tries;
229+
int ret;
230+
231+
ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_2);
232+
if (ret)
233+
return ret;
234+
235+
for (eq_tries = 0; eq_tries < HIBMC_EQ_MAX_RETRY; eq_tries++) {
236+
drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd);
237+
238+
ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status);
239+
if (ret != DP_LINK_STATUS_SIZE) {
240+
drm_err(dp->dev, "get lane status failed\n");
241+
break;
242+
}
243+
244+
if (!drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) {
245+
drm_dbg_dp(dp->dev, "clock recovery check failed\n");
246+
drm_dbg_dp(dp->dev, "cannot continue channel equalization\n");
247+
dp->link.status.clock_recovered = false;
248+
break;
249+
}
250+
251+
if (drm_dp_channel_eq_ok(lane_status, dp->link.cap.lanes)) {
252+
dp->link.status.channel_equalized = true;
253+
drm_dbg_dp(dp->dev, "dp link training eq done\n");
254+
break;
255+
}
256+
257+
hibmc_dp_link_get_adjust_train(dp, lane_status);
258+
ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
259+
dp->link.train_set, dp->link.cap.lanes);
260+
if (ret != dp->link.cap.lanes) {
261+
drm_dbg_dp(dp->dev, "Update link training failed\n");
262+
ret = (ret >= 0) ? -EIO : ret;
263+
break;
264+
}
265+
}
266+
267+
if (eq_tries == HIBMC_EQ_MAX_RETRY)
268+
drm_err(dp->dev, "channel equalization failed %u times\n", eq_tries);
269+
270+
hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
271+
272+
return ret < 0 ? ret : 0;
273+
}
274+
275+
static int hibmc_dp_link_downgrade_training_cr(struct hibmc_dp_dev *dp)
276+
{
277+
if (hibmc_dp_link_reduce_rate(dp))
278+
return hibmc_dp_link_reduce_lane(dp);
279+
280+
return 0;
281+
}
282+
283+
static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp)
284+
{
285+
if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized)) {
286+
if (!hibmc_dp_link_reduce_lane(dp))
287+
return 0;
288+
}
289+
290+
return hibmc_dp_link_reduce_rate(dp);
291+
}
292+
293+
int hibmc_dp_link_training(struct hibmc_dp_dev *dp)
294+
{
295+
struct hibmc_dp_link *link = &dp->link;
296+
int ret;
297+
298+
while (true) {
299+
ret = hibmc_dp_link_training_cr_pre(dp);
300+
if (ret)
301+
goto err;
302+
303+
ret = hibmc_dp_link_training_cr(dp);
304+
if (ret)
305+
goto err;
306+
307+
if (!link->status.clock_recovered) {
308+
ret = hibmc_dp_link_downgrade_training_cr(dp);
309+
if (ret)
310+
goto err;
311+
continue;
312+
}
313+
314+
ret = hibmc_dp_link_training_channel_eq(dp);
315+
if (ret)
316+
goto err;
317+
318+
if (!link->status.channel_equalized) {
319+
ret = hibmc_dp_link_downgrade_training_eq(dp);
320+
if (ret)
321+
goto err;
322+
continue;
323+
}
324+
325+
return 0;
326+
}
327+
328+
err:
329+
hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
330+
331+
return ret;
332+
}

0 commit comments

Comments
 (0)