blob: 630b5fb9da8f934b818aa4b0244b1a38e0709044 [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
Samantha Tranb6a5cd82018-02-01 14:47:16 -080053#ifdef CONFIG_DYNAMIC_DEBUG
54static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
55 struct drm_dp_aux_msg *msg)
56{
57 DEFINE_DYNAMIC_DEBUG_METADATA(ddm, "dp aux tracker");
58
59 if (unlikely(ddm.flags & _DPRINTK_FLAGS_PRINT)) {
60 u8 buf[SZ_64];
61 struct dp_aux_private *aux = container_of(drm_aux,
62 struct dp_aux_private, drm_aux);
63
64 snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
65 aux->native ? "NATIVE" : "I2C",
66 aux->read ? "READ" : "WRITE",
67 msg->address, msg->size);
68
69 print_hex_dump(KERN_DEBUG, buf, DUMP_PREFIX_NONE,
70 8, 1, msg->buffer, msg->size, false);
71 }
72}
73#else
74static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
75 struct drm_dp_aux_msg *msg)
76{
77}
78#endif
79
Ajay Singh Parmarae725622017-03-22 00:03:39 -070080static char *dp_aux_get_error(u32 aux_error)
81{
82 switch (aux_error) {
83 case DP_AUX_ERR_NONE:
84 return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
85 case DP_AUX_ERR_ADDR:
86 return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
87 case DP_AUX_ERR_TOUT:
88 return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
89 case DP_AUX_ERR_NACK:
90 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
91 case DP_AUX_ERR_DEFER:
92 return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
93 case DP_AUX_ERR_NACK_DEFER:
94 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
95 default:
96 return "unknown";
97 }
98}
99
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700100static u32 dp_aux_write(struct dp_aux_private *aux,
101 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700102{
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700103 u32 data[4], reg, len;
104 u8 *msgdata = msg->buffer;
105 int const aux_cmd_fifo_len = 128;
106 int i = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700107
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700108 if (aux->read)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700109 len = 4;
110 else
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700111 len = msg->size + 4;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700112
113 /*
114 * cmd fifo only has depth of 144 bytes
115 * limit buf length to 128 bytes here
116 */
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700117 if (len > aux_cmd_fifo_len) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700118 pr_err("buf len error\n");
119 return 0;
120 }
121
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700122 /* Pack cmd and write to HW */
123 data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
124 if (aux->read)
125 data[0] |= BIT(4); /* R/W */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700126
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700127 data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
128 data[2] = msg->address & 0xff; /* addr[7:0] */
129 data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700130
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700131 for (i = 0; i < len; i++) {
132 reg = (i < 4) ? data[i] : msgdata[i - 4];
133 reg = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
134 if (i == 0)
135 reg |= DP_AUX_DATA_INDEX_WRITE;
136 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700137 aux->catalog->write_data(aux->catalog);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700138 }
139
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700140 aux->catalog->clear_trans(aux->catalog, false);
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530141 aux->catalog->clear_hw_interrupts(aux->catalog);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700142
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700143 reg = 0; /* Transaction number == 1 */
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800144 if (!aux->native) { /* i2c */
145 reg |= BIT(8);
146
147 if (aux->no_send_addr)
148 reg |= BIT(10);
149
150 if (aux->no_send_stop)
151 reg |= BIT(11);
152 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700153
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700154 reg |= BIT(9);
155 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700156 aux->catalog->write_trans(aux->catalog);
157
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700158 return len;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700159}
160
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700161static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
162 struct drm_dp_aux_msg *msg)
163{
164 u32 ret = 0, len = 0, timeout;
165 int const aux_timeout_ms = HZ/4;
166
167 reinit_completion(&aux->comp);
168
169 len = dp_aux_write(aux, msg);
170 if (len == 0) {
171 pr_err("DP AUX write failed\n");
172 return -EINVAL;
173 }
174
175 timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
176 if (!timeout) {
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530177 pr_err("aux %s timeout\n", (aux->read ? "read" : "write"));
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700178 return -ETIMEDOUT;
179 }
180
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700181 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700182 ret = len;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700183 } else {
184 pr_err_ratelimited("aux err: %s\n",
185 dp_aux_get_error(aux->aux_error_num));
186
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700187 ret = -EINVAL;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700188 }
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700189
190 return ret;
191}
192
193static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
194 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700195{
196 u32 data;
197 u8 *dp;
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800198 u32 i, actual_i;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700199 u32 len = msg->size;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700200
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700201 aux->catalog->clear_trans(aux->catalog, true);
202
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700203 data = 0;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700204 data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700205 data |= BIT(0); /* read */
206
207 aux->catalog->data = data;
208 aux->catalog->write_data(aux->catalog);
209
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700210 dp = msg->buffer;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700211
212 /* discard first byte */
213 data = aux->catalog->read_data(aux->catalog);
214
215 for (i = 0; i < len; i++) {
216 data = aux->catalog->read_data(aux->catalog);
217 *dp++ = (u8)((data >> 8) & 0xff);
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800218
219 actual_i = (data >> 16) & 0xFF;
220 if (i != actual_i)
221 pr_warn("Index mismatch: expected %d, found %d\n",
222 i, actual_i);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700223 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700224}
225
226static void dp_aux_native_handler(struct dp_aux_private *aux)
227{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700228 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700229
230 if (isr & DP_INTR_AUX_I2C_DONE)
231 aux->aux_error_num = DP_AUX_ERR_NONE;
232 else if (isr & DP_INTR_WRONG_ADDR)
233 aux->aux_error_num = DP_AUX_ERR_ADDR;
234 else if (isr & DP_INTR_TIMEOUT)
235 aux->aux_error_num = DP_AUX_ERR_TOUT;
236 if (isr & DP_INTR_NACK_DEFER)
237 aux->aux_error_num = DP_AUX_ERR_NACK;
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530238 if (isr & DP_INTR_AUX_ERROR) {
239 aux->aux_error_num = DP_AUX_ERR_PHY;
240 aux->catalog->clear_hw_interrupts(aux->catalog);
241 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700242
243 complete(&aux->comp);
244}
245
246static void dp_aux_i2c_handler(struct dp_aux_private *aux)
247{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700248 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700249
250 if (isr & DP_INTR_AUX_I2C_DONE) {
251 if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
252 aux->aux_error_num = DP_AUX_ERR_NACK;
253 else
254 aux->aux_error_num = DP_AUX_ERR_NONE;
255 } else {
256 if (isr & DP_INTR_WRONG_ADDR)
257 aux->aux_error_num = DP_AUX_ERR_ADDR;
258 else if (isr & DP_INTR_TIMEOUT)
259 aux->aux_error_num = DP_AUX_ERR_TOUT;
260 if (isr & DP_INTR_NACK_DEFER)
261 aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
262 if (isr & DP_INTR_I2C_NACK)
263 aux->aux_error_num = DP_AUX_ERR_NACK;
264 if (isr & DP_INTR_I2C_DEFER)
265 aux->aux_error_num = DP_AUX_ERR_DEFER;
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530266 if (isr & DP_INTR_AUX_ERROR) {
267 aux->aux_error_num = DP_AUX_ERR_PHY;
268 aux->catalog->clear_hw_interrupts(aux->catalog);
269 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700270 }
271
272 complete(&aux->comp);
273}
274
275static void dp_aux_isr(struct dp_aux *dp_aux)
276{
277 struct dp_aux_private *aux;
278
279 if (!dp_aux) {
280 pr_err("invalid input\n");
281 return;
282 }
283
284 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
285
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700286 aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
287
288 if (!aux->cmd_busy)
289 return;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700290
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700291 if (aux->native)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700292 dp_aux_native_handler(aux);
293 else
294 dp_aux_i2c_handler(aux);
295}
296
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530297static void dp_aux_reconfig(struct dp_aux *dp_aux)
298{
299 struct dp_aux_private *aux;
300
301 if (!dp_aux) {
302 pr_err("invalid input\n");
303 return;
304 }
305
306 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
307
308 aux->catalog->update_aux_cfg(aux->catalog,
309 aux->cfg, PHY_AUX_CFG1);
310 aux->catalog->reset(aux->catalog);
311}
312
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530313static void dp_aux_abort_transaction(struct dp_aux *dp_aux)
314{
315 struct dp_aux_private *aux;
316
317 if (!dp_aux) {
318 pr_err("invalid input\n");
319 return;
320 }
321
322 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
323
324 atomic_set(&aux->aborted, 1);
325}
326
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700327static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
328 struct drm_dp_aux_msg *input_msg)
329{
330 u32 const edid_address = 0x50;
331 u32 const segment_address = 0x30;
332 bool i2c_read = input_msg->request &
333 (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
334 u8 *data = NULL;
335
336 if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
337 (input_msg->address != segment_address)))
338 return;
339
340
341 data = input_msg->buffer;
342 if (input_msg->address == segment_address)
343 aux->segment = *data;
344 else
345 aux->offset = *data;
346}
347
348/**
349 * dp_aux_transfer_helper() - helper function for EDID read transactions
350 *
351 * @aux: DP AUX private structure
352 * @input_msg: input message from DRM upstream APIs
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800353 * @send_seg: send the seg to sink
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700354 *
355 * return: void
356 *
357 * This helper function is used to fix EDID reads for non-compliant
358 * sinks that do not handle the i2c middle-of-transaction flag correctly.
359 */
360static void dp_aux_transfer_helper(struct dp_aux_private *aux,
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800361 struct drm_dp_aux_msg *input_msg, bool send_seg)
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700362{
363 struct drm_dp_aux_msg helper_msg;
364 u32 const message_size = 0x10;
365 u32 const segment_address = 0x30;
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530366 u32 const edid_block_length = 0x80;
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700367 bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
368 bool i2c_read = input_msg->request &
369 (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
370
371 if (!i2c_mot || !i2c_read || (input_msg->size == 0))
372 return;
373
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530374 /*
375 * Sending the segment value and EDID offset will be performed
376 * from the DRM upstream EDID driver for each block. Avoid
377 * duplicate AUX transactions related to this while reading the
378 * first 16 bytes of each block.
379 */
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800380 if (!(aux->offset % edid_block_length) || !send_seg)
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530381 goto end;
382
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700383 aux->read = false;
384 aux->cmd_busy = true;
385 aux->no_send_addr = true;
386 aux->no_send_stop = true;
387
388 /*
Padmanabhan Komanduru729c0022017-11-09 17:17:08 +0530389 * Send the segment address for i2c reads for segment > 0 and for which
390 * the middle-of-transaction flag is set. This is required to support
391 * EDID reads of more than 2 blocks as the segment address is reset to 0
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700392 * since we are overriding the middle-of-transaction flag for read
393 * transactions.
394 */
Padmanabhan Komanduru729c0022017-11-09 17:17:08 +0530395 if (aux->segment) {
396 memset(&helper_msg, 0, sizeof(helper_msg));
397 helper_msg.address = segment_address;
398 helper_msg.buffer = &aux->segment;
399 helper_msg.size = 1;
400 dp_aux_cmd_fifo_tx(aux, &helper_msg);
401 }
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700402
403 /*
404 * Send the offset address for every i2c read in which the
405 * middle-of-transaction flag is set. This will ensure that the sink
406 * will update its read pointer and return the correct portion of the
407 * EDID buffer in the subsequent i2c read trasntion triggered in the
408 * native AUX transfer function.
409 */
410 memset(&helper_msg, 0, sizeof(helper_msg));
411 helper_msg.address = input_msg->address;
412 helper_msg.buffer = &aux->offset;
413 helper_msg.size = 1;
414 dp_aux_cmd_fifo_tx(aux, &helper_msg);
Padmanabhan Komanduru95fc2d892017-12-05 18:28:23 +0530415end:
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700416 aux->offset += message_size;
417
418 if (aux->offset == 0x80 || aux->offset == 0x100)
419 aux->segment = 0x0; /* reset segment at end of block */
420}
421
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800422static int dp_aux_transfer_ready(struct dp_aux_private *aux,
423 struct drm_dp_aux_msg *msg, bool send_seg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700424{
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800425 int ret = 0;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700426 int const aux_cmd_native_max = 16;
427 int const aux_cmd_i2c_max = 128;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700428
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530429 if (atomic_read(&aux->aborted)) {
430 ret = -ETIMEDOUT;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800431 goto error;
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530432 }
433
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700434 aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700435
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700436 /* Ignore address only message */
437 if ((msg->size == 0) || (msg->buffer == NULL)) {
438 msg->reply = aux->native ?
439 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800440 goto error;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700441 }
442
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700443 /* msg sanity check */
444 if ((aux->native && (msg->size > aux_cmd_native_max)) ||
445 (msg->size > aux_cmd_i2c_max)) {
446 pr_err("%s: invalid msg: size(%zu), request(%x)\n",
447 __func__, msg->size, msg->request);
448 ret = -EINVAL;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800449 goto error;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700450 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700451
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700452 dp_aux_update_offset_and_segment(aux, msg);
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800453
454 dp_aux_transfer_helper(aux, msg, send_seg);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700455
456 aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
Tatenda Chipeperekwa1a43e702017-09-15 15:45:16 -0700457
Tatenda Chipeperekwa35eafc12017-02-17 22:30:34 -0800458 if (aux->read) {
459 aux->no_send_addr = true;
460 aux->no_send_stop = false;
461 } else {
462 aux->no_send_addr = true;
463 aux->no_send_stop = true;
464 }
465
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800466 aux->cmd_busy = true;
467error:
468 return ret;
469}
470
471static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
472 struct drm_dp_aux_msg *msg)
473{
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800474 u32 timeout;
475 ssize_t ret;
476 struct dp_aux_private *aux = container_of(drm_aux,
477 struct dp_aux_private, drm_aux);
478
479 ret = dp_aux_transfer_ready(aux, msg, false);
480 if (ret)
481 goto end;
482
483 aux->aux_error_num = DP_AUX_ERR_NONE;
484
485 if (aux->native) {
486 if (aux->read && ((msg->address + msg->size) < SZ_1K)) {
487 aux->dp_aux.reg = msg->address;
488
489 reinit_completion(&aux->comp);
490 timeout = wait_for_completion_timeout(&aux->comp, HZ);
491 if (!timeout)
492 pr_err("aux timeout for 0x%x\n", msg->address);
493
494 aux->dp_aux.reg = 0xFFFF;
495
496 memcpy(msg->buffer, aux->dpcd + msg->address,
497 msg->size);
498 aux->aux_error_num = DP_AUX_ERR_NONE;
499 } else {
500 memset(msg->buffer, 0, msg->size);
501 }
502 } else {
503 if (aux->read && msg->address == 0x50) {
504 memcpy(msg->buffer,
505 aux->edid + aux->offset - 16,
506 msg->size);
507 }
508 }
509
510 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
Samantha Tranb6a5cd82018-02-01 14:47:16 -0800511 dp_aux_hex_dump(drm_aux, msg);
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800512
513 msg->reply = aux->native ?
514 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
515 } else {
516 /* Reply defer to retry */
517 msg->reply = aux->native ?
518 DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
519 }
520
521 ret = msg->size;
522end:
523 return ret;
524}
525
526/*
527 * This function does the real job to process an AUX transaction.
528 * It will call aux_reset() function to reset the AUX channel,
529 * if the waiting is timeout.
530 */
531static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
532 struct drm_dp_aux_msg *msg)
533{
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800534 ssize_t ret;
535 int const retry_count = 5;
536 struct dp_aux_private *aux = container_of(drm_aux,
537 struct dp_aux_private, drm_aux);
538
539 mutex_lock(&aux->mutex);
540
541 ret = dp_aux_transfer_ready(aux, msg, true);
542 if (ret)
543 goto unlock_exit;
544
545 if (!aux->cmd_busy) {
546 ret = msg->size;
547 goto unlock_exit;
548 }
549
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700550 ret = dp_aux_cmd_fifo_tx(aux, msg);
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530551 if ((ret < 0) && aux->native && !atomic_read(&aux->aborted)) {
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530552 aux->retry_cnt++;
553 if (!(aux->retry_cnt % retry_count))
554 aux->catalog->update_aux_cfg(aux->catalog,
555 aux->cfg, PHY_AUX_CFG1);
556 aux->catalog->reset(aux->catalog);
557 goto unlock_exit;
558 } else if (ret < 0) {
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700559 goto unlock_exit;
560 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700561
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700562 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
563 if (aux->read)
564 dp_aux_cmd_fifo_rx(aux, msg);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700565
Samantha Tranb6a5cd82018-02-01 14:47:16 -0800566 dp_aux_hex_dump(drm_aux, msg);
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800567
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700568 msg->reply = aux->native ?
569 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
570 } else {
571 /* Reply defer to retry */
572 msg->reply = aux->native ?
573 DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
574 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700575
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700576 /* Return requested size for success or retry */
577 ret = msg->size;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530578 aux->retry_cnt = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700579
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700580unlock_exit:
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700581 aux->cmd_busy = false;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700582 mutex_unlock(&aux->mutex);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700583 return ret;
584}
585
Padmanabhan Komanduru20a21db2017-07-10 16:58:59 +0530586static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
587{
588 int i = 0;
589
590 for (i = 0; i < PHY_AUX_CFG_MAX; i++)
591 aux_cfg[i].current_index = 0;
592}
593
594static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700595{
596 struct dp_aux_private *aux;
597
Padmanabhan Komanduru20a21db2017-07-10 16:58:59 +0530598 if (!dp_aux || !aux_cfg) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700599 pr_err("invalid input\n");
600 return;
601 }
602
603 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
604
Padmanabhan Komanduruf69e3572017-09-27 20:39:32 +0530605 dp_aux_reset_phy_config_indices(aux_cfg);
606 aux->catalog->setup(aux->catalog, aux_cfg);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700607 aux->catalog->reset(aux->catalog);
608 aux->catalog->enable(aux->catalog, true);
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530609 atomic_set(&aux->aborted, 0);
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530610 aux->retry_cnt = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700611}
612
613static void dp_aux_deinit(struct dp_aux *dp_aux)
614{
615 struct dp_aux_private *aux;
616
617 if (!dp_aux) {
618 pr_err("invalid input\n");
619 return;
620 }
621
622 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
623
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530624 atomic_set(&aux->aborted, 1);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700625 aux->catalog->enable(aux->catalog, false);
626}
627
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700628static int dp_aux_register(struct dp_aux *dp_aux)
629{
630 struct dp_aux_private *aux;
631 int ret = 0;
632
633 if (!dp_aux) {
634 pr_err("invalid input\n");
635 ret = -EINVAL;
636 goto exit;
637 }
638
639 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
640
641 aux->drm_aux.name = "sde_dp_aux";
642 aux->drm_aux.dev = aux->dev;
643 aux->drm_aux.transfer = dp_aux_transfer;
644 ret = drm_dp_aux_register(&aux->drm_aux);
645 if (ret) {
646 pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
647 goto exit;
648 }
649 dp_aux->drm_aux = &aux->drm_aux;
650exit:
651 return ret;
652}
653
654static void dp_aux_deregister(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 drm_dp_aux_unregister(&aux->drm_aux);
665}
666
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800667static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
668{
669 struct dp_aux_private *aux;
670
671 if (!dp_aux) {
672 pr_err("invalid input\n");
673 return;
674 }
675
676 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
677
678 complete(&aux->comp);
679}
680
681static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
682 u8 *edid, u8 *dpcd)
683{
684 struct dp_aux_private *aux;
685
686 if (!dp_aux) {
687 pr_err("invalid input\n");
688 return;
689 }
690
691 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
692
693 aux->edid = edid;
694 aux->dpcd = dpcd;
695
696 if (en)
697 aux->drm_aux.transfer = dp_aux_transfer_debug;
698 else
699 aux->drm_aux.transfer = dp_aux_transfer;
700}
701
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530702struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
703 struct dp_aux_cfg *aux_cfg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700704{
705 int rc = 0;
706 struct dp_aux_private *aux;
707 struct dp_aux *dp_aux;
708
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530709 if (!catalog || !aux_cfg) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700710 pr_err("invalid input\n");
711 rc = -ENODEV;
712 goto error;
713 }
714
715 aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
716 if (!aux) {
717 rc = -ENOMEM;
718 goto error;
719 }
720
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700721 init_completion(&aux->comp);
722 aux->cmd_busy = false;
723 mutex_init(&aux->mutex);
724
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700725 aux->dev = dev;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700726 aux->catalog = catalog;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530727 aux->cfg = aux_cfg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700728 dp_aux = &aux->dp_aux;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530729 aux->retry_cnt = 0;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800730 aux->dp_aux.reg = 0xFFFF;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700731
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700732 dp_aux->isr = dp_aux_isr;
733 dp_aux->init = dp_aux_init;
734 dp_aux->deinit = dp_aux_deinit;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700735 dp_aux->drm_aux_register = dp_aux_register;
736 dp_aux->drm_aux_deregister = dp_aux_deregister;
Padmanabhan Komanduru2e9914b2017-08-04 20:51:31 +0530737 dp_aux->reconfig = dp_aux_reconfig;
Padmanabhan Komanduruece783d2017-10-11 19:50:01 +0530738 dp_aux->abort = dp_aux_abort_transaction;
Ajay Singh Parmar09e6af62018-01-19 17:56:21 -0800739 dp_aux->dpcd_updated = dp_aux_dpcd_updated;
740 dp_aux->set_sim_mode = dp_aux_set_sim_mode;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700741
742 return dp_aux;
743error:
744 return ERR_PTR(rc);
745}
746
747void dp_aux_put(struct dp_aux *dp_aux)
748{
749 struct dp_aux_private *aux;
750
751 if (!dp_aux)
752 return;
753
754 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
755
Tatenda Chipeperekwa47ddbcd2017-08-30 13:43:21 -0700756 mutex_destroy(&aux->mutex);
757
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700758 devm_kfree(aux->dev, aux);
759}