0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #include <linux/delay.h>
0024 #include <linux/errno.h>
0025 #include <linux/export.h>
0026 #include <linux/i2c.h>
0027 #include <linux/slab.h>
0028 #include <linux/string.h>
0029
0030 #include <drm/display/drm_dp_dual_mode_helper.h>
0031 #include <drm/drm_device.h>
0032 #include <drm/drm_print.h>
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 #define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
0064 u8 offset, void *buffer, size_t size)
0065 {
0066 struct i2c_msg msgs[] = {
0067 {
0068 .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0069 .flags = 0,
0070 .len = 1,
0071 .buf = &offset,
0072 },
0073 {
0074 .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0075 .flags = I2C_M_RD,
0076 .len = size,
0077 .buf = buffer,
0078 },
0079 };
0080 int ret;
0081
0082 ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
0083 if (ret < 0)
0084 return ret;
0085 if (ret != ARRAY_SIZE(msgs))
0086 return -EPROTO;
0087
0088 return 0;
0089 }
0090 EXPORT_SYMBOL(drm_dp_dual_mode_read);
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105 ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
0106 u8 offset, const void *buffer, size_t size)
0107 {
0108 struct i2c_msg msg = {
0109 .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0110 .flags = 0,
0111 .len = 1 + size,
0112 .buf = NULL,
0113 };
0114 void *data;
0115 int ret;
0116
0117 data = kmalloc(msg.len, GFP_KERNEL);
0118 if (!data)
0119 return -ENOMEM;
0120
0121 msg.buf = data;
0122
0123 memcpy(data, &offset, 1);
0124 memcpy(data + 1, buffer, size);
0125
0126 ret = i2c_transfer(adapter, &msg, 1);
0127
0128 kfree(data);
0129
0130 if (ret < 0)
0131 return ret;
0132 if (ret != 1)
0133 return -EPROTO;
0134
0135 return 0;
0136 }
0137 EXPORT_SYMBOL(drm_dp_dual_mode_write);
0138
0139 static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
0140 {
0141 static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
0142 "DP-HDMI ADAPTOR\x04";
0143
0144 return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
0145 sizeof(dp_dual_mode_hdmi_id)) == 0;
0146 }
0147
0148 static bool is_type1_adaptor(uint8_t adaptor_id)
0149 {
0150 return adaptor_id == 0 || adaptor_id == 0xff;
0151 }
0152
0153 static bool is_type2_adaptor(uint8_t adaptor_id)
0154 {
0155 return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
0156 DP_DUAL_MODE_REV_TYPE2);
0157 }
0158
0159 static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
0160 const uint8_t adaptor_id)
0161 {
0162 return is_hdmi_adaptor(hdmi_id) &&
0163 (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
0164 DP_DUAL_MODE_TYPE_HAS_DPCD));
0165 }
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183 enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
0184 struct i2c_adapter *adapter)
0185 {
0186 char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
0187 uint8_t adaptor_id = 0x00;
0188 ssize_t ret;
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
0205 hdmi_id, sizeof(hdmi_id));
0206 drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
0207 ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
0208 if (ret)
0209 return DRM_DP_DUAL_MODE_UNKNOWN;
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
0224 &adaptor_id, sizeof(adaptor_id));
0225 drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
0226 if (ret == 0) {
0227 if (is_lspcon_adaptor(hdmi_id, adaptor_id))
0228 return DRM_DP_DUAL_MODE_LSPCON;
0229 if (is_type2_adaptor(adaptor_id)) {
0230 if (is_hdmi_adaptor(hdmi_id))
0231 return DRM_DP_DUAL_MODE_TYPE2_HDMI;
0232 else
0233 return DRM_DP_DUAL_MODE_TYPE2_DVI;
0234 }
0235
0236
0237
0238
0239
0240 if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
0241 drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
0242
0243 }
0244
0245 if (is_hdmi_adaptor(hdmi_id))
0246 return DRM_DP_DUAL_MODE_TYPE1_HDMI;
0247 else
0248 return DRM_DP_DUAL_MODE_TYPE1_DVI;
0249 }
0250 EXPORT_SYMBOL(drm_dp_dual_mode_detect);
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268 int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
0269 struct i2c_adapter *adapter)
0270 {
0271 uint8_t max_tmds_clock;
0272 ssize_t ret;
0273
0274
0275 if (type == DRM_DP_DUAL_MODE_NONE)
0276 return 0;
0277
0278
0279
0280
0281
0282 if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
0283 return 165000;
0284
0285 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
0286 &max_tmds_clock, sizeof(max_tmds_clock));
0287 if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
0288 drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
0289 return 165000;
0290 }
0291
0292 return max_tmds_clock * 5000 / 2;
0293 }
0294 EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313 int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
0314 enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
0315 bool *enabled)
0316 {
0317 uint8_t tmds_oen;
0318 ssize_t ret;
0319
0320 if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
0321 *enabled = true;
0322 return 0;
0323 }
0324
0325 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
0326 &tmds_oen, sizeof(tmds_oen));
0327 if (ret) {
0328 drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
0329 return ret;
0330 }
0331
0332 *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
0333
0334 return 0;
0335 }
0336 EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354 int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
0355 struct i2c_adapter *adapter, bool enable)
0356 {
0357 uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
0358 ssize_t ret;
0359 int retry;
0360
0361 if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
0362 return 0;
0363
0364
0365
0366
0367
0368 for (retry = 0; retry < 3; retry++) {
0369 uint8_t tmp;
0370
0371 ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
0372 &tmds_oen, sizeof(tmds_oen));
0373 if (ret) {
0374 drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
0375 enable ? "enable" : "disable", retry + 1);
0376 return ret;
0377 }
0378
0379 ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
0380 &tmp, sizeof(tmp));
0381 if (ret) {
0382 drm_dbg_kms(dev,
0383 "I2C read failed during TMDS output buffer %s (%d attempts)\n",
0384 enable ? "enabling" : "disabling", retry + 1);
0385 return ret;
0386 }
0387
0388 if (tmp == tmds_oen)
0389 return 0;
0390 }
0391
0392 drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
0393 enable ? "enabling" : "disabling");
0394
0395 return -EIO;
0396 }
0397 EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
0398
0399
0400
0401
0402
0403
0404
0405
0406 const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
0407 {
0408 switch (type) {
0409 case DRM_DP_DUAL_MODE_NONE:
0410 return "none";
0411 case DRM_DP_DUAL_MODE_TYPE1_DVI:
0412 return "type 1 DVI";
0413 case DRM_DP_DUAL_MODE_TYPE1_HDMI:
0414 return "type 1 HDMI";
0415 case DRM_DP_DUAL_MODE_TYPE2_DVI:
0416 return "type 2 DVI";
0417 case DRM_DP_DUAL_MODE_TYPE2_HDMI:
0418 return "type 2 HDMI";
0419 case DRM_DP_DUAL_MODE_LSPCON:
0420 return "lspcon";
0421 default:
0422 WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
0423 return "unknown";
0424 }
0425 }
0426 EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
0427
0428
0429
0430
0431
0432
0433
0434
0435
0436
0437
0438
0439 int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
0440 enum drm_lspcon_mode *mode)
0441 {
0442 u8 data;
0443 int ret = 0;
0444 int retry;
0445
0446 if (!mode) {
0447 drm_err(dev, "NULL input\n");
0448 return -EINVAL;
0449 }
0450
0451
0452 for (retry = 0; retry < 6; retry++) {
0453 if (retry)
0454 usleep_range(500, 1000);
0455
0456 ret = drm_dp_dual_mode_read(adapter,
0457 DP_DUAL_MODE_LSPCON_CURRENT_MODE,
0458 &data, sizeof(data));
0459 if (!ret)
0460 break;
0461 }
0462
0463 if (ret < 0) {
0464 drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
0465 return -EFAULT;
0466 }
0467
0468 if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
0469 *mode = DRM_LSPCON_MODE_PCON;
0470 else
0471 *mode = DRM_LSPCON_MODE_LS;
0472 return 0;
0473 }
0474 EXPORT_SYMBOL(drm_lspcon_get_mode);
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486 int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
0487 enum drm_lspcon_mode mode)
0488 {
0489 u8 data = 0;
0490 int ret;
0491 int time_out = 200;
0492 enum drm_lspcon_mode current_mode;
0493
0494 if (mode == DRM_LSPCON_MODE_PCON)
0495 data = DP_DUAL_MODE_LSPCON_MODE_PCON;
0496
0497
0498 ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
0499 &data, sizeof(data));
0500 if (ret < 0) {
0501 drm_err(dev, "LSPCON mode change failed\n");
0502 return ret;
0503 }
0504
0505
0506
0507
0508
0509
0510 do {
0511 ret = drm_lspcon_get_mode(dev, adapter, ¤t_mode);
0512 if (ret) {
0513 drm_err(dev, "can't confirm LSPCON mode change\n");
0514 return ret;
0515 } else {
0516 if (current_mode != mode) {
0517 msleep(10);
0518 time_out -= 10;
0519 } else {
0520 drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
0521 mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
0522 return 0;
0523 }
0524 }
0525 } while (time_out);
0526
0527 drm_err(dev, "LSPCON mode change timed out\n");
0528 return -ETIMEDOUT;
0529 }
0530 EXPORT_SYMBOL(drm_lspcon_set_mode);