Line data Source code
1 : /*
2 : * Copyright © 2016 Intel Corporation
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a
5 : * copy of this software and associated documentation files (the "Software"),
6 : * to deal in the Software without restriction, including without limitation
7 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 : * and/or sell copies of the Software, and to permit persons to whom the
9 : * Software is furnished to do so, subject to the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included in
12 : * all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 : * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 : * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 : * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 : * OTHER DEALINGS IN THE SOFTWARE.
21 : */
22 :
23 : #include <linux/delay.h>
24 : #include <linux/errno.h>
25 : #include <linux/export.h>
26 : #include <linux/i2c.h>
27 : #include <linux/slab.h>
28 : #include <linux/string.h>
29 :
30 : #include <drm/display/drm_dp_dual_mode_helper.h>
31 : #include <drm/drm_device.h>
32 : #include <drm/drm_print.h>
33 :
34 : /**
35 : * DOC: dp dual mode helpers
36 : *
37 : * Helper functions to deal with DP dual mode (aka. DP++) adaptors.
38 : *
39 : * Type 1:
40 : * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C.
41 : *
42 : * Type 2:
43 : * Adaptor registers and sink DDC bus can be accessed either via I2C or
44 : * I2C-over-AUX. Source devices may choose to implement either of these
45 : * access methods.
46 : */
47 :
48 : #define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
49 :
50 : /**
51 : * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s)
52 : * @adapter: I2C adapter for the DDC bus
53 : * @offset: register offset
54 : * @buffer: buffer for return data
55 : * @size: sizo of the buffer
56 : *
57 : * Reads @size bytes from the DP dual mode adaptor registers
58 : * starting at @offset.
59 : *
60 : * Returns:
61 : * 0 on success, negative error code on failure
62 : */
63 0 : ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
64 : u8 offset, void *buffer, size_t size)
65 : {
66 0 : u8 zero = 0;
67 0 : char *tmpbuf = NULL;
68 : /*
69 : * As sub-addressing is not supported by all adaptors,
70 : * always explicitly read from the start and discard
71 : * any bytes that come before the requested offset.
72 : * This way, no matter whether the adaptor supports it
73 : * or not, we'll end up reading the proper data.
74 : */
75 0 : struct i2c_msg msgs[] = {
76 : {
77 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
78 : .flags = 0,
79 : .len = 1,
80 : .buf = &zero,
81 : },
82 : {
83 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
84 : .flags = I2C_M_RD,
85 0 : .len = size + offset,
86 : .buf = buffer,
87 : },
88 : };
89 : int ret;
90 :
91 0 : if (offset) {
92 0 : tmpbuf = kmalloc(size + offset, GFP_KERNEL);
93 0 : if (!tmpbuf)
94 : return -ENOMEM;
95 :
96 0 : msgs[1].buf = tmpbuf;
97 : }
98 :
99 0 : ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
100 0 : if (tmpbuf)
101 0 : memcpy(buffer, tmpbuf + offset, size);
102 :
103 0 : kfree(tmpbuf);
104 :
105 0 : if (ret < 0)
106 0 : return ret;
107 0 : if (ret != ARRAY_SIZE(msgs))
108 : return -EPROTO;
109 :
110 0 : return 0;
111 : }
112 : EXPORT_SYMBOL(drm_dp_dual_mode_read);
113 :
114 : /**
115 : * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
116 : * @adapter: I2C adapter for the DDC bus
117 : * @offset: register offset
118 : * @buffer: buffer for write data
119 : * @size: sizo of the buffer
120 : *
121 : * Writes @size bytes to the DP dual mode adaptor registers
122 : * starting at @offset.
123 : *
124 : * Returns:
125 : * 0 on success, negative error code on failure
126 : */
127 0 : ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
128 : u8 offset, const void *buffer, size_t size)
129 : {
130 0 : struct i2c_msg msg = {
131 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
132 : .flags = 0,
133 0 : .len = 1 + size,
134 : .buf = NULL,
135 : };
136 : void *data;
137 : int ret;
138 :
139 0 : data = kmalloc(msg.len, GFP_KERNEL);
140 0 : if (!data)
141 : return -ENOMEM;
142 :
143 0 : msg.buf = data;
144 :
145 0 : memcpy(data, &offset, 1);
146 0 : memcpy(data + 1, buffer, size);
147 :
148 0 : ret = i2c_transfer(adapter, &msg, 1);
149 :
150 0 : kfree(data);
151 :
152 0 : if (ret < 0)
153 0 : return ret;
154 0 : if (ret != 1)
155 : return -EPROTO;
156 :
157 0 : return 0;
158 : }
159 : EXPORT_SYMBOL(drm_dp_dual_mode_write);
160 :
161 : static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
162 : {
163 : static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
164 : "DP-HDMI ADAPTOR\x04";
165 :
166 0 : return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
167 : sizeof(dp_dual_mode_hdmi_id)) == 0;
168 : }
169 :
170 : static bool is_type1_adaptor(uint8_t adaptor_id)
171 : {
172 0 : return adaptor_id == 0 || adaptor_id == 0xff;
173 : }
174 :
175 : static bool is_type2_adaptor(uint8_t adaptor_id)
176 : {
177 : return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
178 : DP_DUAL_MODE_REV_TYPE2);
179 : }
180 :
181 : static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
182 : const uint8_t adaptor_id)
183 : {
184 0 : return is_hdmi_adaptor(hdmi_id) &&
185 : (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
186 : DP_DUAL_MODE_TYPE_HAS_DPCD));
187 : }
188 :
189 : /**
190 : * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
191 : * @dev: &drm_device to use
192 : * @adapter: I2C adapter for the DDC bus
193 : *
194 : * Attempt to identify the type of the DP dual mode adaptor used.
195 : *
196 : * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
197 : * certain whether we're dealing with a native HDMI port or
198 : * a type 1 DVI dual mode adaptor. The driver will have to use
199 : * some other hardware/driver specific mechanism to make that
200 : * distinction.
201 : *
202 : * Returns:
203 : * The type of the DP dual mode adaptor used
204 : */
205 0 : enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
206 : struct i2c_adapter *adapter)
207 : {
208 0 : char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
209 0 : uint8_t adaptor_id = 0x00;
210 : ssize_t ret;
211 :
212 : /*
213 : * Let's see if the adaptor is there the by reading the
214 : * HDMI ID registers.
215 : *
216 : * Note that type 1 DVI adaptors are not required to implemnt
217 : * any registers, and that presents a problem for detection.
218 : * If the i2c transfer is nacked, we may or may not be dealing
219 : * with a type 1 DVI adaptor. Some other mechanism of detecting
220 : * the presence of the adaptor is required. One way would be
221 : * to check the state of the CONFIG1 pin, Another method would
222 : * simply require the driver to know whether the port is a DP++
223 : * port or a native HDMI port. Both of these methods are entirely
224 : * hardware/driver specific so we can't deal with them here.
225 : */
226 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
227 : hdmi_id, sizeof(hdmi_id));
228 0 : drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
229 : ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
230 0 : if (ret)
231 : return DRM_DP_DUAL_MODE_UNKNOWN;
232 :
233 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
234 : &adaptor_id, sizeof(adaptor_id));
235 0 : drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
236 0 : if (ret == 0) {
237 0 : if (is_lspcon_adaptor(hdmi_id, adaptor_id))
238 : return DRM_DP_DUAL_MODE_LSPCON;
239 0 : if (is_type2_adaptor(adaptor_id)) {
240 0 : if (is_hdmi_adaptor(hdmi_id))
241 : return DRM_DP_DUAL_MODE_TYPE2_HDMI;
242 : else
243 0 : return DRM_DP_DUAL_MODE_TYPE2_DVI;
244 : }
245 : /*
246 : * If not a proper type 1 ID, still assume type 1, but let
247 : * the user know that we may have misdetected the type.
248 : */
249 0 : if (!is_type1_adaptor(adaptor_id))
250 0 : drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
251 :
252 : }
253 :
254 0 : if (is_hdmi_adaptor(hdmi_id))
255 : return DRM_DP_DUAL_MODE_TYPE1_HDMI;
256 : else
257 0 : return DRM_DP_DUAL_MODE_TYPE1_DVI;
258 : }
259 : EXPORT_SYMBOL(drm_dp_dual_mode_detect);
260 :
261 : /**
262 : * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
263 : * @dev: &drm_device to use
264 : * @type: DP dual mode adaptor type
265 : * @adapter: I2C adapter for the DDC bus
266 : *
267 : * Determine the max TMDS clock the adaptor supports based on the
268 : * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
269 : * register (on type2 adaptors). As some type 1 adaptors have
270 : * problems with registers (see comments in drm_dp_dual_mode_detect())
271 : * we don't read the register on those, instead we simply assume
272 : * a 165 MHz limit based on the specification.
273 : *
274 : * Returns:
275 : * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
276 : */
277 0 : int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
278 : struct i2c_adapter *adapter)
279 : {
280 : uint8_t max_tmds_clock;
281 : ssize_t ret;
282 :
283 : /* native HDMI so no limit */
284 0 : if (type == DRM_DP_DUAL_MODE_NONE)
285 : return 0;
286 :
287 : /*
288 : * Type 1 adaptors are limited to 165MHz
289 : * Type 2 adaptors can tells us their limit
290 : */
291 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
292 : return 165000;
293 :
294 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
295 : &max_tmds_clock, sizeof(max_tmds_clock));
296 0 : if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
297 0 : drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
298 0 : return 165000;
299 : }
300 :
301 0 : return max_tmds_clock * 5000 / 2;
302 : }
303 : EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
304 :
305 : /**
306 : * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
307 : * @dev: &drm_device to use
308 : * @type: DP dual mode adaptor type
309 : * @adapter: I2C adapter for the DDC bus
310 : * @enabled: current state of the TMDS output buffers
311 : *
312 : * Get the state of the TMDS output buffers in the adaptor. For
313 : * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
314 : * register. As some type 1 adaptors have problems with registers
315 : * (see comments in drm_dp_dual_mode_detect()) we don't read the
316 : * register on those, instead we simply assume that the buffers
317 : * are always enabled.
318 : *
319 : * Returns:
320 : * 0 on success, negative error code on failure
321 : */
322 0 : int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
323 : enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
324 : bool *enabled)
325 : {
326 : uint8_t tmds_oen;
327 : ssize_t ret;
328 :
329 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
330 0 : *enabled = true;
331 0 : return 0;
332 : }
333 :
334 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
335 : &tmds_oen, sizeof(tmds_oen));
336 0 : if (ret) {
337 0 : drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
338 0 : return ret;
339 : }
340 :
341 0 : *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
342 :
343 0 : return 0;
344 : }
345 : EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
346 :
347 : /**
348 : * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
349 : * @dev: &drm_device to use
350 : * @type: DP dual mode adaptor type
351 : * @adapter: I2C adapter for the DDC bus
352 : * @enable: enable (as opposed to disable) the TMDS output buffers
353 : *
354 : * Set the state of the TMDS output buffers in the adaptor. For
355 : * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register.
356 : * Type1 adaptors do not support any register writes.
357 : *
358 : * Returns:
359 : * 0 on success, negative error code on failure
360 : */
361 0 : int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
362 : struct i2c_adapter *adapter, bool enable)
363 : {
364 0 : uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
365 : ssize_t ret;
366 : int retry;
367 :
368 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
369 : return 0;
370 :
371 : /*
372 : * LSPCON adapters in low-power state may ignore the first write, so
373 : * read back and verify the written value a few times.
374 : */
375 0 : for (retry = 0; retry < 3; retry++) {
376 : uint8_t tmp;
377 :
378 0 : ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
379 : &tmds_oen, sizeof(tmds_oen));
380 0 : if (ret) {
381 0 : drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
382 : enable ? "enable" : "disable", retry + 1);
383 0 : return ret;
384 : }
385 :
386 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
387 : &tmp, sizeof(tmp));
388 0 : if (ret) {
389 0 : drm_dbg_kms(dev,
390 : "I2C read failed during TMDS output buffer %s (%d attempts)\n",
391 : enable ? "enabling" : "disabling", retry + 1);
392 0 : return ret;
393 : }
394 :
395 0 : if (tmp == tmds_oen)
396 : return 0;
397 : }
398 :
399 0 : drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
400 : enable ? "enabling" : "disabling");
401 :
402 0 : return -EIO;
403 : }
404 : EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
405 :
406 : /**
407 : * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
408 : * @type: DP dual mode adaptor type
409 : *
410 : * Returns:
411 : * String representation of the DP dual mode adaptor type
412 : */
413 0 : const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
414 : {
415 0 : switch (type) {
416 : case DRM_DP_DUAL_MODE_NONE:
417 : return "none";
418 : case DRM_DP_DUAL_MODE_TYPE1_DVI:
419 0 : return "type 1 DVI";
420 : case DRM_DP_DUAL_MODE_TYPE1_HDMI:
421 0 : return "type 1 HDMI";
422 : case DRM_DP_DUAL_MODE_TYPE2_DVI:
423 0 : return "type 2 DVI";
424 : case DRM_DP_DUAL_MODE_TYPE2_HDMI:
425 0 : return "type 2 HDMI";
426 : case DRM_DP_DUAL_MODE_LSPCON:
427 0 : return "lspcon";
428 : default:
429 0 : WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
430 : return "unknown";
431 : }
432 : }
433 : EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
434 :
435 : /**
436 : * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
437 : * reading offset (0x80, 0x41)
438 : * @dev: &drm_device to use
439 : * @adapter: I2C-over-aux adapter
440 : * @mode: current lspcon mode of operation output variable
441 : *
442 : * Returns:
443 : * 0 on success, sets the current_mode value to appropriate mode
444 : * -error on failure
445 : */
446 0 : int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
447 : enum drm_lspcon_mode *mode)
448 : {
449 : u8 data;
450 0 : int ret = 0;
451 : int retry;
452 :
453 0 : if (!mode) {
454 0 : drm_err(dev, "NULL input\n");
455 0 : return -EINVAL;
456 : }
457 :
458 : /* Read Status: i2c over aux */
459 0 : for (retry = 0; retry < 6; retry++) {
460 0 : if (retry)
461 : usleep_range(500, 1000);
462 :
463 0 : ret = drm_dp_dual_mode_read(adapter,
464 : DP_DUAL_MODE_LSPCON_CURRENT_MODE,
465 : &data, sizeof(data));
466 0 : if (!ret)
467 : break;
468 : }
469 :
470 0 : if (ret < 0) {
471 0 : drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
472 0 : return -EFAULT;
473 : }
474 :
475 0 : if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
476 0 : *mode = DRM_LSPCON_MODE_PCON;
477 : else
478 0 : *mode = DRM_LSPCON_MODE_LS;
479 : return 0;
480 : }
481 : EXPORT_SYMBOL(drm_lspcon_get_mode);
482 :
483 : /**
484 : * drm_lspcon_set_mode: Change LSPCON's mode of operation by
485 : * writing offset (0x80, 0x40)
486 : * @dev: &drm_device to use
487 : * @adapter: I2C-over-aux adapter
488 : * @mode: required mode of operation
489 : *
490 : * Returns:
491 : * 0 on success, -error on failure/timeout
492 : */
493 0 : int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
494 : enum drm_lspcon_mode mode)
495 : {
496 0 : u8 data = 0;
497 : int ret;
498 0 : int time_out = 200;
499 : enum drm_lspcon_mode current_mode;
500 :
501 0 : if (mode == DRM_LSPCON_MODE_PCON)
502 0 : data = DP_DUAL_MODE_LSPCON_MODE_PCON;
503 :
504 : /* Change mode */
505 0 : ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
506 : &data, sizeof(data));
507 0 : if (ret < 0) {
508 0 : drm_err(dev, "LSPCON mode change failed\n");
509 0 : return ret;
510 : }
511 :
512 : /*
513 : * Confirm mode change by reading the status bit.
514 : * Sometimes, it takes a while to change the mode,
515 : * so wait and retry until time out or done.
516 : */
517 : do {
518 0 : ret = drm_lspcon_get_mode(dev, adapter, ¤t_mode);
519 0 : if (ret) {
520 0 : drm_err(dev, "can't confirm LSPCON mode change\n");
521 0 : return ret;
522 : } else {
523 0 : if (current_mode != mode) {
524 0 : msleep(10);
525 0 : time_out -= 10;
526 : } else {
527 0 : drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
528 : mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
529 0 : return 0;
530 : }
531 : }
532 0 : } while (time_out);
533 :
534 0 : drm_err(dev, "LSPCON mode change timed out\n");
535 0 : return -ETIMEDOUT;
536 : }
537 : EXPORT_SYMBOL(drm_lspcon_set_mode);
|