blob: 0a3fb2447927ae795090f2169b10e52b423ec16f [file] [log] [blame]
Ajay Singh Parmarae725622017-03-22 00:03:39 -07001/*
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -08002 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
Ajay Singh Parmarae725622017-03-22 00:03:39 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
16
17#include <linux/delay.h>
18
19#include "dp_aux.h"
20
21#define DP_AUX_ENUM_STR(x) #x
22
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070023enum {
24 DP_AUX_DATA_INDEX_WRITE = BIT(31),
Ajay Singh Parmarae725622017-03-22 00:03:39 -070025};
26
27struct dp_aux_private {
28 struct device *dev;
29 struct dp_aux dp_aux;
30 struct dp_catalog_aux *catalog;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +053031 struct dp_aux_cfg *cfg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070032 struct mutex mutex;
33 struct completion comp;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -080034 struct drm_dp_aux drm_aux;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070035
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -070036 bool cmd_busy;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070037 bool native;
38 bool read;
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -080039 bool no_send_addr;
40 bool no_send_stop;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -080041
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -070042 u32 offset;
43 u32 segment;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -080044 u32 aux_error_num;
45 u32 retry_cnt;
46
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +053047 atomic_t aborted;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070048
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -080049 u8 *dpcd;
50 u8 *edid;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070051};
52
53static char *dp_aux_get_error(u32 aux_error)
54{
55 switch (aux_error) {
56 case DP_AUX_ERR_NONE:
57 return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
58 case DP_AUX_ERR_ADDR:
59 return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
60 case DP_AUX_ERR_TOUT:
61 return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
62 case DP_AUX_ERR_NACK:
63 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
64 case DP_AUX_ERR_DEFER:
65 return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
66 case DP_AUX_ERR_NACK_DEFER:
67 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
68 default:
69 return "unknown";
70 }
71}
72
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070073static u32 dp_aux_write(struct dp_aux_private *aux,
74 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -070075{
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070076 u32 data[4], reg, len;
77 u8 *msgdata = msg->buffer;
78 int const aux_cmd_fifo_len = 128;
79 int i = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070080
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070081 if (aux->read)
Ajay Singh Parmarae725622017-03-22 00:03:39 -070082 len = 4;
83 else
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070084 len = msg->size + 4;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070085
86 /*
87 * cmd fifo only has depth of 144 bytes
88 * limit buf length to 128 bytes here
89 */
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070090 if (len > aux_cmd_fifo_len) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -070091 pr_err("buf len error\n");
92 return 0;
93 }
94
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070095 /* Pack cmd and write to HW */
96 data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
97 if (aux->read)
98 data[0] |= BIT(4); /* R/W */
Ajay Singh Parmarae725622017-03-22 00:03:39 -070099
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700100 data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
101 data[2] = msg->address & 0xff; /* addr[7:0] */
102 data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700103
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700104 for (i = 0; i < len; i++) {
105 reg = (i < 4) ? data[i] : msgdata[i - 4];
106 reg = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
107 if (i == 0)
108 reg |= DP_AUX_DATA_INDEX_WRITE;
109 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700110 aux->catalog->write_data(aux->catalog);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700111 }
112
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700113 aux->catalog->clear_trans(aux->catalog, false);
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530114 aux->catalog->clear_hw_interrupts(aux->catalog);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700115
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700116 reg = 0; /* Transaction number == 1 */
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800117 if (!aux->native) { /* i2c */
118 reg |= BIT(8);
119
120 if (aux->no_send_addr)
121 reg |= BIT(10);
122
123 if (aux->no_send_stop)
124 reg |= BIT(11);
125 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700126
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700127 reg |= BIT(9);
128 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700129 aux->catalog->write_trans(aux->catalog);
130
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700131 return len;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700132}
133
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700134static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
135 struct drm_dp_aux_msg *msg)
136{
137 u32 ret = 0, len = 0, timeout;
138 int const aux_timeout_ms = HZ/4;
139
140 reinit_completion(&aux->comp);
141
142 len = dp_aux_write(aux, msg);
143 if (len == 0) {
144 pr_err("DP AUX write failed\n");
145 return -EINVAL;
146 }
147
148 timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
149 if (!timeout) {
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530150 pr_err("aux %s timeout\n", (aux->read ? "read" : "write"));
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700151 return -ETIMEDOUT;
152 }
153
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700154 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700155 ret = len;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700156 } else {
157 pr_err_ratelimited("aux err: %s\n",
158 dp_aux_get_error(aux->aux_error_num));
159
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700160 ret = -EINVAL;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700161 }
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700162
163 return ret;
164}
165
166static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
167 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700168{
169 u32 data;
170 u8 *dp;
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800171 u32 i, actual_i;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700172 u32 len = msg->size;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700173
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700174 aux->catalog->clear_trans(aux->catalog, true);
175
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700176 data = 0;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700177 data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700178 data |= BIT(0); /* read */
179
180 aux->catalog->data = data;
181 aux->catalog->write_data(aux->catalog);
182
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700183 dp = msg->buffer;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700184
185 /* discard first byte */
186 data = aux->catalog->read_data(aux->catalog);
187
188 for (i = 0; i < len; i++) {
189 data = aux->catalog->read_data(aux->catalog);
190 *dp++ = (u8)((data >> 8) & 0xff);
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800191
192 actual_i = (data >> 16) & 0xFF;
193 if (i != actual_i)
194 pr_warn("Index mismatch: expected %d, found %d\n",
195 i, actual_i);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700196 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700197}
198
199static void dp_aux_native_handler(struct dp_aux_private *aux)
200{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700201 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700202
203 if (isr & DP_INTR_AUX_I2C_DONE)
204 aux->aux_error_num = DP_AUX_ERR_NONE;
205 else if (isr & DP_INTR_WRONG_ADDR)
206 aux->aux_error_num = DP_AUX_ERR_ADDR;
207 else if (isr & DP_INTR_TIMEOUT)
208 aux->aux_error_num = DP_AUX_ERR_TOUT;
209 if (isr & DP_INTR_NACK_DEFER)
210 aux->aux_error_num = DP_AUX_ERR_NACK;
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530211 if (isr & DP_INTR_AUX_ERROR) {
212 aux->aux_error_num = DP_AUX_ERR_PHY;
213 aux->catalog->clear_hw_interrupts(aux->catalog);
214 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700215
216 complete(&aux->comp);
217}
218
219static void dp_aux_i2c_handler(struct dp_aux_private *aux)
220{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700221 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700222
223 if (isr & DP_INTR_AUX_I2C_DONE) {
224 if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
225 aux->aux_error_num = DP_AUX_ERR_NACK;
226 else
227 aux->aux_error_num = DP_AUX_ERR_NONE;
228 } else {
229 if (isr & DP_INTR_WRONG_ADDR)
230 aux->aux_error_num = DP_AUX_ERR_ADDR;
231 else if (isr & DP_INTR_TIMEOUT)
232 aux->aux_error_num = DP_AUX_ERR_TOUT;
233 if (isr & DP_INTR_NACK_DEFER)
234 aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
235 if (isr & DP_INTR_I2C_NACK)
236 aux->aux_error_num = DP_AUX_ERR_NACK;
237 if (isr & DP_INTR_I2C_DEFER)
238 aux->aux_error_num = DP_AUX_ERR_DEFER;
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530239 if (isr & DP_INTR_AUX_ERROR) {
240 aux->aux_error_num = DP_AUX_ERR_PHY;
241 aux->catalog->clear_hw_interrupts(aux->catalog);
242 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700243 }
244
245 complete(&aux->comp);
246}
247
248static void dp_aux_isr(struct dp_aux *dp_aux)
249{
250 struct dp_aux_private *aux;
251
252 if (!dp_aux) {
253 pr_err("invalid input\n");
254 return;
255 }
256
257 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
258
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700259 aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
260
261 if (!aux->cmd_busy)
262 return;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700263
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700264 if (aux->native)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700265 dp_aux_native_handler(aux);
266 else
267 dp_aux_i2c_handler(aux);
268}
269
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530270static void dp_aux_reconfig(struct dp_aux *dp_aux)
271{
272 struct dp_aux_private *aux;
273
274 if (!dp_aux) {
275 pr_err("invalid input\n");
276 return;
277 }
278
279 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
280
281 aux->catalog->update_aux_cfg(aux->catalog,
282 aux->cfg, PHY_AUX_CFG1);
283 aux->catalog->reset(aux->catalog);
284}
285
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530286static void dp_aux_abort_transaction(struct dp_aux *dp_aux)
287{
288 struct dp_aux_private *aux;
289
290 if (!dp_aux) {
291 pr_err("invalid input\n");
292 return;
293 }
294
295 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
296
297 atomic_set(&aux->aborted, 1);
298}
299
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700300static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
301 struct drm_dp_aux_msg *input_msg)
302{
303 u32 const edid_address = 0x50;
304 u32 const segment_address = 0x30;
305 bool i2c_read = input_msg->request &
306 (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
307 u8 *data = NULL;
308
309 if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
310 (input_msg->address != segment_address)))
311 return;
312
313
314 data = input_msg->buffer;
315 if (input_msg->address == segment_address)
316 aux->segment = *data;
317 else
318 aux->offset = *data;
319}
320
321/**
322 * dp_aux_transfer_helper() - helper function for EDID read transactions
323 *
324 * @aux: DP AUX private structure
325 * @input_msg: input message from DRM upstream APIs
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800326 * @send_seg: send the seg to sink
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700327 *
328 * return: void
329 *
330 * This helper function is used to fix EDID reads for non-compliant
331 * sinks that do not handle the i2c middle-of-transaction flag correctly.
332 */
333static void dp_aux_transfer_helper(struct dp_aux_private *aux,
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800334 struct drm_dp_aux_msg *input_msg, bool send_seg)
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700335{
336 struct drm_dp_aux_msg helper_msg;
337 u32 const message_size = 0x10;
338 u32 const segment_address = 0x30;
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530339 u32 const edid_block_length = 0x80;
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700340 bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
341 bool i2c_read = input_msg->request &
342 (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
343
344 if (!i2c_mot || !i2c_read || (input_msg->size == 0))
345 return;
346
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530347 /*
348 * Sending the segment value and EDID offset will be performed
349 * from the DRM upstream EDID driver for each block. Avoid
350 * duplicate AUX transactions related to this while reading the
351 * first 16 bytes of each block.
352 */
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800353 if (!(aux->offset % edid_block_length) || !send_seg)
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530354 goto end;
355
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700356 aux->read = false;
357 aux->cmd_busy = true;
358 aux->no_send_addr = true;
359 aux->no_send_stop = true;
360
361 /*
Padmanabhan Komanduru729c0022017-11-09 17:17:08 +0530362 * Send the segment address for i2c reads for segment > 0 and for which
363 * the middle-of-transaction flag is set. This is required to support
364 * EDID reads of more than 2 blocks as the segment address is reset to 0
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700365 * since we are overriding the middle-of-transaction flag for read
366 * transactions.
367 */
Padmanabhan Komanduru729c0022017-11-09 17:17:08 +0530368 if (aux->segment) {
369 memset(&helper_msg, 0, sizeof(helper_msg));
370 helper_msg.address = segment_address;
371 helper_msg.buffer = &aux->segment;
372 helper_msg.size = 1;
373 dp_aux_cmd_fifo_tx(aux, &helper_msg);
374 }
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700375
376 /*
377 * Send the offset address for every i2c read in which the
378 * middle-of-transaction flag is set. This will ensure that the sink
379 * will update its read pointer and return the correct portion of the
380 * EDID buffer in the subsequent i2c read trasntion triggered in the
381 * native AUX transfer function.
382 */
383 memset(&helper_msg, 0, sizeof(helper_msg));
384 helper_msg.address = input_msg->address;
385 helper_msg.buffer = &aux->offset;
386 helper_msg.size = 1;
387 dp_aux_cmd_fifo_tx(aux, &helper_msg);
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530388end:
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700389 aux->offset += message_size;
390
391 if (aux->offset == 0x80 || aux->offset == 0x100)
392 aux->segment = 0x0; /* reset segment at end of block */
393}
394
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800395static int dp_aux_transfer_ready(struct dp_aux_private *aux,
396 struct drm_dp_aux_msg *msg, bool send_seg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700397{
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800398 int ret = 0;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700399 int const aux_cmd_native_max = 16;
400 int const aux_cmd_i2c_max = 128;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700401
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530402 if (atomic_read(&aux->aborted)) {
403 ret = -ETIMEDOUT;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800404 goto error;
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530405 }
406
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700407 aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700408
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700409 /* Ignore address only message */
410 if ((msg->size == 0) || (msg->buffer == NULL)) {
411 msg->reply = aux->native ?
412 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800413 goto error;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700414 }
415
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700416 /* msg sanity check */
417 if ((aux->native && (msg->size > aux_cmd_native_max)) ||
418 (msg->size > aux_cmd_i2c_max)) {
419 pr_err("%s: invalid msg: size(%zu), request(%x)\n",
420 __func__, msg->size, msg->request);
421 ret = -EINVAL;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800422 goto error;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700423 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700424
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700425 dp_aux_update_offset_and_segment(aux, msg);
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800426
427 dp_aux_transfer_helper(aux, msg, send_seg);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700428
429 aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700430
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800431 if (aux->read) {
432 aux->no_send_addr = true;
433 aux->no_send_stop = false;
434 } else {
435 aux->no_send_addr = true;
436 aux->no_send_stop = true;
437 }
438
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800439 aux->cmd_busy = true;
440error:
441 return ret;
442}
443
444static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
445 struct drm_dp_aux_msg *msg)
446{
447 u8 buf[SZ_64];
448 u32 timeout;
449 ssize_t ret;
450 struct dp_aux_private *aux = container_of(drm_aux,
451 struct dp_aux_private, drm_aux);
452
453 ret = dp_aux_transfer_ready(aux, msg, false);
454 if (ret)
455 goto end;
456
457 aux->aux_error_num = DP_AUX_ERR_NONE;
458
459 if (aux->native) {
460 if (aux->read && ((msg->address + msg->size) < SZ_1K)) {
461 aux->dp_aux.reg = msg->address;
462
463 reinit_completion(&aux->comp);
464 timeout = wait_for_completion_timeout(&aux->comp, HZ);
465 if (!timeout)
466 pr_err("aux timeout for 0x%x\n", msg->address);
467
468 aux->dp_aux.reg = 0xFFFF;
469
470 memcpy(msg->buffer, aux->dpcd + msg->address,
471 msg->size);
472 aux->aux_error_num = DP_AUX_ERR_NONE;
473 } else {
474 memset(msg->buffer, 0, msg->size);
475 }
476 } else {
477 if (aux->read && msg->address == 0x50) {
478 memcpy(msg->buffer,
479 aux->edid + aux->offset - 16,
480 msg->size);
481 }
482 }
483
484 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
485 snprintf(buf, SZ_64, "[drm-dp] dbg: %5s %5s %5xh(%2zu): ",
486 aux->native ? "NATIVE" : "I2C",
487 aux->read ? "READ" : "WRITE",
488 msg->address, msg->size);
489
490 print_hex_dump(KERN_DEBUG, buf,
491 DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);
492
493 msg->reply = aux->native ?
494 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
495 } else {
496 /* Reply defer to retry */
497 msg->reply = aux->native ?
498 DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
499 }
500
501 ret = msg->size;
502end:
503 return ret;
504}
505
506/*
507 * This function does the real job to process an AUX transaction.
508 * It will call aux_reset() function to reset the AUX channel,
509 * if the waiting is timeout.
510 */
511static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
512 struct drm_dp_aux_msg *msg)
513{
514 u8 buf[SZ_64];
515 ssize_t ret;
516 int const retry_count = 5;
517 struct dp_aux_private *aux = container_of(drm_aux,
518 struct dp_aux_private, drm_aux);
519
520 mutex_lock(&aux->mutex);
521
522 ret = dp_aux_transfer_ready(aux, msg, true);
523 if (ret)
524 goto unlock_exit;
525
526 if (!aux->cmd_busy) {
527 ret = msg->size;
528 goto unlock_exit;
529 }
530
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700531 ret = dp_aux_cmd_fifo_tx(aux, msg);
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530532 if ((ret < 0) && aux->native && !atomic_read(&aux->aborted)) {
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530533 aux->retry_cnt++;
534 if (!(aux->retry_cnt % retry_count))
535 aux->catalog->update_aux_cfg(aux->catalog,
536 aux->cfg, PHY_AUX_CFG1);
537 aux->catalog->reset(aux->catalog);
538 goto unlock_exit;
539 } else if (ret < 0) {
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700540 goto unlock_exit;
541 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700542
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700543 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
544 if (aux->read)
545 dp_aux_cmd_fifo_rx(aux, msg);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700546
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800547 snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
548 aux->native ? "NATIVE" : "I2C",
549 aux->read ? "READ" : "WRITE",
550 msg->address, msg->size);
551
552 print_hex_dump(KERN_DEBUG, buf,
553 DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);
554
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700555 msg->reply = aux->native ?
556 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
557 } else {
558 /* Reply defer to retry */
559 msg->reply = aux->native ?
560 DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
561 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700562
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700563 /* Return requested size for success or retry */
564 ret = msg->size;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530565 aux->retry_cnt = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700566
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700567unlock_exit:
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700568 aux->cmd_busy = false;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700569 mutex_unlock(&aux->mutex);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700570 return ret;
571}
572
Padmanabhan Komanduru20a21db2017-07-10 16:58:59 +0530573static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
574{
575 int i = 0;
576
577 for (i = 0; i < PHY_AUX_CFG_MAX; i++)
578 aux_cfg[i].current_index = 0;
579}
580
581static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700582{
583 struct dp_aux_private *aux;
584
Padmanabhan Komanduru20a21db2017-07-10 16:58:59 +0530585 if (!dp_aux || !aux_cfg) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700586 pr_err("invalid input\n");
587 return;
588 }
589
590 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
591
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530592 dp_aux_reset_phy_config_indices(aux_cfg);
593 aux->catalog->setup(aux->catalog, aux_cfg);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700594 aux->catalog->reset(aux->catalog);
595 aux->catalog->enable(aux->catalog, true);
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530596 atomic_set(&aux->aborted, 0);
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530597 aux->retry_cnt = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700598}
599
600static void dp_aux_deinit(struct dp_aux *dp_aux)
601{
602 struct dp_aux_private *aux;
603
604 if (!dp_aux) {
605 pr_err("invalid input\n");
606 return;
607 }
608
609 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
610
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530611 atomic_set(&aux->aborted, 1);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700612 aux->catalog->enable(aux->catalog, false);
613}
614
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700615static int dp_aux_register(struct dp_aux *dp_aux)
616{
617 struct dp_aux_private *aux;
618 int ret = 0;
619
620 if (!dp_aux) {
621 pr_err("invalid input\n");
622 ret = -EINVAL;
623 goto exit;
624 }
625
626 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
627
628 aux->drm_aux.name = "sde_dp_aux";
629 aux->drm_aux.dev = aux->dev;
630 aux->drm_aux.transfer = dp_aux_transfer;
631 ret = drm_dp_aux_register(&aux->drm_aux);
632 if (ret) {
633 pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
634 goto exit;
635 }
636 dp_aux->drm_aux = &aux->drm_aux;
637exit:
638 return ret;
639}
640
641static void dp_aux_deregister(struct dp_aux *dp_aux)
642{
643 struct dp_aux_private *aux;
644
645 if (!dp_aux) {
646 pr_err("invalid input\n");
647 return;
648 }
649
650 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
651 drm_dp_aux_unregister(&aux->drm_aux);
652}
653
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800654static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
655{
656 struct dp_aux_private *aux;
657
658 if (!dp_aux) {
659 pr_err("invalid input\n");
660 return;
661 }
662
663 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
664
665 complete(&aux->comp);
666}
667
668static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
669 u8 *edid, u8 *dpcd)
670{
671 struct dp_aux_private *aux;
672
673 if (!dp_aux) {
674 pr_err("invalid input\n");
675 return;
676 }
677
678 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
679
680 aux->edid = edid;
681 aux->dpcd = dpcd;
682
683 if (en)
684 aux->drm_aux.transfer = dp_aux_transfer_debug;
685 else
686 aux->drm_aux.transfer = dp_aux_transfer;
687}
688
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530689struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
690 struct dp_aux_cfg *aux_cfg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700691{
692 int rc = 0;
693 struct dp_aux_private *aux;
694 struct dp_aux *dp_aux;
695
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530696 if (!catalog || !aux_cfg) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700697 pr_err("invalid input\n");
698 rc = -ENODEV;
699 goto error;
700 }
701
702 aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
703 if (!aux) {
704 rc = -ENOMEM;
705 goto error;
706 }
707
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700708 init_completion(&aux->comp);
709 aux->cmd_busy = false;
710 mutex_init(&aux->mutex);
711
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700712 aux->dev = dev;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700713 aux->catalog = catalog;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530714 aux->cfg = aux_cfg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700715 dp_aux = &aux->dp_aux;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530716 aux->retry_cnt = 0;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800717 aux->dp_aux.reg = 0xFFFF;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700718
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700719 dp_aux->isr = dp_aux_isr;
720 dp_aux->init = dp_aux_init;
721 dp_aux->deinit = dp_aux_deinit;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700722 dp_aux->drm_aux_register = dp_aux_register;
723 dp_aux->drm_aux_deregister = dp_aux_deregister;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530724 dp_aux->reconfig = dp_aux_reconfig;
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530725 dp_aux->abort = dp_aux_abort_transaction;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800726 dp_aux->dpcd_updated = dp_aux_dpcd_updated;
727 dp_aux->set_sim_mode = dp_aux_set_sim_mode;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700728
729 return dp_aux;
730error:
731 return ERR_PTR(rc);
732}
733
734void dp_aux_put(struct dp_aux *dp_aux)
735{
736 struct dp_aux_private *aux;
737
738 if (!dp_aux)
739 return;
740
741 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
742
Tatenda Chipeperekwa47ddbcd2017-08-30 13:43:21 -0700743 mutex_destroy(&aux->mutex);
744
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700745 devm_kfree(aux->dev, aux);
746}