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