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 : struct i2c_msg msgs[] = {
67 : {
68 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
69 : .flags = 0,
70 : .len = 1,
71 : .buf = &offset,
72 : },
73 : {
74 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
75 : .flags = I2C_M_RD,
76 : .len = size,
77 : .buf = buffer,
78 : },
79 : };
80 : int ret;
81 :
82 0 : ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
83 0 : if (ret < 0)
84 0 : return ret;
85 0 : if (ret != ARRAY_SIZE(msgs))
86 : return -EPROTO;
87 :
88 0 : return 0;
89 : }
90 : EXPORT_SYMBOL(drm_dp_dual_mode_read);
91 :
92 : /**
93 : * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
94 : * @adapter: I2C adapter for the DDC bus
95 : * @offset: register offset
96 : * @buffer: buffer for write data
97 : * @size: sizo of the buffer
98 : *
99 : * Writes @size bytes to the DP dual mode adaptor registers
100 : * starting at @offset.
101 : *
102 : * Returns:
103 : * 0 on success, negative error code on failure
104 : */
105 0 : ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
106 : u8 offset, const void *buffer, size_t size)
107 : {
108 0 : struct i2c_msg msg = {
109 : .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
110 : .flags = 0,
111 0 : .len = 1 + size,
112 : .buf = NULL,
113 : };
114 : void *data;
115 : int ret;
116 :
117 0 : data = kmalloc(msg.len, GFP_KERNEL);
118 0 : if (!data)
119 : return -ENOMEM;
120 :
121 0 : msg.buf = data;
122 :
123 0 : memcpy(data, &offset, 1);
124 0 : memcpy(data + 1, buffer, size);
125 :
126 0 : ret = i2c_transfer(adapter, &msg, 1);
127 :
128 0 : kfree(data);
129 :
130 0 : if (ret < 0)
131 0 : return ret;
132 0 : if (ret != 1)
133 : return -EPROTO;
134 :
135 0 : return 0;
136 : }
137 : EXPORT_SYMBOL(drm_dp_dual_mode_write);
138 :
139 : static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
140 : {
141 : static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
142 : "DP-HDMI ADAPTOR\x04";
143 :
144 0 : return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
145 : sizeof(dp_dual_mode_hdmi_id)) == 0;
146 : }
147 :
148 : static bool is_type1_adaptor(uint8_t adaptor_id)
149 : {
150 0 : return adaptor_id == 0 || adaptor_id == 0xff;
151 : }
152 :
153 : static bool is_type2_adaptor(uint8_t adaptor_id)
154 : {
155 : return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
156 : DP_DUAL_MODE_REV_TYPE2);
157 : }
158 :
159 : static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
160 : const uint8_t adaptor_id)
161 : {
162 0 : return is_hdmi_adaptor(hdmi_id) &&
163 : (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
164 : DP_DUAL_MODE_TYPE_HAS_DPCD));
165 : }
166 :
167 : /**
168 : * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
169 : * @dev: &drm_device to use
170 : * @adapter: I2C adapter for the DDC bus
171 : *
172 : * Attempt to identify the type of the DP dual mode adaptor used.
173 : *
174 : * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
175 : * certain whether we're dealing with a native HDMI port or
176 : * a type 1 DVI dual mode adaptor. The driver will have to use
177 : * some other hardware/driver specific mechanism to make that
178 : * distinction.
179 : *
180 : * Returns:
181 : * The type of the DP dual mode adaptor used
182 : */
183 0 : enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
184 : struct i2c_adapter *adapter)
185 : {
186 0 : char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
187 0 : uint8_t adaptor_id = 0x00;
188 : ssize_t ret;
189 :
190 : /*
191 : * Let's see if the adaptor is there the by reading the
192 : * HDMI ID registers.
193 : *
194 : * Note that type 1 DVI adaptors are not required to implemnt
195 : * any registers, and that presents a problem for detection.
196 : * If the i2c transfer is nacked, we may or may not be dealing
197 : * with a type 1 DVI adaptor. Some other mechanism of detecting
198 : * the presence of the adaptor is required. One way would be
199 : * to check the state of the CONFIG1 pin, Another method would
200 : * simply require the driver to know whether the port is a DP++
201 : * port or a native HDMI port. Both of these methods are entirely
202 : * hardware/driver specific so we can't deal with them here.
203 : */
204 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
205 : hdmi_id, sizeof(hdmi_id));
206 0 : drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
207 : ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
208 0 : if (ret)
209 : return DRM_DP_DUAL_MODE_UNKNOWN;
210 :
211 : /*
212 : * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
213 : * the offset but ignore it, and instead they just always return
214 : * data from the start of the HDMI ID buffer. So for a broken
215 : * type 1 HDMI adaptor a single byte read will always give us
216 : * 0x44, and for a type 1 DVI adaptor it should give 0x00
217 : * (assuming it implements any registers). Fortunately neither
218 : * of those values will match the type 2 signature of the
219 : * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
220 : * the type 2 adaptor detection safely even in the presence
221 : * of broken type 1 adaptors.
222 : */
223 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
224 : &adaptor_id, sizeof(adaptor_id));
225 0 : drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
226 0 : if (ret == 0) {
227 0 : if (is_lspcon_adaptor(hdmi_id, adaptor_id))
228 : return DRM_DP_DUAL_MODE_LSPCON;
229 0 : if (is_type2_adaptor(adaptor_id)) {
230 0 : if (is_hdmi_adaptor(hdmi_id))
231 : return DRM_DP_DUAL_MODE_TYPE2_HDMI;
232 : else
233 0 : return DRM_DP_DUAL_MODE_TYPE2_DVI;
234 : }
235 : /*
236 : * If neither a proper type 1 ID nor a broken type 1 adaptor
237 : * as described above, assume type 1, but let the user know
238 : * that we may have misdetected the type.
239 : */
240 0 : if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
241 0 : drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
242 :
243 : }
244 :
245 0 : if (is_hdmi_adaptor(hdmi_id))
246 : return DRM_DP_DUAL_MODE_TYPE1_HDMI;
247 : else
248 0 : return DRM_DP_DUAL_MODE_TYPE1_DVI;
249 : }
250 : EXPORT_SYMBOL(drm_dp_dual_mode_detect);
251 :
252 : /**
253 : * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
254 : * @dev: &drm_device to use
255 : * @type: DP dual mode adaptor type
256 : * @adapter: I2C adapter for the DDC bus
257 : *
258 : * Determine the max TMDS clock the adaptor supports based on the
259 : * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
260 : * register (on type2 adaptors). As some type 1 adaptors have
261 : * problems with registers (see comments in drm_dp_dual_mode_detect())
262 : * we don't read the register on those, instead we simply assume
263 : * a 165 MHz limit based on the specification.
264 : *
265 : * Returns:
266 : * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
267 : */
268 0 : int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
269 : struct i2c_adapter *adapter)
270 : {
271 : uint8_t max_tmds_clock;
272 : ssize_t ret;
273 :
274 : /* native HDMI so no limit */
275 0 : if (type == DRM_DP_DUAL_MODE_NONE)
276 : return 0;
277 :
278 : /*
279 : * Type 1 adaptors are limited to 165MHz
280 : * Type 2 adaptors can tells us their limit
281 : */
282 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
283 : return 165000;
284 :
285 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
286 : &max_tmds_clock, sizeof(max_tmds_clock));
287 0 : if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
288 0 : drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
289 0 : return 165000;
290 : }
291 :
292 0 : return max_tmds_clock * 5000 / 2;
293 : }
294 : EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
295 :
296 : /**
297 : * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
298 : * @dev: &drm_device to use
299 : * @type: DP dual mode adaptor type
300 : * @adapter: I2C adapter for the DDC bus
301 : * @enabled: current state of the TMDS output buffers
302 : *
303 : * Get the state of the TMDS output buffers in the adaptor. For
304 : * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
305 : * register. As some type 1 adaptors have problems with registers
306 : * (see comments in drm_dp_dual_mode_detect()) we don't read the
307 : * register on those, instead we simply assume that the buffers
308 : * are always enabled.
309 : *
310 : * Returns:
311 : * 0 on success, negative error code on failure
312 : */
313 0 : int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
314 : enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
315 : bool *enabled)
316 : {
317 : uint8_t tmds_oen;
318 : ssize_t ret;
319 :
320 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
321 0 : *enabled = true;
322 0 : return 0;
323 : }
324 :
325 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
326 : &tmds_oen, sizeof(tmds_oen));
327 0 : if (ret) {
328 0 : drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
329 0 : return ret;
330 : }
331 :
332 0 : *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
333 :
334 0 : return 0;
335 : }
336 : EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
337 :
338 : /**
339 : * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
340 : * @dev: &drm_device to use
341 : * @type: DP dual mode adaptor type
342 : * @adapter: I2C adapter for the DDC bus
343 : * @enable: enable (as opposed to disable) the TMDS output buffers
344 : *
345 : * Set the state of the TMDS output buffers in the adaptor. For
346 : * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
347 : * some type 1 adaptors have problems with registers (see comments
348 : * in drm_dp_dual_mode_detect()) we avoid touching the register,
349 : * making this function a no-op on type 1 adaptors.
350 : *
351 : * Returns:
352 : * 0 on success, negative error code on failure
353 : */
354 0 : int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
355 : struct i2c_adapter *adapter, bool enable)
356 : {
357 0 : uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
358 : ssize_t ret;
359 : int retry;
360 :
361 0 : if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
362 : return 0;
363 :
364 : /*
365 : * LSPCON adapters in low-power state may ignore the first write, so
366 : * read back and verify the written value a few times.
367 : */
368 0 : for (retry = 0; retry < 3; retry++) {
369 : uint8_t tmp;
370 :
371 0 : ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
372 : &tmds_oen, sizeof(tmds_oen));
373 0 : if (ret) {
374 0 : drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
375 : enable ? "enable" : "disable", retry + 1);
376 0 : return ret;
377 : }
378 :
379 0 : ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
380 : &tmp, sizeof(tmp));
381 0 : if (ret) {
382 0 : drm_dbg_kms(dev,
383 : "I2C read failed during TMDS output buffer %s (%d attempts)\n",
384 : enable ? "enabling" : "disabling", retry + 1);
385 0 : return ret;
386 : }
387 :
388 0 : if (tmp == tmds_oen)
389 : return 0;
390 : }
391 :
392 0 : drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
393 : enable ? "enabling" : "disabling");
394 :
395 0 : return -EIO;
396 : }
397 : EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
398 :
399 : /**
400 : * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
401 : * @type: DP dual mode adaptor type
402 : *
403 : * Returns:
404 : * String representation of the DP dual mode adaptor type
405 : */
406 0 : const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
407 : {
408 0 : switch (type) {
409 : case DRM_DP_DUAL_MODE_NONE:
410 : return "none";
411 : case DRM_DP_DUAL_MODE_TYPE1_DVI:
412 0 : return "type 1 DVI";
413 : case DRM_DP_DUAL_MODE_TYPE1_HDMI:
414 0 : return "type 1 HDMI";
415 : case DRM_DP_DUAL_MODE_TYPE2_DVI:
416 0 : return "type 2 DVI";
417 : case DRM_DP_DUAL_MODE_TYPE2_HDMI:
418 0 : return "type 2 HDMI";
419 : case DRM_DP_DUAL_MODE_LSPCON:
420 0 : return "lspcon";
421 : default:
422 0 : WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
423 : return "unknown";
424 : }
425 : }
426 : EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
427 :
428 : /**
429 : * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
430 : * reading offset (0x80, 0x41)
431 : * @dev: &drm_device to use
432 : * @adapter: I2C-over-aux adapter
433 : * @mode: current lspcon mode of operation output variable
434 : *
435 : * Returns:
436 : * 0 on success, sets the current_mode value to appropriate mode
437 : * -error on failure
438 : */
439 0 : int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
440 : enum drm_lspcon_mode *mode)
441 : {
442 : u8 data;
443 0 : int ret = 0;
444 : int retry;
445 :
446 0 : if (!mode) {
447 0 : drm_err(dev, "NULL input\n");
448 0 : return -EINVAL;
449 : }
450 :
451 : /* Read Status: i2c over aux */
452 0 : for (retry = 0; retry < 6; retry++) {
453 0 : if (retry)
454 : usleep_range(500, 1000);
455 :
456 0 : ret = drm_dp_dual_mode_read(adapter,
457 : DP_DUAL_MODE_LSPCON_CURRENT_MODE,
458 : &data, sizeof(data));
459 0 : if (!ret)
460 : break;
461 : }
462 :
463 0 : if (ret < 0) {
464 0 : drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
465 0 : return -EFAULT;
466 : }
467 :
468 0 : if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
469 0 : *mode = DRM_LSPCON_MODE_PCON;
470 : else
471 0 : *mode = DRM_LSPCON_MODE_LS;
472 : return 0;
473 : }
474 : EXPORT_SYMBOL(drm_lspcon_get_mode);
475 :
476 : /**
477 : * drm_lspcon_set_mode: Change LSPCON's mode of operation by
478 : * writing offset (0x80, 0x40)
479 : * @dev: &drm_device to use
480 : * @adapter: I2C-over-aux adapter
481 : * @mode: required mode of operation
482 : *
483 : * Returns:
484 : * 0 on success, -error on failure/timeout
485 : */
486 0 : int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
487 : enum drm_lspcon_mode mode)
488 : {
489 0 : u8 data = 0;
490 : int ret;
491 0 : int time_out = 200;
492 : enum drm_lspcon_mode current_mode;
493 :
494 0 : if (mode == DRM_LSPCON_MODE_PCON)
495 0 : data = DP_DUAL_MODE_LSPCON_MODE_PCON;
496 :
497 : /* Change mode */
498 0 : ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
499 : &data, sizeof(data));
500 0 : if (ret < 0) {
501 0 : drm_err(dev, "LSPCON mode change failed\n");
502 0 : return ret;
503 : }
504 :
505 : /*
506 : * Confirm mode change by reading the status bit.
507 : * Sometimes, it takes a while to change the mode,
508 : * so wait and retry until time out or done.
509 : */
510 : do {
511 0 : ret = drm_lspcon_get_mode(dev, adapter, ¤t_mode);
512 0 : if (ret) {
513 0 : drm_err(dev, "can't confirm LSPCON mode change\n");
514 0 : return ret;
515 : } else {
516 0 : if (current_mode != mode) {
517 0 : msleep(10);
518 0 : time_out -= 10;
519 : } else {
520 0 : drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
521 : mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
522 0 : return 0;
523 : }
524 : }
525 0 : } while (time_out);
526 :
527 0 : drm_err(dev, "LSPCON mode change timed out\n");
528 0 : return -ETIMEDOUT;
529 : }
530 : EXPORT_SYMBOL(drm_lspcon_set_mode);
|