xref: /dflybsd-src/sys/dev/drm/drm_dp_helper.c (revision 24edb8848e2499ece59b84a04f554a7a897feeab)
16f486c69SFrançois Tigeot /*
26f486c69SFrançois Tigeot  * Copyright © 2009 Keith Packard
36f486c69SFrançois Tigeot  *
46f486c69SFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
56f486c69SFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
66f486c69SFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
76f486c69SFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
86f486c69SFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
96f486c69SFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
106f486c69SFrançois Tigeot  * written prior permission.  The copyright holders make no representations
116f486c69SFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
126f486c69SFrançois Tigeot  * is" without express or implied warranty.
136f486c69SFrançois Tigeot  *
146f486c69SFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
156f486c69SFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
166f486c69SFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
176f486c69SFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
186f486c69SFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
196f486c69SFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
206f486c69SFrançois Tigeot  * OF THIS SOFTWARE.
216f486c69SFrançois Tigeot  */
226f486c69SFrançois Tigeot 
2319df918dSFrançois Tigeot #include <linux/export.h>
2418e26a6dSFrançois Tigeot #include <drm/drmP.h>
2518e26a6dSFrançois Tigeot #include <drm/drm_dp_helper.h>
266f486c69SFrançois Tigeot 
276f486c69SFrançois Tigeot 
2819df918dSFrançois Tigeot /* Helpers for DP link training */
299edbd4a0SFrançois Tigeot static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
306f486c69SFrançois Tigeot {
316f486c69SFrançois Tigeot 	return link_status[r - DP_LANE0_1_STATUS];
326f486c69SFrançois Tigeot }
336f486c69SFrançois Tigeot 
349edbd4a0SFrançois Tigeot static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
356f486c69SFrançois Tigeot 			     int lane)
366f486c69SFrançois Tigeot {
376f486c69SFrançois Tigeot 	int i = DP_LANE0_1_STATUS + (lane >> 1);
386f486c69SFrançois Tigeot 	int s = (lane & 1) * 4;
396f486c69SFrançois Tigeot 	u8 l = dp_link_status(link_status, i);
406f486c69SFrançois Tigeot 	return (l >> s) & 0xf;
416f486c69SFrançois Tigeot }
426f486c69SFrançois Tigeot 
439edbd4a0SFrançois Tigeot bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
446f486c69SFrançois Tigeot 			  int lane_count)
456f486c69SFrançois Tigeot {
466f486c69SFrançois Tigeot 	u8 lane_align;
476f486c69SFrançois Tigeot 	u8 lane_status;
486f486c69SFrançois Tigeot 	int lane;
496f486c69SFrançois Tigeot 
506f486c69SFrançois Tigeot 	lane_align = dp_link_status(link_status,
516f486c69SFrançois Tigeot 				    DP_LANE_ALIGN_STATUS_UPDATED);
526f486c69SFrançois Tigeot 	if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
536f486c69SFrançois Tigeot 		return false;
546f486c69SFrançois Tigeot 	for (lane = 0; lane < lane_count; lane++) {
556f486c69SFrançois Tigeot 		lane_status = dp_get_lane_status(link_status, lane);
566f486c69SFrançois Tigeot 		if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
576f486c69SFrançois Tigeot 			return false;
586f486c69SFrançois Tigeot 	}
596f486c69SFrançois Tigeot 	return true;
606f486c69SFrançois Tigeot }
6119df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_channel_eq_ok);
626f486c69SFrançois Tigeot 
639edbd4a0SFrançois Tigeot bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
646f486c69SFrançois Tigeot 			      int lane_count)
656f486c69SFrançois Tigeot {
666f486c69SFrançois Tigeot 	int lane;
676f486c69SFrançois Tigeot 	u8 lane_status;
686f486c69SFrançois Tigeot 
696f486c69SFrançois Tigeot 	for (lane = 0; lane < lane_count; lane++) {
706f486c69SFrançois Tigeot 		lane_status = dp_get_lane_status(link_status, lane);
716f486c69SFrançois Tigeot 		if ((lane_status & DP_LANE_CR_DONE) == 0)
726f486c69SFrançois Tigeot 			return false;
736f486c69SFrançois Tigeot 	}
746f486c69SFrançois Tigeot 	return true;
756f486c69SFrançois Tigeot }
7619df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_clock_recovery_ok);
776f486c69SFrançois Tigeot 
789edbd4a0SFrançois Tigeot u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
796f486c69SFrançois Tigeot 				     int lane)
806f486c69SFrançois Tigeot {
816f486c69SFrançois Tigeot 	int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
826f486c69SFrançois Tigeot 	int s = ((lane & 1) ?
836f486c69SFrançois Tigeot 		 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
846f486c69SFrançois Tigeot 		 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
856f486c69SFrançois Tigeot 	u8 l = dp_link_status(link_status, i);
866f486c69SFrançois Tigeot 
876f486c69SFrançois Tigeot 	return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
886f486c69SFrançois Tigeot }
8919df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage);
906f486c69SFrançois Tigeot 
919edbd4a0SFrançois Tigeot u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
926f486c69SFrançois Tigeot 					  int lane)
936f486c69SFrançois Tigeot {
946f486c69SFrançois Tigeot 	int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
956f486c69SFrançois Tigeot 	int s = ((lane & 1) ?
966f486c69SFrançois Tigeot 		 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
976f486c69SFrançois Tigeot 		 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
986f486c69SFrançois Tigeot 	u8 l = dp_link_status(link_status, i);
996f486c69SFrançois Tigeot 
1006f486c69SFrançois Tigeot 	return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
1016f486c69SFrançois Tigeot }
10219df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
1036f486c69SFrançois Tigeot 
1049edbd4a0SFrançois Tigeot void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
1056f486c69SFrançois Tigeot 	if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
10619df918dSFrançois Tigeot 		udelay(100);
1076f486c69SFrançois Tigeot 	else
10819df918dSFrançois Tigeot 		mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
1096f486c69SFrançois Tigeot }
11019df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
1116f486c69SFrançois Tigeot 
1129edbd4a0SFrançois Tigeot void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
1136f486c69SFrançois Tigeot 	if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
11419df918dSFrançois Tigeot 		udelay(400);
1156f486c69SFrançois Tigeot 	else
11619df918dSFrançois Tigeot 		mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
1176f486c69SFrançois Tigeot }
11819df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
1196f486c69SFrançois Tigeot 
1206f486c69SFrançois Tigeot u8 drm_dp_link_rate_to_bw_code(int link_rate)
1216f486c69SFrançois Tigeot {
1226f486c69SFrançois Tigeot 	switch (link_rate) {
1236f486c69SFrançois Tigeot 	case 162000:
1246f486c69SFrançois Tigeot 	default:
1256f486c69SFrançois Tigeot 		return DP_LINK_BW_1_62;
1266f486c69SFrançois Tigeot 	case 270000:
1276f486c69SFrançois Tigeot 		return DP_LINK_BW_2_7;
1286f486c69SFrançois Tigeot 	case 540000:
1296f486c69SFrançois Tigeot 		return DP_LINK_BW_5_4;
1306f486c69SFrançois Tigeot 	}
1316f486c69SFrançois Tigeot }
13219df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
1336f486c69SFrançois Tigeot 
1346f486c69SFrançois Tigeot int drm_dp_bw_code_to_link_rate(u8 link_bw)
1356f486c69SFrançois Tigeot {
1366f486c69SFrançois Tigeot 	switch (link_bw) {
1376f486c69SFrançois Tigeot 	case DP_LINK_BW_1_62:
1386f486c69SFrançois Tigeot 	default:
1396f486c69SFrançois Tigeot 		return 162000;
1406f486c69SFrançois Tigeot 	case DP_LINK_BW_2_7:
1416f486c69SFrançois Tigeot 		return 270000;
1426f486c69SFrançois Tigeot 	case DP_LINK_BW_5_4:
1436f486c69SFrançois Tigeot 		return 540000;
1446f486c69SFrançois Tigeot 	}
1456f486c69SFrançois Tigeot }
14619df918dSFrançois Tigeot EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
147*24edb884SFrançois Tigeot 
148*24edb884SFrançois Tigeot /**
149*24edb884SFrançois Tigeot  * DOC: dp helpers
150*24edb884SFrançois Tigeot  *
151*24edb884SFrançois Tigeot  * The DisplayPort AUX channel is an abstraction to allow generic, driver-
152*24edb884SFrançois Tigeot  * independent access to AUX functionality. Drivers can take advantage of
153*24edb884SFrançois Tigeot  * this by filling in the fields of the drm_dp_aux structure.
154*24edb884SFrançois Tigeot  *
155*24edb884SFrançois Tigeot  * Transactions are described using a hardware-independent drm_dp_aux_msg
156*24edb884SFrançois Tigeot  * structure, which is passed into a driver's .transfer() implementation.
157*24edb884SFrançois Tigeot  * Both native and I2C-over-AUX transactions are supported.
158*24edb884SFrançois Tigeot  */
159*24edb884SFrançois Tigeot 
160*24edb884SFrançois Tigeot static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
161*24edb884SFrançois Tigeot 			      unsigned int offset, void *buffer, size_t size)
162*24edb884SFrançois Tigeot {
163*24edb884SFrançois Tigeot 	struct drm_dp_aux_msg msg;
164*24edb884SFrançois Tigeot 	unsigned int retry;
165*24edb884SFrançois Tigeot 	int err;
166*24edb884SFrançois Tigeot 
167*24edb884SFrançois Tigeot 	memset(&msg, 0, sizeof(msg));
168*24edb884SFrançois Tigeot 	msg.address = offset;
169*24edb884SFrançois Tigeot 	msg.request = request;
170*24edb884SFrançois Tigeot 	msg.buffer = buffer;
171*24edb884SFrançois Tigeot 	msg.size = size;
172*24edb884SFrançois Tigeot 
173*24edb884SFrançois Tigeot 	/*
174*24edb884SFrançois Tigeot 	 * The specification doesn't give any recommendation on how often to
175*24edb884SFrançois Tigeot 	 * retry native transactions, so retry 7 times like for I2C-over-AUX
176*24edb884SFrançois Tigeot 	 * transactions.
177*24edb884SFrançois Tigeot 	 */
178*24edb884SFrançois Tigeot 	for (retry = 0; retry < 7; retry++) {
179*24edb884SFrançois Tigeot 
180*24edb884SFrançois Tigeot 		mutex_lock(&aux->hw_mutex);
181*24edb884SFrançois Tigeot 		err = aux->transfer(aux, &msg);
182*24edb884SFrançois Tigeot 		mutex_unlock(&aux->hw_mutex);
183*24edb884SFrançois Tigeot 		if (err < 0) {
184*24edb884SFrançois Tigeot 			if (err == -EBUSY)
185*24edb884SFrançois Tigeot 				continue;
186*24edb884SFrançois Tigeot 
187*24edb884SFrançois Tigeot 			return err;
188*24edb884SFrançois Tigeot 		}
189*24edb884SFrançois Tigeot 
190*24edb884SFrançois Tigeot 
191*24edb884SFrançois Tigeot 		switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
192*24edb884SFrançois Tigeot 		case DP_AUX_NATIVE_REPLY_ACK:
193*24edb884SFrançois Tigeot 			if (err < size)
194*24edb884SFrançois Tigeot 				return -EPROTO;
195*24edb884SFrançois Tigeot 			return err;
196*24edb884SFrançois Tigeot 
197*24edb884SFrançois Tigeot 		case DP_AUX_NATIVE_REPLY_NACK:
198*24edb884SFrançois Tigeot 			return -EIO;
199*24edb884SFrançois Tigeot 
200*24edb884SFrançois Tigeot 		case DP_AUX_NATIVE_REPLY_DEFER:
201*24edb884SFrançois Tigeot 			usleep_range(400, 500);
202*24edb884SFrançois Tigeot 			break;
203*24edb884SFrançois Tigeot 		}
204*24edb884SFrançois Tigeot 	}
205*24edb884SFrançois Tigeot 
206*24edb884SFrançois Tigeot 	DRM_DEBUG_KMS("too many retries, giving up\n");
207*24edb884SFrançois Tigeot 	return -EIO;
208*24edb884SFrançois Tigeot }
209*24edb884SFrançois Tigeot 
210*24edb884SFrançois Tigeot /**
211*24edb884SFrançois Tigeot  * drm_dp_dpcd_read() - read a series of bytes from the DPCD
212*24edb884SFrançois Tigeot  * @aux: DisplayPort AUX channel
213*24edb884SFrançois Tigeot  * @offset: address of the (first) register to read
214*24edb884SFrançois Tigeot  * @buffer: buffer to store the register values
215*24edb884SFrançois Tigeot  * @size: number of bytes in @buffer
216*24edb884SFrançois Tigeot  *
217*24edb884SFrançois Tigeot  * Returns the number of bytes transferred on success, or a negative error
218*24edb884SFrançois Tigeot  * code on failure. -EIO is returned if the request was NAKed by the sink or
219*24edb884SFrançois Tigeot  * if the retry count was exceeded. If not all bytes were transferred, this
220*24edb884SFrançois Tigeot  * function returns -EPROTO. Errors from the underlying AUX channel transfer
221*24edb884SFrançois Tigeot  * function, with the exception of -EBUSY (which causes the transaction to
222*24edb884SFrançois Tigeot  * be retried), are propagated to the caller.
223*24edb884SFrançois Tigeot  */
224*24edb884SFrançois Tigeot ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
225*24edb884SFrançois Tigeot 			 void *buffer, size_t size)
226*24edb884SFrançois Tigeot {
227*24edb884SFrançois Tigeot 	return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer,
228*24edb884SFrançois Tigeot 				  size);
229*24edb884SFrançois Tigeot }
230*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_dpcd_read);
231*24edb884SFrançois Tigeot 
232*24edb884SFrançois Tigeot /**
233*24edb884SFrançois Tigeot  * drm_dp_dpcd_write() - write a series of bytes to the DPCD
234*24edb884SFrançois Tigeot  * @aux: DisplayPort AUX channel
235*24edb884SFrançois Tigeot  * @offset: address of the (first) register to write
236*24edb884SFrançois Tigeot  * @buffer: buffer containing the values to write
237*24edb884SFrançois Tigeot  * @size: number of bytes in @buffer
238*24edb884SFrançois Tigeot  *
239*24edb884SFrançois Tigeot  * Returns the number of bytes transferred on success, or a negative error
240*24edb884SFrançois Tigeot  * code on failure. -EIO is returned if the request was NAKed by the sink or
241*24edb884SFrançois Tigeot  * if the retry count was exceeded. If not all bytes were transferred, this
242*24edb884SFrançois Tigeot  * function returns -EPROTO. Errors from the underlying AUX channel transfer
243*24edb884SFrançois Tigeot  * function, with the exception of -EBUSY (which causes the transaction to
244*24edb884SFrançois Tigeot  * be retried), are propagated to the caller.
245*24edb884SFrançois Tigeot  */
246*24edb884SFrançois Tigeot ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
247*24edb884SFrançois Tigeot 			  void *buffer, size_t size)
248*24edb884SFrançois Tigeot {
249*24edb884SFrançois Tigeot 	return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer,
250*24edb884SFrançois Tigeot 				  size);
251*24edb884SFrançois Tigeot }
252*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_dpcd_write);
253*24edb884SFrançois Tigeot 
254