1 /* 2 * Copyright 2015 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Dave Airlie 23 */ 24 #include <drm/drmP.h> 25 #include <drm/radeon_drm.h> 26 #include "radeon.h" 27 #include "nid.h" 28 29 #define AUX_RX_ERROR_FLAGS (AUX_SW_RX_OVERFLOW | \ 30 AUX_SW_RX_HPD_DISCON | \ 31 AUX_SW_RX_PARTIAL_BYTE | \ 32 AUX_SW_NON_AUX_MODE | \ 33 AUX_SW_RX_MIN_COUNT_VIOL | \ 34 AUX_SW_RX_INVALID_STOP | \ 35 AUX_SW_RX_SYNC_INVALID_L | \ 36 AUX_SW_RX_SYNC_INVALID_H | \ 37 AUX_SW_RX_INVALID_START | \ 38 AUX_SW_RX_RECV_NO_DET | \ 39 AUX_SW_RX_RECV_INVALID_H | \ 40 AUX_SW_RX_RECV_INVALID_V) 41 42 #define AUX_SW_REPLY_GET_BYTE_COUNT(x) (((x) >> 24) & 0x1f) 43 44 #define BARE_ADDRESS_SIZE 3 45 46 static const u32 aux_offset[] = 47 { 48 0x6200 - 0x6200, 49 0x6250 - 0x6200, 50 0x62a0 - 0x6200, 51 0x6300 - 0x6200, 52 0x6350 - 0x6200, 53 0x63a0 - 0x6200, 54 }; 55 56 ssize_t 57 radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) 58 { 59 struct radeon_i2c_chan *chan = 60 container_of(aux, struct radeon_i2c_chan, aux); 61 struct drm_device *dev = chan->dev; 62 struct radeon_device *rdev = dev->dev_private; 63 int ret = 0, i; 64 uint32_t tmp, ack = 0; 65 int instance = chan->rec.i2c_id & 0xf; 66 u8 byte; 67 u8 *buf = msg->buffer; 68 int retry_count = 0; 69 int bytes; 70 int msize; 71 bool is_write = false; 72 73 if (WARN_ON(msg->size > 16)) 74 return -E2BIG; 75 76 switch (msg->request & ~DP_AUX_I2C_MOT) { 77 case DP_AUX_NATIVE_WRITE: 78 case DP_AUX_I2C_WRITE: 79 is_write = true; 80 break; 81 case DP_AUX_NATIVE_READ: 82 case DP_AUX_I2C_READ: 83 break; 84 default: 85 return -EINVAL; 86 } 87 88 /* work out two sizes required */ 89 msize = 0; 90 bytes = BARE_ADDRESS_SIZE; 91 if (msg->size) { 92 msize = msg->size - 1; 93 bytes++; 94 if (is_write) 95 bytes += msg->size; 96 } 97 98 mutex_lock(&chan->mutex); 99 100 /* switch the pad to aux mode */ 101 tmp = RREG32(chan->rec.mask_clk_reg); 102 tmp |= (1 << 16); 103 WREG32(chan->rec.mask_clk_reg, tmp); 104 105 /* setup AUX control register with correct HPD pin */ 106 tmp = RREG32(AUX_CONTROL + aux_offset[instance]); 107 108 tmp &= AUX_HPD_SEL(0x7); 109 tmp |= AUX_HPD_SEL(chan->rec.hpd); 110 tmp |= AUX_EN | AUX_LS_READ_EN; 111 112 WREG32(AUX_CONTROL + aux_offset[instance], tmp); 113 114 /* atombios appears to write this twice lets copy it */ 115 WREG32(AUX_SW_CONTROL + aux_offset[instance], 116 AUX_SW_WR_BYTES(bytes)); 117 WREG32(AUX_SW_CONTROL + aux_offset[instance], 118 AUX_SW_WR_BYTES(bytes)); 119 120 /* write the data header into the registers */ 121 /* request, addres, msg size */ 122 byte = (msg->request << 4); 123 WREG32(AUX_SW_DATA + aux_offset[instance], 124 AUX_SW_DATA_MASK(byte) | AUX_SW_AUTOINCREMENT_DISABLE); 125 126 byte = (msg->address >> 8) & 0xff; 127 WREG32(AUX_SW_DATA + aux_offset[instance], 128 AUX_SW_DATA_MASK(byte)); 129 130 byte = msg->address & 0xff; 131 WREG32(AUX_SW_DATA + aux_offset[instance], 132 AUX_SW_DATA_MASK(byte)); 133 134 byte = msize; 135 WREG32(AUX_SW_DATA + aux_offset[instance], 136 AUX_SW_DATA_MASK(byte)); 137 138 /* if we are writing - write the msg buffer */ 139 if (is_write) { 140 for (i = 0; i < msg->size; i++) { 141 WREG32(AUX_SW_DATA + aux_offset[instance], 142 AUX_SW_DATA_MASK(buf[i])); 143 } 144 } 145 146 /* clear the ACK */ 147 WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK); 148 149 /* write the size and GO bits */ 150 WREG32(AUX_SW_CONTROL + aux_offset[instance], 151 AUX_SW_WR_BYTES(bytes) | AUX_SW_GO); 152 153 /* poll the status registers - TODO irq support */ 154 do { 155 tmp = RREG32(AUX_SW_STATUS + aux_offset[instance]); 156 if (tmp & AUX_SW_DONE) { 157 break; 158 } 159 usleep_range(100, 200); 160 } while (retry_count++ < 1000); 161 162 if (retry_count >= 1000) { 163 DRM_ERROR("auxch hw never signalled completion, error %08x\n", tmp); 164 ret = -EIO; 165 goto done; 166 } 167 168 if (tmp & AUX_SW_RX_TIMEOUT) { 169 DRM_DEBUG_KMS("dp_aux_ch timed out\n"); 170 ret = -ETIMEDOUT; 171 goto done; 172 } 173 if (tmp & AUX_RX_ERROR_FLAGS) { 174 DRM_DEBUG_KMS("dp_aux_ch flags not zero: %08x\n", tmp); 175 ret = -EIO; 176 goto done; 177 } 178 179 bytes = AUX_SW_REPLY_GET_BYTE_COUNT(tmp); 180 if (bytes) { 181 WREG32(AUX_SW_DATA + aux_offset[instance], 182 AUX_SW_DATA_RW | AUX_SW_AUTOINCREMENT_DISABLE); 183 184 tmp = RREG32(AUX_SW_DATA + aux_offset[instance]); 185 ack = (tmp >> 8) & 0xff; 186 187 for (i = 0; i < bytes - 1; i++) { 188 tmp = RREG32(AUX_SW_DATA + aux_offset[instance]); 189 if (buf) 190 buf[i] = (tmp >> 8) & 0xff; 191 } 192 if (buf) 193 ret = bytes - 1; 194 } 195 196 WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK); 197 198 if (is_write) 199 ret = msg->size; 200 done: 201 mutex_unlock(&chan->mutex); 202 203 if (ret >= 0) 204 msg->reply = ack >> 4; 205 return ret; 206 } 207