1254817Sdumbbell/* 2254817Sdumbbell * Copyright �� 2009 Keith Packard 3254817Sdumbbell * 4254817Sdumbbell * Permission to use, copy, modify, distribute, and sell this software and its 5254817Sdumbbell * documentation for any purpose is hereby granted without fee, provided that 6254817Sdumbbell * the above copyright notice appear in all copies and that both that copyright 7254817Sdumbbell * notice and this permission notice appear in supporting documentation, and 8254817Sdumbbell * that the name of the copyright holders not be used in advertising or 9254817Sdumbbell * publicity pertaining to distribution of the software without specific, 10254817Sdumbbell * written prior permission. The copyright holders make no representations 11254817Sdumbbell * about the suitability of this software for any purpose. It is provided "as 12254817Sdumbbell * is" without express or implied warranty. 13254817Sdumbbell * 14254817Sdumbbell * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15254817Sdumbbell * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16254817Sdumbbell * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17254817Sdumbbell * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18254817Sdumbbell * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19254817Sdumbbell * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20254817Sdumbbell * OF THIS SOFTWARE. 21254817Sdumbbell */ 22254817Sdumbbell 23254817Sdumbbell#include <sys/cdefs.h> 24254817Sdumbbell__FBSDID("$FreeBSD$"); 25254817Sdumbbell 26254817Sdumbbell#include <dev/drm2/drmP.h> 27254817Sdumbbell#include <dev/drm2/drm_dp_helper.h> 28254817Sdumbbell 29254817Sdumbbell/** 30254817Sdumbbell * DOC: dp helpers 31254817Sdumbbell * 32254817Sdumbbell * These functions contain some common logic and helpers at various abstraction 33254817Sdumbbell * levels to deal with Display Port sink devices and related things like DP aux 34254817Sdumbbell * channel transfers, EDID reading over DP aux channels, decoding certain DPCD 35254817Sdumbbell * blocks, ... 36254817Sdumbbell */ 37254817Sdumbbell 38282199Sdumbbell/* Helpers for DP link training */ 39254817Sdumbbellstatic u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) 40254817Sdumbbell{ 41254817Sdumbbell return link_status[r - DP_LANE0_1_STATUS]; 42254817Sdumbbell} 43254817Sdumbbell 44254817Sdumbbellstatic u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], 45254817Sdumbbell int lane) 46254817Sdumbbell{ 47254817Sdumbbell int i = DP_LANE0_1_STATUS + (lane >> 1); 48254817Sdumbbell int s = (lane & 1) * 4; 49254817Sdumbbell u8 l = dp_link_status(link_status, i); 50254817Sdumbbell return (l >> s) & 0xf; 51254817Sdumbbell} 52254817Sdumbbell 53254817Sdumbbellbool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], 54254817Sdumbbell int lane_count) 55254817Sdumbbell{ 56254817Sdumbbell u8 lane_align; 57254817Sdumbbell u8 lane_status; 58254817Sdumbbell int lane; 59254817Sdumbbell 60254817Sdumbbell lane_align = dp_link_status(link_status, 61254817Sdumbbell DP_LANE_ALIGN_STATUS_UPDATED); 62254817Sdumbbell if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) 63254817Sdumbbell return false; 64254817Sdumbbell for (lane = 0; lane < lane_count; lane++) { 65254817Sdumbbell lane_status = dp_get_lane_status(link_status, lane); 66254817Sdumbbell if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) 67254817Sdumbbell return false; 68254817Sdumbbell } 69254817Sdumbbell return true; 70254817Sdumbbell} 71282199SdumbbellEXPORT_SYMBOL(drm_dp_channel_eq_ok); 72254817Sdumbbell 73254817Sdumbbellbool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], 74254817Sdumbbell int lane_count) 75254817Sdumbbell{ 76254817Sdumbbell int lane; 77254817Sdumbbell u8 lane_status; 78254817Sdumbbell 79254817Sdumbbell for (lane = 0; lane < lane_count; lane++) { 80254817Sdumbbell lane_status = dp_get_lane_status(link_status, lane); 81254817Sdumbbell if ((lane_status & DP_LANE_CR_DONE) == 0) 82254817Sdumbbell return false; 83254817Sdumbbell } 84254817Sdumbbell return true; 85254817Sdumbbell} 86282199SdumbbellEXPORT_SYMBOL(drm_dp_clock_recovery_ok); 87254817Sdumbbell 88254817Sdumbbellu8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], 89254817Sdumbbell int lane) 90254817Sdumbbell{ 91254817Sdumbbell int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 92254817Sdumbbell int s = ((lane & 1) ? 93254817Sdumbbell DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 94254817Sdumbbell DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); 95254817Sdumbbell u8 l = dp_link_status(link_status, i); 96254817Sdumbbell 97254817Sdumbbell return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; 98254817Sdumbbell} 99282199SdumbbellEXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); 100254817Sdumbbell 101254817Sdumbbellu8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], 102254817Sdumbbell int lane) 103254817Sdumbbell{ 104254817Sdumbbell int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 105254817Sdumbbell int s = ((lane & 1) ? 106254817Sdumbbell DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : 107254817Sdumbbell DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); 108254817Sdumbbell u8 l = dp_link_status(link_status, i); 109254817Sdumbbell 110254817Sdumbbell return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 111254817Sdumbbell} 112282199SdumbbellEXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); 113254817Sdumbbell 114254817Sdumbbellvoid drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 115254817Sdumbbell if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 116282199Sdumbbell udelay(100); 117254817Sdumbbell else 118282199Sdumbbell mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 119254817Sdumbbell} 120282199SdumbbellEXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); 121254817Sdumbbell 122254817Sdumbbellvoid drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { 123254817Sdumbbell if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) 124282199Sdumbbell udelay(400); 125254817Sdumbbell else 126282199Sdumbbell mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); 127254817Sdumbbell} 128282199SdumbbellEXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); 129254817Sdumbbell 130254817Sdumbbellu8 drm_dp_link_rate_to_bw_code(int link_rate) 131254817Sdumbbell{ 132254817Sdumbbell switch (link_rate) { 133254817Sdumbbell case 162000: 134254817Sdumbbell default: 135254817Sdumbbell return DP_LINK_BW_1_62; 136254817Sdumbbell case 270000: 137254817Sdumbbell return DP_LINK_BW_2_7; 138254817Sdumbbell case 540000: 139254817Sdumbbell return DP_LINK_BW_5_4; 140254817Sdumbbell } 141254817Sdumbbell} 142282199SdumbbellEXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); 143254817Sdumbbell 144254817Sdumbbellint drm_dp_bw_code_to_link_rate(u8 link_bw) 145254817Sdumbbell{ 146254817Sdumbbell switch (link_bw) { 147254817Sdumbbell case DP_LINK_BW_1_62: 148254817Sdumbbell default: 149254817Sdumbbell return 162000; 150254817Sdumbbell case DP_LINK_BW_2_7: 151254817Sdumbbell return 270000; 152254817Sdumbbell case DP_LINK_BW_5_4: 153254817Sdumbbell return 540000; 154254817Sdumbbell } 155254817Sdumbbell} 156282199SdumbbellEXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); 157