1 /* 2 * Copyright © 2009 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23 #include <sys/cdefs.h> 24 __FBSDID("$FreeBSD$"); 25 26 #include <dev/drm2/drmP.h> 27 #include <dev/drm2/drm_dp_helper.h> 28 29 /** 30 * DOC: dp helpers 31 * 32 * These functions contain some common logic and helpers at various abstraction 33 * levels to deal with Display Port sink devices and related things like DP aux 34 * channel transfers, EDID reading over DP aux channels, decoding certain DPCD 35 * blocks, ... 36 */ 37 38 /* Helpers for DP link training */ 39 static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) 40 { 41 return link_status[r - DP_LANE0_1_STATUS]; 42 } 43 44 static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], 45 int lane) 46 { 47 int i = DP_LANE0_1_STATUS + (lane >> 1); 48 int s = (lane & 1) * 4; 49 u8 l = dp_link_status(link_status, i); 50 return (l >> s) & 0xf; 51 } 52 53 bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], 54 int lane_count) 55 { 56 u8 lane_align; 57 u8 lane_status; 58 int lane; 59 60 lane_align = dp_link_status(link_status, 61 DP_LANE_ALIGN_STATUS_UPDATED); 62 if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) 63 return false; 64 for (lane = 0; lane < lane_count; lane++) { 65 lane_status = dp_get_lane_status(link_status, lane); 66 if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) 67 return false; 68 } 69 return true; 70 } 71 EXPORT_SYMBOL(drm_dp_channel_eq_ok); 72 73 bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], 74 int lane_count) 75 { 76 int lane; 77 u8 lane_status; 78 79 for (lane = 0; lane < lane_count; lane++) { 80 lane_status = dp_get_lane_status(link_status, lane); 81 if ((lane_status & DP_LANE_CR_DONE) == 0) 82 return false; 83 } 84 return true; 85 } 86 EXPORT_SYMBOL(drm_dp_clock_recovery_ok); 87 88 u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], 89 int lane) 90 { 91 int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 92 int s = ((lane & 1) ? 93 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 94 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); 95 u8 l = dp_link_status(link_status, i); 96 97 return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; 98 } 99 EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); 100 101 u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], 102 int lane) 103 { 104 int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 105 int s = ((lane & 1) ? 106 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : 107 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); 108 u8 l = dp_link_status(link_status, i); 109 110 return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 111 } 112 EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); 113 114 void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 115 if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 116 udelay(100); 117 else 118 mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 119 } 120 EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); 121 122 void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 123 if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 124 udelay(400); 125 else 126 mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 127 } 128 EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); 129 130 u8 drm_dp_link_rate_to_bw_code(int link_rate) 131 { 132 switch (link_rate) { 133 case 162000: 134 default: 135 return DP_LINK_BW_1_62; 136 case 270000: 137 return DP_LINK_BW_2_7; 138 case 540000: 139 return DP_LINK_BW_5_4; 140 } 141 } 142 EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); 143 144 int drm_dp_bw_code_to_link_rate(u8 link_bw) 145 { 146 switch (link_bw) { 147 case DP_LINK_BW_1_62: 148 default: 149 return 162000; 150 case DP_LINK_BW_2_7: 151 return 270000; 152 case DP_LINK_BW_5_4: 153 return 540000; 154 } 155 } 156 EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); 157