blob: 67c6b2dc23fedd5e694123c5d23e9c939d98e7fd [file] [log] [blame]
Ajay Singh Parmarae725622017-03-22 00:03:39 -07001/*
2 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
3 *
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;
31
32 struct mutex mutex;
33 struct completion comp;
34
Ajay Singh Parmarae725622017-03-22 00:03:39 -070035 u32 aux_error_num;
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -070036 bool cmd_busy;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070037 bool native;
38 bool read;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070039
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070040 struct drm_dp_aux drm_aux;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070041};
42
43static char *dp_aux_get_error(u32 aux_error)
44{
45 switch (aux_error) {
46 case DP_AUX_ERR_NONE:
47 return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
48 case DP_AUX_ERR_ADDR:
49 return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
50 case DP_AUX_ERR_TOUT:
51 return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
52 case DP_AUX_ERR_NACK:
53 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
54 case DP_AUX_ERR_DEFER:
55 return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
56 case DP_AUX_ERR_NACK_DEFER:
57 return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
58 default:
59 return "unknown";
60 }
61}
62
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070063static u32 dp_aux_write(struct dp_aux_private *aux,
64 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -070065{
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070066 u32 data[4], reg, len;
67 u8 *msgdata = msg->buffer;
68 int const aux_cmd_fifo_len = 128;
69 int i = 0;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070070
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070071 if (aux->read)
Ajay Singh Parmarae725622017-03-22 00:03:39 -070072 len = 4;
73 else
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070074 len = msg->size + 4;
Ajay Singh Parmarae725622017-03-22 00:03:39 -070075
76 /*
77 * cmd fifo only has depth of 144 bytes
78 * limit buf length to 128 bytes here
79 */
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070080 if (len > aux_cmd_fifo_len) {
Ajay Singh Parmarae725622017-03-22 00:03:39 -070081 pr_err("buf len error\n");
82 return 0;
83 }
84
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070085 /* Pack cmd and write to HW */
86 data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
87 if (aux->read)
88 data[0] |= BIT(4); /* R/W */
Ajay Singh Parmarae725622017-03-22 00:03:39 -070089
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070090 data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
91 data[2] = msg->address & 0xff; /* addr[7:0] */
92 data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
Ajay Singh Parmarae725622017-03-22 00:03:39 -070093
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -070094 for (i = 0; i < len; i++) {
95 reg = (i < 4) ? data[i] : msgdata[i - 4];
96 reg = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
97 if (i == 0)
98 reg |= DP_AUX_DATA_INDEX_WRITE;
99 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700100 aux->catalog->write_data(aux->catalog);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700101 }
102
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700103 reg = 0; /* Transaction number == 1 */
104 if (!aux->native) /* i2c */
105 reg |= (BIT(8) | BIT(10) | BIT(11));
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700106
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700107 reg |= BIT(9);
108 aux->catalog->data = reg;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700109 aux->catalog->write_trans(aux->catalog);
110
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700111 return len;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700112}
113
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700114static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
115 struct drm_dp_aux_msg *msg)
116{
117 u32 ret = 0, len = 0, timeout;
118 int const aux_timeout_ms = HZ/4;
119
120 reinit_completion(&aux->comp);
121
122 len = dp_aux_write(aux, msg);
123 if (len == 0) {
124 pr_err("DP AUX write failed\n");
125 return -EINVAL;
126 }
127
128 timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
129 if (!timeout) {
130 pr_err("aux write timeout\n");
131 return -ETIMEDOUT;
132 }
133
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700134 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700135 ret = len;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700136 } else {
137 pr_err_ratelimited("aux err: %s\n",
138 dp_aux_get_error(aux->aux_error_num));
139
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700140 ret = -EINVAL;
Ajay Singh Parmarf63dbc7ab2017-07-02 22:45:28 -0700141 }
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700142
143 return ret;
144}
145
146static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
147 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700148{
149 u32 data;
150 u8 *dp;
151 u32 i;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700152 u32 len = msg->size;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700153
154 data = 0;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700155 data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700156 data |= BIT(0); /* read */
157
158 aux->catalog->data = data;
159 aux->catalog->write_data(aux->catalog);
160
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700161 dp = msg->buffer;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700162
163 /* discard first byte */
164 data = aux->catalog->read_data(aux->catalog);
165
166 for (i = 0; i < len; i++) {
167 data = aux->catalog->read_data(aux->catalog);
168 *dp++ = (u8)((data >> 8) & 0xff);
169 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700170}
171
172static void dp_aux_native_handler(struct dp_aux_private *aux)
173{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700174 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700175
176 if (isr & DP_INTR_AUX_I2C_DONE)
177 aux->aux_error_num = DP_AUX_ERR_NONE;
178 else if (isr & DP_INTR_WRONG_ADDR)
179 aux->aux_error_num = DP_AUX_ERR_ADDR;
180 else if (isr & DP_INTR_TIMEOUT)
181 aux->aux_error_num = DP_AUX_ERR_TOUT;
182 if (isr & DP_INTR_NACK_DEFER)
183 aux->aux_error_num = DP_AUX_ERR_NACK;
184
185 complete(&aux->comp);
186}
187
188static void dp_aux_i2c_handler(struct dp_aux_private *aux)
189{
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700190 u32 isr = aux->catalog->isr;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700191
192 if (isr & DP_INTR_AUX_I2C_DONE) {
193 if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
194 aux->aux_error_num = DP_AUX_ERR_NACK;
195 else
196 aux->aux_error_num = DP_AUX_ERR_NONE;
197 } else {
198 if (isr & DP_INTR_WRONG_ADDR)
199 aux->aux_error_num = DP_AUX_ERR_ADDR;
200 else if (isr & DP_INTR_TIMEOUT)
201 aux->aux_error_num = DP_AUX_ERR_TOUT;
202 if (isr & DP_INTR_NACK_DEFER)
203 aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
204 if (isr & DP_INTR_I2C_NACK)
205 aux->aux_error_num = DP_AUX_ERR_NACK;
206 if (isr & DP_INTR_I2C_DEFER)
207 aux->aux_error_num = DP_AUX_ERR_DEFER;
208 }
209
210 complete(&aux->comp);
211}
212
213static void dp_aux_isr(struct dp_aux *dp_aux)
214{
215 struct dp_aux_private *aux;
216
217 if (!dp_aux) {
218 pr_err("invalid input\n");
219 return;
220 }
221
222 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
223
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700224 aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
225
226 if (!aux->cmd_busy)
227 return;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700228
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700229 if (aux->native)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700230 dp_aux_native_handler(aux);
231 else
232 dp_aux_i2c_handler(aux);
233}
234
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700235/*
236 * This function does the real job to process an AUX transaction.
237 * It will call aux_reset() function to reset the AUX channel,
238 * if the waiting is timeout.
239 */
240static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
241 struct drm_dp_aux_msg *msg)
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700242{
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700243 ssize_t ret;
244 int const aux_cmd_native_max = 16;
245 int const aux_cmd_i2c_max = 128;
246 struct dp_aux_private *aux = container_of(drm_aux,
247 struct dp_aux_private, drm_aux);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700248
249 mutex_lock(&aux->mutex);
250
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700251 aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
252 aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700253 aux->cmd_busy = true;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700254
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700255 /* Ignore address only message */
256 if ((msg->size == 0) || (msg->buffer == NULL)) {
257 msg->reply = aux->native ?
258 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
259 ret = msg->size;
260 goto unlock_exit;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700261 }
262
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700263 /* msg sanity check */
264 if ((aux->native && (msg->size > aux_cmd_native_max)) ||
265 (msg->size > aux_cmd_i2c_max)) {
266 pr_err("%s: invalid msg: size(%zu), request(%x)\n",
267 __func__, msg->size, msg->request);
268 ret = -EINVAL;
269 goto unlock_exit;
270 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700271
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700272 ret = dp_aux_cmd_fifo_tx(aux, msg);
273 if (ret < 0) {
274 aux->catalog->reset(aux->catalog); /* reset aux */
275 goto unlock_exit;
276 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700277
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700278 if (aux->aux_error_num == DP_AUX_ERR_NONE) {
279 if (aux->read)
280 dp_aux_cmd_fifo_rx(aux, msg);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700281
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700282 msg->reply = aux->native ?
283 DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
284 } else {
285 /* Reply defer to retry */
286 msg->reply = aux->native ?
287 DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
288 }
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700289
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700290 /* Return requested size for success or retry */
291 ret = msg->size;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700292
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700293unlock_exit:
Padmanabhan Komanduru15e757d2017-05-24 04:07:31 -0700294 aux->cmd_busy = false;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700295 mutex_unlock(&aux->mutex);
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700296 return ret;
297}
298
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700299static void dp_aux_init(struct dp_aux *dp_aux, u32 *aux_cfg)
300{
301 struct dp_aux_private *aux;
302
303 if (!dp_aux) {
304 pr_err("invalid input\n");
305 return;
306 }
307
308 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
309
310 aux->catalog->reset(aux->catalog);
311 aux->catalog->enable(aux->catalog, true);
312 aux->catalog->setup(aux->catalog, aux_cfg);
313}
314
315static void dp_aux_deinit(struct dp_aux *dp_aux)
316{
317 struct dp_aux_private *aux;
318
319 if (!dp_aux) {
320 pr_err("invalid input\n");
321 return;
322 }
323
324 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
325
326 aux->catalog->enable(aux->catalog, false);
327}
328
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700329static int dp_aux_register(struct dp_aux *dp_aux)
330{
331 struct dp_aux_private *aux;
332 int ret = 0;
333
334 if (!dp_aux) {
335 pr_err("invalid input\n");
336 ret = -EINVAL;
337 goto exit;
338 }
339
340 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
341
342 aux->drm_aux.name = "sde_dp_aux";
343 aux->drm_aux.dev = aux->dev;
344 aux->drm_aux.transfer = dp_aux_transfer;
345 ret = drm_dp_aux_register(&aux->drm_aux);
346 if (ret) {
347 pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
348 goto exit;
349 }
350 dp_aux->drm_aux = &aux->drm_aux;
351exit:
352 return ret;
353}
354
355static void dp_aux_deregister(struct dp_aux *dp_aux)
356{
357 struct dp_aux_private *aux;
358
359 if (!dp_aux) {
360 pr_err("invalid input\n");
361 return;
362 }
363
364 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
365 drm_dp_aux_unregister(&aux->drm_aux);
366}
367
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700368struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog)
369{
370 int rc = 0;
371 struct dp_aux_private *aux;
372 struct dp_aux *dp_aux;
373
374 if (!catalog) {
375 pr_err("invalid input\n");
376 rc = -ENODEV;
377 goto error;
378 }
379
380 aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
381 if (!aux) {
382 rc = -ENOMEM;
383 goto error;
384 }
385
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700386 init_completion(&aux->comp);
387 aux->cmd_busy = false;
388 mutex_init(&aux->mutex);
389
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700390 aux->dev = dev;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700391 aux->catalog = catalog;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700392 dp_aux = &aux->dp_aux;
393
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700394 dp_aux->isr = dp_aux_isr;
395 dp_aux->init = dp_aux_init;
396 dp_aux->deinit = dp_aux_deinit;
Padmanabhan Komandurud84b38b2017-05-24 04:24:57 -0700397 dp_aux->drm_aux_register = dp_aux_register;
398 dp_aux->drm_aux_deregister = dp_aux_deregister;
Ajay Singh Parmarae725622017-03-22 00:03:39 -0700399
400 return dp_aux;
401error:
402 return ERR_PTR(rc);
403}
404
405void dp_aux_put(struct dp_aux *dp_aux)
406{
407 struct dp_aux_private *aux;
408
409 if (!dp_aux)
410 return;
411
412 aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
413
414 devm_kfree(aux->dev, aux);
415}