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 #include <dev/drm2/drmP.h> 25 #include <dev/drm2/drm_dp_helper.h> 26 27 /** 28 * DOC: dp helpers 29 * 30 * These functions contain some common logic and helpers at various abstraction 31 * levels to deal with Display Port sink devices and related things like DP aux 32 * channel transfers, EDID reading over DP aux channels, decoding certain DPCD 33 * blocks, ... 34 */ 35 36 /* Helpers for DP link training */ 37 static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) 38 { 39 return link_status[r - DP_LANE0_1_STATUS]; 40 } 41 42 static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], 43 int lane) 44 { 45 int i = DP_LANE0_1_STATUS + (lane >> 1); 46 int s = (lane & 1) * 4; 47 u8 l = dp_link_status(link_status, i); 48 return (l >> s) & 0xf; 49 } 50 51 bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], 52 int lane_count) 53 { 54 u8 lane_align; 55 u8 lane_status; 56 int lane; 57 58 lane_align = dp_link_status(link_status, 59 DP_LANE_ALIGN_STATUS_UPDATED); 60 if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) 61 return false; 62 for (lane = 0; lane < lane_count; lane++) { 63 lane_status = dp_get_lane_status(link_status, lane); 64 if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) 65 return false; 66 } 67 return true; 68 } 69 EXPORT_SYMBOL(drm_dp_channel_eq_ok); 70 71 bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], 72 int lane_count) 73 { 74 int lane; 75 u8 lane_status; 76 77 for (lane = 0; lane < lane_count; lane++) { 78 lane_status = dp_get_lane_status(link_status, lane); 79 if ((lane_status & DP_LANE_CR_DONE) == 0) 80 return false; 81 } 82 return true; 83 } 84 EXPORT_SYMBOL(drm_dp_clock_recovery_ok); 85 86 u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], 87 int lane) 88 { 89 int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 90 int s = ((lane & 1) ? 91 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 92 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); 93 u8 l = dp_link_status(link_status, i); 94 95 return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; 96 } 97 EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); 98 99 u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], 100 int lane) 101 { 102 int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 103 int s = ((lane & 1) ? 104 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : 105 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); 106 u8 l = dp_link_status(link_status, i); 107 108 return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 109 } 110 EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); 111 112 void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 113 if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 114 udelay(100); 115 else 116 mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 117 } 118 EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); 119 120 void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 121 if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 122 udelay(400); 123 else 124 mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 125 } 126 EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); 127 128 u8 drm_dp_link_rate_to_bw_code(int link_rate) 129 { 130 switch (link_rate) { 131 case 162000: 132 default: 133 return DP_LINK_BW_1_62; 134 case 270000: 135 return DP_LINK_BW_2_7; 136 case 540000: 137 return DP_LINK_BW_5_4; 138 } 139 } 140 EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); 141 142 int drm_dp_bw_code_to_link_rate(u8 link_bw) 143 { 144 switch (link_bw) { 145 case DP_LINK_BW_1_62: 146 default: 147 return 162000; 148 case DP_LINK_BW_2_7: 149 return 270000; 150 case DP_LINK_BW_5_4: 151 return 540000; 152 } 153 } 154 EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); 155