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