1c5b1fb65SWyon Bi // SPDX-License-Identifier: GPL-2.0+
2c5b1fb65SWyon Bi /*
3c5b1fb65SWyon Bi * Copyright © 2009 Keith Packard
4c5b1fb65SWyon Bi *
5c5b1fb65SWyon Bi * Permission to use, copy, modify, distribute, and sell this software and its
6c5b1fb65SWyon Bi * documentation for any purpose is hereby granted without fee, provided that
7c5b1fb65SWyon Bi * the above copyright notice appear in all copies and that both that copyright
8c5b1fb65SWyon Bi * notice and this permission notice appear in supporting documentation, and
9c5b1fb65SWyon Bi * that the name of the copyright holders not be used in advertising or
10c5b1fb65SWyon Bi * publicity pertaining to distribution of the software without specific,
11c5b1fb65SWyon Bi * written prior permission. The copyright holders make no representations
12c5b1fb65SWyon Bi * about the suitability of this software for any purpose. It is provided "as
13c5b1fb65SWyon Bi * is" without express or implied warranty.
14c5b1fb65SWyon Bi *
15c5b1fb65SWyon Bi * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16c5b1fb65SWyon Bi * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17c5b1fb65SWyon Bi * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18c5b1fb65SWyon Bi * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19c5b1fb65SWyon Bi * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20c5b1fb65SWyon Bi * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21c5b1fb65SWyon Bi * OF THIS SOFTWARE.
22c5b1fb65SWyon Bi */
23c5b1fb65SWyon Bi
24c5b1fb65SWyon Bi #include <common.h>
25c5b1fb65SWyon Bi #include <drm/drm_dp_helper.h>
26c5b1fb65SWyon Bi
27c5b1fb65SWyon Bi /**
28c5b1fb65SWyon Bi * DOC: dp helpers
29c5b1fb65SWyon Bi *
30c5b1fb65SWyon Bi * These functions contain some common logic and helpers at various abstraction
31c5b1fb65SWyon Bi * levels to deal with Display Port sink devices and related things like DP aux
32c5b1fb65SWyon Bi * channel transfers, EDID reading over DP aux channels, decoding certain DPCD
33c5b1fb65SWyon Bi * blocks, ...
34c5b1fb65SWyon Bi */
35c5b1fb65SWyon Bi
36c5b1fb65SWyon Bi /* Helpers for DP link training */
dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE],int r)37c5b1fb65SWyon Bi static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
38c5b1fb65SWyon Bi {
39c5b1fb65SWyon Bi return link_status[r - DP_LANE0_1_STATUS];
40c5b1fb65SWyon Bi }
41c5b1fb65SWyon Bi
dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],int lane)42c5b1fb65SWyon Bi static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
43c5b1fb65SWyon Bi int lane)
44c5b1fb65SWyon Bi {
45c5b1fb65SWyon Bi int i = DP_LANE0_1_STATUS + (lane >> 1);
46c5b1fb65SWyon Bi int s = (lane & 1) * 4;
47c5b1fb65SWyon Bi u8 l = dp_link_status(link_status, i);
48c5b1fb65SWyon Bi
49c5b1fb65SWyon Bi return (l >> s) & 0xf;
50c5b1fb65SWyon Bi }
51c5b1fb65SWyon Bi
drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],int lane_count)52c5b1fb65SWyon Bi bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
53c5b1fb65SWyon Bi int lane_count)
54c5b1fb65SWyon Bi {
55c5b1fb65SWyon Bi u8 lane_align;
56c5b1fb65SWyon Bi u8 lane_status;
57c5b1fb65SWyon Bi int lane;
58c5b1fb65SWyon Bi
59c5b1fb65SWyon Bi lane_align = dp_link_status(link_status,
60c5b1fb65SWyon Bi DP_LANE_ALIGN_STATUS_UPDATED);
61c5b1fb65SWyon Bi if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
62c5b1fb65SWyon Bi return false;
63c5b1fb65SWyon Bi for (lane = 0; lane < lane_count; lane++) {
64c5b1fb65SWyon Bi lane_status = dp_get_lane_status(link_status, lane);
65c5b1fb65SWyon Bi if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
66c5b1fb65SWyon Bi return false;
67c5b1fb65SWyon Bi }
68c5b1fb65SWyon Bi return true;
69c5b1fb65SWyon Bi }
70c5b1fb65SWyon Bi
drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],int lane_count)71c5b1fb65SWyon Bi bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
72c5b1fb65SWyon Bi int lane_count)
73c5b1fb65SWyon Bi {
74c5b1fb65SWyon Bi int lane;
75c5b1fb65SWyon Bi u8 lane_status;
76c5b1fb65SWyon Bi
77c5b1fb65SWyon Bi for (lane = 0; lane < lane_count; lane++) {
78c5b1fb65SWyon Bi lane_status = dp_get_lane_status(link_status, lane);
79c5b1fb65SWyon Bi if ((lane_status & DP_LANE_CR_DONE) == 0)
80c5b1fb65SWyon Bi return false;
81c5b1fb65SWyon Bi }
82c5b1fb65SWyon Bi return true;
83c5b1fb65SWyon Bi }
84c5b1fb65SWyon Bi
drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],int lane)85c5b1fb65SWyon Bi u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
86c5b1fb65SWyon Bi int lane)
87c5b1fb65SWyon Bi {
88c5b1fb65SWyon Bi int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
89c5b1fb65SWyon Bi int s = ((lane & 1) ?
90c5b1fb65SWyon Bi DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
91c5b1fb65SWyon Bi DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
92c5b1fb65SWyon Bi u8 l = dp_link_status(link_status, i);
93c5b1fb65SWyon Bi
94c5b1fb65SWyon Bi return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
95c5b1fb65SWyon Bi }
96c5b1fb65SWyon Bi
drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],int lane)97c5b1fb65SWyon Bi u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
98c5b1fb65SWyon Bi int lane)
99c5b1fb65SWyon Bi {
100c5b1fb65SWyon Bi int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
101c5b1fb65SWyon Bi int s = ((lane & 1) ?
102c5b1fb65SWyon Bi DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
103c5b1fb65SWyon Bi DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
104c5b1fb65SWyon Bi u8 l = dp_link_status(link_status, i);
105c5b1fb65SWyon Bi
106c5b1fb65SWyon Bi return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
107c5b1fb65SWyon Bi }
108c5b1fb65SWyon Bi
drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])109c5b1fb65SWyon Bi void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
110c5b1fb65SWyon Bi {
111c5b1fb65SWyon Bi int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
112c5b1fb65SWyon Bi DP_TRAINING_AUX_RD_MASK;
113c5b1fb65SWyon Bi
114c5b1fb65SWyon Bi if (rd_interval > 4)
115c5b1fb65SWyon Bi printf("AUX interval %d, out of range (max 4)\n", rd_interval);
116c5b1fb65SWyon Bi
117c5b1fb65SWyon Bi if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
118c5b1fb65SWyon Bi udelay(100);
119c5b1fb65SWyon Bi else
120c5b1fb65SWyon Bi mdelay(rd_interval * 4);
121c5b1fb65SWyon Bi }
122c5b1fb65SWyon Bi
drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])123c5b1fb65SWyon Bi void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
124c5b1fb65SWyon Bi {
125c5b1fb65SWyon Bi int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
126c5b1fb65SWyon Bi DP_TRAINING_AUX_RD_MASK;
127c5b1fb65SWyon Bi
128c5b1fb65SWyon Bi if (rd_interval > 4)
129c5b1fb65SWyon Bi printf("AUX interval %d, out of range (max 4)\n", rd_interval);
130c5b1fb65SWyon Bi
131c5b1fb65SWyon Bi if (rd_interval == 0)
132c5b1fb65SWyon Bi udelay(400);
133c5b1fb65SWyon Bi else
134c5b1fb65SWyon Bi mdelay(rd_interval * 4);
135c5b1fb65SWyon Bi }
136c5b1fb65SWyon Bi
drm_dp_link_rate_to_bw_code(int link_rate)137c5b1fb65SWyon Bi u8 drm_dp_link_rate_to_bw_code(int link_rate)
138c5b1fb65SWyon Bi {
139*7efec348SDamon Ding /* Spec says link_bw = link_rate / 0.27Gbps */
140*7efec348SDamon Ding return link_rate / 27000;
141c5b1fb65SWyon Bi }
142c5b1fb65SWyon Bi
drm_dp_bw_code_to_link_rate(u8 link_bw)143c5b1fb65SWyon Bi int drm_dp_bw_code_to_link_rate(u8 link_bw)
144c5b1fb65SWyon Bi {
145*7efec348SDamon Ding /* Spec says link_rate = link_bw * 0.27Gbps */
146*7efec348SDamon Ding return link_bw * 27000;
147c5b1fb65SWyon Bi }
148ebdfc6a4SZhang Yubing
149ebdfc6a4SZhang Yubing #define AUX_RETRY_INTERVAL 500 /* us */
150ebdfc6a4SZhang Yubing
drm_dp_dpcd_access(struct drm_dp_aux * aux,u8 request,unsigned int offset,void * buffer,size_t size)151ebdfc6a4SZhang Yubing static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
152ebdfc6a4SZhang Yubing unsigned int offset, void *buffer, size_t size)
153ebdfc6a4SZhang Yubing {
154ebdfc6a4SZhang Yubing struct drm_dp_aux_msg msg;
155ebdfc6a4SZhang Yubing unsigned int retry, native_reply;
156ebdfc6a4SZhang Yubing int err = 0, ret = 0;
157ebdfc6a4SZhang Yubing
158ebdfc6a4SZhang Yubing memset(&msg, 0, sizeof(msg));
159ebdfc6a4SZhang Yubing msg.address = offset;
160ebdfc6a4SZhang Yubing msg.request = request;
161ebdfc6a4SZhang Yubing msg.buffer = buffer;
162ebdfc6a4SZhang Yubing msg.size = size;
163ebdfc6a4SZhang Yubing
164ebdfc6a4SZhang Yubing /*
165ebdfc6a4SZhang Yubing * The specification doesn't give any recommendation on how often to
166ebdfc6a4SZhang Yubing * retry native transactions. We used to retry 7 times like for
167ebdfc6a4SZhang Yubing * aux i2c transactions but real world devices this wasn't
168ebdfc6a4SZhang Yubing * sufficient, bump to 32 which makes Dell 4k monitors happier.
169ebdfc6a4SZhang Yubing */
170ebdfc6a4SZhang Yubing for (retry = 0; retry < 32; retry++) {
171ebdfc6a4SZhang Yubing if (ret != 0 && ret != -ETIMEDOUT)
172ebdfc6a4SZhang Yubing udelay(AUX_RETRY_INTERVAL);
173ebdfc6a4SZhang Yubing
174ebdfc6a4SZhang Yubing ret = aux->transfer(aux, &msg);
175ebdfc6a4SZhang Yubing if (ret >= 0) {
176ebdfc6a4SZhang Yubing native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
177ebdfc6a4SZhang Yubing if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
178ebdfc6a4SZhang Yubing if (ret == size)
179ebdfc6a4SZhang Yubing goto out;
180ebdfc6a4SZhang Yubing
181ebdfc6a4SZhang Yubing ret = -EPROTO;
182ebdfc6a4SZhang Yubing } else {
183ebdfc6a4SZhang Yubing ret = -EIO;
184ebdfc6a4SZhang Yubing }
185ebdfc6a4SZhang Yubing }
186ebdfc6a4SZhang Yubing
187ebdfc6a4SZhang Yubing /*
188ebdfc6a4SZhang Yubing * We want the error we return to be the error we received on
189ebdfc6a4SZhang Yubing * the first transaction, since we may get a different error the
190ebdfc6a4SZhang Yubing * next time we retry
191ebdfc6a4SZhang Yubing */
192ebdfc6a4SZhang Yubing if (!err)
193ebdfc6a4SZhang Yubing err = ret;
194ebdfc6a4SZhang Yubing }
195ebdfc6a4SZhang Yubing
196ebdfc6a4SZhang Yubing printf("%s: Too many retries, giving up. First error: %d\n",
197ebdfc6a4SZhang Yubing aux->name, err);
198ebdfc6a4SZhang Yubing ret = err;
199ebdfc6a4SZhang Yubing
200ebdfc6a4SZhang Yubing out:
201ebdfc6a4SZhang Yubing return ret;
202ebdfc6a4SZhang Yubing }
203ebdfc6a4SZhang Yubing
drm_dp_dpcd_read(struct drm_dp_aux * aux,unsigned int offset,void * buffer,size_t size)204ebdfc6a4SZhang Yubing ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
205ebdfc6a4SZhang Yubing void *buffer, size_t size)
206ebdfc6a4SZhang Yubing {
207ebdfc6a4SZhang Yubing int ret;
208ebdfc6a4SZhang Yubing
209ebdfc6a4SZhang Yubing ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
210ebdfc6a4SZhang Yubing buffer, 1);
211ebdfc6a4SZhang Yubing if (ret != 1)
212ebdfc6a4SZhang Yubing goto out;
213ebdfc6a4SZhang Yubing
214ebdfc6a4SZhang Yubing ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
215ebdfc6a4SZhang Yubing buffer, size);
216ebdfc6a4SZhang Yubing
217ebdfc6a4SZhang Yubing out:
218ebdfc6a4SZhang Yubing return ret;
219ebdfc6a4SZhang Yubing }
220ebdfc6a4SZhang Yubing
drm_dp_dpcd_write(struct drm_dp_aux * aux,unsigned int offset,void * buffer,size_t size)221ebdfc6a4SZhang Yubing ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
222ebdfc6a4SZhang Yubing void *buffer, size_t size)
223ebdfc6a4SZhang Yubing {
224ebdfc6a4SZhang Yubing int ret;
225ebdfc6a4SZhang Yubing
226ebdfc6a4SZhang Yubing ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset,
227ebdfc6a4SZhang Yubing buffer, size);
228ebdfc6a4SZhang Yubing
229ebdfc6a4SZhang Yubing return ret;
230ebdfc6a4SZhang Yubing }
231ebdfc6a4SZhang Yubing
drm_dp_dpcd_read_link_status(struct drm_dp_aux * aux,u8 status[DP_LINK_STATUS_SIZE])232ebdfc6a4SZhang Yubing int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
233ebdfc6a4SZhang Yubing u8 status[DP_LINK_STATUS_SIZE])
234ebdfc6a4SZhang Yubing {
235ebdfc6a4SZhang Yubing return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status,
236ebdfc6a4SZhang Yubing DP_LINK_STATUS_SIZE);
237ebdfc6a4SZhang Yubing }
238ebdfc6a4SZhang Yubing
drm_dp_read_extended_dpcd_caps(struct drm_dp_aux * aux,u8 dpcd[DP_RECEIVER_CAP_SIZE])239ebdfc6a4SZhang Yubing static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
240ebdfc6a4SZhang Yubing u8 dpcd[DP_RECEIVER_CAP_SIZE])
241ebdfc6a4SZhang Yubing {
242ebdfc6a4SZhang Yubing u8 dpcd_ext[6];
243ebdfc6a4SZhang Yubing int ret;
244ebdfc6a4SZhang Yubing
245ebdfc6a4SZhang Yubing /*
246ebdfc6a4SZhang Yubing * Prior to DP1.3 the bit represented by
247ebdfc6a4SZhang Yubing * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
248ebdfc6a4SZhang Yubing * If it is set DP_DPCD_REV at 0000h could be at a value less than
249ebdfc6a4SZhang Yubing * the true capability of the panel. The only way to check is to
250ebdfc6a4SZhang Yubing * then compare 0000h and 2200h.
251ebdfc6a4SZhang Yubing */
252ebdfc6a4SZhang Yubing if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
253ebdfc6a4SZhang Yubing DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
254ebdfc6a4SZhang Yubing return 0;
255ebdfc6a4SZhang Yubing
256ebdfc6a4SZhang Yubing ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
257ebdfc6a4SZhang Yubing sizeof(dpcd_ext));
258ebdfc6a4SZhang Yubing if (ret < 0)
259ebdfc6a4SZhang Yubing return ret;
260ebdfc6a4SZhang Yubing if (ret != sizeof(dpcd_ext))
261ebdfc6a4SZhang Yubing return -EIO;
262ebdfc6a4SZhang Yubing
263ebdfc6a4SZhang Yubing if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
264ebdfc6a4SZhang Yubing printf("%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
265ebdfc6a4SZhang Yubing aux->name, dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]);
266ebdfc6a4SZhang Yubing return 0;
267ebdfc6a4SZhang Yubing }
268ebdfc6a4SZhang Yubing
269ebdfc6a4SZhang Yubing if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
270ebdfc6a4SZhang Yubing return 0;
271ebdfc6a4SZhang Yubing
27243448503SZhang Yubing debug("%s: Base DPCD: %*ph\n",
273ebdfc6a4SZhang Yubing aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
274ebdfc6a4SZhang Yubing
275ebdfc6a4SZhang Yubing memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));
276ebdfc6a4SZhang Yubing
277ebdfc6a4SZhang Yubing return 0;
278ebdfc6a4SZhang Yubing }
279ebdfc6a4SZhang Yubing
drm_dp_read_dpcd_caps(struct drm_dp_aux * aux,u8 dpcd[DP_RECEIVER_CAP_SIZE])280ebdfc6a4SZhang Yubing int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
281ebdfc6a4SZhang Yubing u8 dpcd[DP_RECEIVER_CAP_SIZE])
282ebdfc6a4SZhang Yubing {
283ebdfc6a4SZhang Yubing int ret;
284ebdfc6a4SZhang Yubing
285ebdfc6a4SZhang Yubing ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
286ebdfc6a4SZhang Yubing if (ret < 0)
287ebdfc6a4SZhang Yubing return ret;
288ebdfc6a4SZhang Yubing if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
289ebdfc6a4SZhang Yubing return -EIO;
290ebdfc6a4SZhang Yubing
291ebdfc6a4SZhang Yubing ret = drm_dp_read_extended_dpcd_caps(aux, dpcd);
292ebdfc6a4SZhang Yubing if (ret < 0)
293ebdfc6a4SZhang Yubing return ret;
294ebdfc6a4SZhang Yubing
29543448503SZhang Yubing debug("%s: DPCD: %*ph\n",
296ebdfc6a4SZhang Yubing aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
297ebdfc6a4SZhang Yubing
298ebdfc6a4SZhang Yubing return ret;
299ebdfc6a4SZhang Yubing }
300ebdfc6a4SZhang Yubing
drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg * msg)301ebdfc6a4SZhang Yubing static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
302ebdfc6a4SZhang Yubing {
303ebdfc6a4SZhang Yubing /*
304ebdfc6a4SZhang Yubing * In case of i2c defer or short i2c ack reply to a write,
305ebdfc6a4SZhang Yubing * we need to switch to WRITE_STATUS_UPDATE to drain the
306ebdfc6a4SZhang Yubing * rest of the message
307ebdfc6a4SZhang Yubing */
308ebdfc6a4SZhang Yubing if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
309ebdfc6a4SZhang Yubing msg->request &= DP_AUX_I2C_MOT;
310ebdfc6a4SZhang Yubing msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
311ebdfc6a4SZhang Yubing }
312ebdfc6a4SZhang Yubing }
313ebdfc6a4SZhang Yubing
drm_dp_i2c_do_msg(struct drm_dp_aux * aux,struct drm_dp_aux_msg * msg)314ebdfc6a4SZhang Yubing static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
315ebdfc6a4SZhang Yubing {
316ebdfc6a4SZhang Yubing unsigned int retry, defer_i2c;
317ebdfc6a4SZhang Yubing int ret;
318ebdfc6a4SZhang Yubing /*
319ebdfc6a4SZhang Yubing * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
320ebdfc6a4SZhang Yubing * is required to retry at least seven times upon receiving AUX_DEFER
321ebdfc6a4SZhang Yubing * before giving up the AUX transaction.
322ebdfc6a4SZhang Yubing *
323ebdfc6a4SZhang Yubing * We also try to account for the i2c bus speed.
324ebdfc6a4SZhang Yubing */
325ebdfc6a4SZhang Yubing int max_retries = 7;
326ebdfc6a4SZhang Yubing
327ebdfc6a4SZhang Yubing for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c);
328ebdfc6a4SZhang Yubing retry++) {
329ebdfc6a4SZhang Yubing ret = aux->transfer(aux, msg);
330ebdfc6a4SZhang Yubing if (ret < 0) {
331ebdfc6a4SZhang Yubing if (ret == -EBUSY)
332ebdfc6a4SZhang Yubing continue;
333ebdfc6a4SZhang Yubing
334ebdfc6a4SZhang Yubing /*
335ebdfc6a4SZhang Yubing * While timeouts can be errors, they're usually normal
336ebdfc6a4SZhang Yubing * behavior (for instance, when a driver tries to
337ebdfc6a4SZhang Yubing * communicate with a non-existent DisplayPort device).
338ebdfc6a4SZhang Yubing * Avoid spamming the kernel log with timeout errors.
339ebdfc6a4SZhang Yubing */
340ebdfc6a4SZhang Yubing if (ret == -ETIMEDOUT)
341ebdfc6a4SZhang Yubing printf("%s: transaction timed out\n",
342ebdfc6a4SZhang Yubing aux->name);
343ebdfc6a4SZhang Yubing else
344ebdfc6a4SZhang Yubing printf("%s: transaction failed: %d\n",
345ebdfc6a4SZhang Yubing aux->name, ret);
346ebdfc6a4SZhang Yubing return ret;
347ebdfc6a4SZhang Yubing }
348ebdfc6a4SZhang Yubing
349ebdfc6a4SZhang Yubing switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
350ebdfc6a4SZhang Yubing case DP_AUX_NATIVE_REPLY_ACK:
351ebdfc6a4SZhang Yubing /*
352ebdfc6a4SZhang Yubing * For I2C-over-AUX transactions this isn't enough, we
353ebdfc6a4SZhang Yubing * need to check for the I2C ACK reply.
354ebdfc6a4SZhang Yubing */
355ebdfc6a4SZhang Yubing break;
356ebdfc6a4SZhang Yubing
357ebdfc6a4SZhang Yubing case DP_AUX_NATIVE_REPLY_NACK:
358ebdfc6a4SZhang Yubing printf("%s: native nack (result=%d, size=%zu)\n",
359ebdfc6a4SZhang Yubing aux->name, ret, msg->size);
360ebdfc6a4SZhang Yubing return -EREMOTEIO;
361ebdfc6a4SZhang Yubing
362ebdfc6a4SZhang Yubing case DP_AUX_NATIVE_REPLY_DEFER:
363ebdfc6a4SZhang Yubing printf("%s: native defer\n", aux->name);
364ebdfc6a4SZhang Yubing /*
365ebdfc6a4SZhang Yubing * We could check for I2C bit rate capabilities and if
366ebdfc6a4SZhang Yubing * available adjust this interval. We could also be
367ebdfc6a4SZhang Yubing * more careful with DP-to-legacy adapters where a
368ebdfc6a4SZhang Yubing * long legacy cable may force very low I2C bit rates.
369ebdfc6a4SZhang Yubing *
370ebdfc6a4SZhang Yubing * For now just defer for long enough to hopefully be
371ebdfc6a4SZhang Yubing * safe for all use-cases.
372ebdfc6a4SZhang Yubing */
373ebdfc6a4SZhang Yubing udelay(AUX_RETRY_INTERVAL);
374ebdfc6a4SZhang Yubing continue;
375ebdfc6a4SZhang Yubing
376ebdfc6a4SZhang Yubing default:
377ebdfc6a4SZhang Yubing printf("%s: invalid native reply %#04x\n",
378ebdfc6a4SZhang Yubing aux->name, msg->reply);
379ebdfc6a4SZhang Yubing return -EREMOTEIO;
380ebdfc6a4SZhang Yubing }
381ebdfc6a4SZhang Yubing
382ebdfc6a4SZhang Yubing switch (msg->reply & DP_AUX_I2C_REPLY_MASK) {
383ebdfc6a4SZhang Yubing case DP_AUX_I2C_REPLY_ACK:
384ebdfc6a4SZhang Yubing /*
385ebdfc6a4SZhang Yubing * Both native ACK and I2C ACK replies received. We
386ebdfc6a4SZhang Yubing * can assume the transfer was successful.
387ebdfc6a4SZhang Yubing */
388ebdfc6a4SZhang Yubing if (ret != msg->size)
389ebdfc6a4SZhang Yubing drm_dp_i2c_msg_write_status_update(msg);
390ebdfc6a4SZhang Yubing return ret;
391ebdfc6a4SZhang Yubing
392ebdfc6a4SZhang Yubing case DP_AUX_I2C_REPLY_NACK:
393ebdfc6a4SZhang Yubing printf("%s: I2C nack (result=%d, size=%zu)\n",
394ebdfc6a4SZhang Yubing aux->name, ret, msg->size);
395ebdfc6a4SZhang Yubing aux->i2c_nack_count++;
396ebdfc6a4SZhang Yubing return -EREMOTEIO;
397ebdfc6a4SZhang Yubing
398ebdfc6a4SZhang Yubing case DP_AUX_I2C_REPLY_DEFER:
399ebdfc6a4SZhang Yubing printf("%s: I2C defer\n", aux->name);
400ebdfc6a4SZhang Yubing /* DP Compliance Test 4.2.2.5 Requirement:
401ebdfc6a4SZhang Yubing * Must have at least 7 retries for I2C defers on the
402ebdfc6a4SZhang Yubing * transaction to pass this test
403ebdfc6a4SZhang Yubing */
404ebdfc6a4SZhang Yubing aux->i2c_defer_count++;
405ebdfc6a4SZhang Yubing if (defer_i2c < 7)
406ebdfc6a4SZhang Yubing defer_i2c++;
407ebdfc6a4SZhang Yubing udelay(AUX_RETRY_INTERVAL);
408ebdfc6a4SZhang Yubing drm_dp_i2c_msg_write_status_update(msg);
409ebdfc6a4SZhang Yubing
410ebdfc6a4SZhang Yubing continue;
411ebdfc6a4SZhang Yubing
412ebdfc6a4SZhang Yubing default:
413ebdfc6a4SZhang Yubing printf("%s: invalid I2C reply %#04x\n",
414ebdfc6a4SZhang Yubing aux->name, msg->reply);
415ebdfc6a4SZhang Yubing return -EREMOTEIO;
416ebdfc6a4SZhang Yubing }
417ebdfc6a4SZhang Yubing }
418ebdfc6a4SZhang Yubing
419ebdfc6a4SZhang Yubing printf("%s: Too many retries, giving up\n", aux->name);
420ebdfc6a4SZhang Yubing return -EREMOTEIO;
421ebdfc6a4SZhang Yubing }
422ebdfc6a4SZhang Yubing
drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg * msg,const struct i2c_msg * i2c_msg)423ebdfc6a4SZhang Yubing static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
424ebdfc6a4SZhang Yubing const struct i2c_msg *i2c_msg)
425ebdfc6a4SZhang Yubing {
426ebdfc6a4SZhang Yubing msg->request = (i2c_msg->flags & I2C_M_RD) ?
427ebdfc6a4SZhang Yubing DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
428ebdfc6a4SZhang Yubing if (!(i2c_msg->flags & I2C_M_STOP))
429ebdfc6a4SZhang Yubing msg->request |= DP_AUX_I2C_MOT;
430ebdfc6a4SZhang Yubing }
431ebdfc6a4SZhang Yubing
432ebdfc6a4SZhang Yubing /*
433ebdfc6a4SZhang Yubing * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
434ebdfc6a4SZhang Yubing *
435ebdfc6a4SZhang Yubing * Returns an error code on failure, or a recommended transfer size on success.
436ebdfc6a4SZhang Yubing */
drm_dp_i2c_drain_msg(struct drm_dp_aux * aux,struct drm_dp_aux_msg * orig_msg)437ebdfc6a4SZhang Yubing static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux,
438ebdfc6a4SZhang Yubing struct drm_dp_aux_msg *orig_msg)
439ebdfc6a4SZhang Yubing {
440ebdfc6a4SZhang Yubing int err, ret = orig_msg->size;
441ebdfc6a4SZhang Yubing struct drm_dp_aux_msg msg = *orig_msg;
442ebdfc6a4SZhang Yubing
443ebdfc6a4SZhang Yubing while (msg.size > 0) {
444ebdfc6a4SZhang Yubing err = drm_dp_i2c_do_msg(aux, &msg);
445ebdfc6a4SZhang Yubing if (err <= 0)
446ebdfc6a4SZhang Yubing return err == 0 ? -EPROTO : err;
447ebdfc6a4SZhang Yubing
448ebdfc6a4SZhang Yubing if (err < msg.size && err < ret) {
449ebdfc6a4SZhang Yubing printf("%s: Reply: requested %zu bytes got %d bytes\n",
450ebdfc6a4SZhang Yubing aux->name, msg.size, err);
451ebdfc6a4SZhang Yubing ret = err;
452ebdfc6a4SZhang Yubing }
453ebdfc6a4SZhang Yubing
454ebdfc6a4SZhang Yubing msg.size -= err;
455ebdfc6a4SZhang Yubing msg.buffer += err;
456ebdfc6a4SZhang Yubing }
457ebdfc6a4SZhang Yubing
458ebdfc6a4SZhang Yubing return ret;
459ebdfc6a4SZhang Yubing }
460ebdfc6a4SZhang Yubing
drm_dp_i2c_xfer(struct ddc_adapter * adapter,struct i2c_msg * msgs,int num)461ebdfc6a4SZhang Yubing int drm_dp_i2c_xfer(struct ddc_adapter *adapter, struct i2c_msg *msgs,
462ebdfc6a4SZhang Yubing int num)
463ebdfc6a4SZhang Yubing {
464ebdfc6a4SZhang Yubing struct drm_dp_aux *aux = container_of(adapter, struct drm_dp_aux, ddc);
465ebdfc6a4SZhang Yubing unsigned int i, j;
466ebdfc6a4SZhang Yubing unsigned int transfer_size;
467ebdfc6a4SZhang Yubing struct drm_dp_aux_msg msg;
468ebdfc6a4SZhang Yubing int err = 0;
469ebdfc6a4SZhang Yubing
470ebdfc6a4SZhang Yubing memset(&msg, 0, sizeof(msg));
471ebdfc6a4SZhang Yubing
472ebdfc6a4SZhang Yubing for (i = 0; i < num; i++) {
473ebdfc6a4SZhang Yubing msg.address = msgs[i].addr;
474ebdfc6a4SZhang Yubing drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
475ebdfc6a4SZhang Yubing /* Send a bare address packet to start the transaction.
476ebdfc6a4SZhang Yubing * Zero sized messages specify an address only (bare
477ebdfc6a4SZhang Yubing * address) transaction.
478ebdfc6a4SZhang Yubing */
479ebdfc6a4SZhang Yubing msg.buffer = NULL;
480ebdfc6a4SZhang Yubing msg.size = 0;
481ebdfc6a4SZhang Yubing err = drm_dp_i2c_do_msg(aux, &msg);
482ebdfc6a4SZhang Yubing
483ebdfc6a4SZhang Yubing /*
484ebdfc6a4SZhang Yubing * Reset msg.request in case in case it got
485ebdfc6a4SZhang Yubing * changed into a WRITE_STATUS_UPDATE.
486ebdfc6a4SZhang Yubing */
487ebdfc6a4SZhang Yubing drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
488ebdfc6a4SZhang Yubing
489ebdfc6a4SZhang Yubing if (err < 0)
490ebdfc6a4SZhang Yubing break;
491ebdfc6a4SZhang Yubing /* We want each transaction to be as large as possible, but
492ebdfc6a4SZhang Yubing * we'll go to smaller sizes if the hardware gives us a
493ebdfc6a4SZhang Yubing * short reply.
494ebdfc6a4SZhang Yubing */
495ebdfc6a4SZhang Yubing transfer_size = DP_AUX_MAX_PAYLOAD_BYTES;
496ebdfc6a4SZhang Yubing for (j = 0; j < msgs[i].len; j += msg.size) {
497ebdfc6a4SZhang Yubing msg.buffer = msgs[i].buf + j;
498ebdfc6a4SZhang Yubing msg.size = min(transfer_size, msgs[i].len - j);
499ebdfc6a4SZhang Yubing
500ebdfc6a4SZhang Yubing err = drm_dp_i2c_drain_msg(aux, &msg);
501ebdfc6a4SZhang Yubing
502ebdfc6a4SZhang Yubing /*
503ebdfc6a4SZhang Yubing * Reset msg.request in case in case it got
504ebdfc6a4SZhang Yubing * changed into a WRITE_STATUS_UPDATE.
505ebdfc6a4SZhang Yubing */
506ebdfc6a4SZhang Yubing drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
507ebdfc6a4SZhang Yubing
508ebdfc6a4SZhang Yubing if (err < 0)
509ebdfc6a4SZhang Yubing break;
510ebdfc6a4SZhang Yubing transfer_size = err;
511ebdfc6a4SZhang Yubing }
512ebdfc6a4SZhang Yubing if (err < 0)
513ebdfc6a4SZhang Yubing break;
514ebdfc6a4SZhang Yubing }
515ebdfc6a4SZhang Yubing if (err >= 0)
516ebdfc6a4SZhang Yubing err = num;
517ebdfc6a4SZhang Yubing /* Send a bare address packet to close out the transaction.
518ebdfc6a4SZhang Yubing * Zero sized messages specify an address only (bare
519ebdfc6a4SZhang Yubing * address) transaction.
520ebdfc6a4SZhang Yubing */
521ebdfc6a4SZhang Yubing msg.request &= ~DP_AUX_I2C_MOT;
522ebdfc6a4SZhang Yubing msg.buffer = NULL;
523ebdfc6a4SZhang Yubing msg.size = 0;
524ebdfc6a4SZhang Yubing (void)drm_dp_i2c_do_msg(aux, &msg);
525ebdfc6a4SZhang Yubing
526ebdfc6a4SZhang Yubing return err;
527ebdfc6a4SZhang Yubing }
528ebdfc6a4SZhang Yubing
529