File Index
Files and directories
Sign in
FILE

dp_link.c

File preview with syntax highlighting.

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