FILE
displayport.c
File preview with syntax highlighting.
linux-6.19.14/drivers/usb/typec/altmodes/displayport.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * USB Typec-C DisplayPort Alternate Mode driver 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 * 8 * DisplayPort is trademark of VESA (www.vesa.org) 9 */ 10 11#include <linux/delay.h> 12#include <linux/mutex.h> 13#include <linux/module.h> 14#include <linux/property.h> 15#include <linux/usb/pd_vdo.h> 16#include <linux/usb/typec_dp.h> 17#include <drm/drm_connector.h> 18#include "displayport.h" 19 20#define DP_HEADER(_dp, ver, cmd) (VDO((_dp)->alt->svid, 1, ver, cmd) \ 21 | VDO_OPOS(USB_TYPEC_DP_MODE)) 22 23enum { 24 DP_CONF_USB, 25 DP_CONF_DFP_D, 26 DP_CONF_UFP_D, 27 DP_CONF_DUAL_D, 28}; 29 30/* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */ 31#define DP_PIN_ASSIGN_GEN2_BR_MASK (BIT(DP_PIN_ASSIGN_A) | \ 32 BIT(DP_PIN_ASSIGN_B)) 33 34/* Pin assignments that use DP v1.3 signaling to carry DP protocol */ 35#define DP_PIN_ASSIGN_DP_BR_MASK (BIT(DP_PIN_ASSIGN_C) | \ 36 BIT(DP_PIN_ASSIGN_D) | \ 37 BIT(DP_PIN_ASSIGN_E) | \ 38 BIT(DP_PIN_ASSIGN_F)) 39 40/* DP only pin assignments */ 41#define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \ 42 BIT(DP_PIN_ASSIGN_C) | \ 43 BIT(DP_PIN_ASSIGN_E)) 44 45/* Pin assignments where one channel is for USB */ 46#define DP_PIN_ASSIGN_MULTI_FUNC_MASK (BIT(DP_PIN_ASSIGN_B) | \ 47 BIT(DP_PIN_ASSIGN_D) | \ 48 BIT(DP_PIN_ASSIGN_F)) 49 50enum dp_state { 51 DP_STATE_IDLE, 52 DP_STATE_ENTER, 53 DP_STATE_ENTER_PRIME, 54 DP_STATE_UPDATE, 55 DP_STATE_CONFIGURE, 56 DP_STATE_CONFIGURE_PRIME, 57 DP_STATE_EXIT, 58 DP_STATE_EXIT_PRIME, 59}; 60 61struct dp_altmode { 62 struct typec_displayport_data data; 63 struct typec_displayport_data data_prime; 64 65 enum dp_state state; 66 bool hpd; 67 bool pending_hpd; 68 u32 irq_hpd_count; 69 /* 70 * hpd is mandatory for irq_hpd assertion, so irq_hpd also needs its own pending flag if 71 * both hpd and irq_hpd are asserted in the first Status Update before the pin assignment 72 * is configured. 73 */ 74 bool pending_irq_hpd; 75 76 struct mutex lock; /* device lock */ 77 struct work_struct work; 78 struct typec_altmode *alt; 79 const struct typec_altmode *port; 80 struct fwnode_handle *connector_fwnode; 81 struct typec_altmode *plug_prime; 82}; 83 84static int dp_altmode_notify(struct dp_altmode *dp) 85{ 86 unsigned long conf; 87 u8 state; 88 89 if (dp->data.conf) { 90 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); 91 conf = TYPEC_MODAL_STATE(state); 92 } else { 93 conf = TYPEC_STATE_USB; 94 } 95 96 return typec_altmode_notify(dp->alt, conf, &dp->data); 97} 98 99static int dp_altmode_configure(struct dp_altmode *dp, u8 con) 100{ 101 u8 pin_assign = 0; 102 u32 conf; 103 u32 signal; 104 105 /* DP Signalling */ 106 signal = DP_CAP_DP_SIGNALLING(dp->port->vdo) & DP_CAP_DP_SIGNALLING(dp->alt->vdo); 107 if (dp->plug_prime) 108 signal &= DP_CAP_DP_SIGNALLING(dp->plug_prime->vdo); 109 110 conf = signal << DP_CONF_SIGNALLING_SHIFT; 111 112 switch (con) { 113 case DP_STATUS_CON_DISABLED: 114 return 0; 115 case DP_STATUS_CON_DFP_D: 116 conf |= DP_CONF_UFP_U_AS_DFP_D; 117 pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) & 118 DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); 119 /* Account for active cable capabilities */ 120 if (dp->plug_prime) 121 pin_assign &= DP_CAP_DFP_D_PIN_ASSIGN(dp->plug_prime->vdo); 122 break; 123 case DP_STATUS_CON_UFP_D: 124 case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */ 125 conf |= DP_CONF_UFP_U_AS_UFP_D; 126 pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) & 127 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo); 128 /* Account for active cable capabilities */ 129 if (dp->plug_prime) 130 pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo); 131 break; 132 default: 133 break; 134 } 135 136 /* Determining the initial pin assignment. */ 137 if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) { 138 /* Is USB together with DP preferred */ 139 if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC && 140 pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK) 141 pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK; 142 else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) { 143 pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK; 144 /* Default to pin assign C if available */ 145 if (pin_assign & BIT(DP_PIN_ASSIGN_C)) 146 pin_assign = BIT(DP_PIN_ASSIGN_C); 147 } 148 149 if (!pin_assign) 150 return -EINVAL; 151 152 conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign); 153 } 154 155 dp->data.conf = conf; 156 if (dp->plug_prime) 157 dp->data_prime.conf = conf; 158 159 return 0; 160} 161 162static int dp_altmode_status_update(struct dp_altmode *dp) 163{ 164 bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); 165 bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE); 166 bool irq_hpd = !!(dp->data.status & DP_STATUS_IRQ_HPD); 167 u8 con = DP_STATUS_CONNECTION(dp->data.status); 168 int ret = 0; 169 170 if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) { 171 dp->data.conf = 0; 172 dp->data_prime.conf = 0; 173 dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME : 174 DP_STATE_CONFIGURE; 175 } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) { 176 dp->state = DP_STATE_EXIT; 177 } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) { 178 ret = dp_altmode_configure(dp, con); 179 if (!ret) { 180 dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME : 181 DP_STATE_CONFIGURE; 182 if (dp->hpd != hpd) { 183 dp->hpd = hpd; 184 dp->pending_hpd = true; 185 } 186 if (dp->hpd && dp->pending_hpd && irq_hpd) 187 dp->pending_irq_hpd = true; 188 } 189 } else { 190 drm_connector_oob_hotplug_event(dp->connector_fwnode, 191 hpd ? connector_status_connected : 192 connector_status_disconnected); 193 dp->hpd = hpd; 194 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); 195 if (hpd && irq_hpd) { 196 dp->irq_hpd_count++; 197 sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd"); 198 } 199 } 200 201 return ret; 202} 203 204static int dp_altmode_configured(struct dp_altmode *dp) 205{ 206 sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); 207 sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); 208 /* 209 * If the DFP_D/UFP_D sends a change in HPD when first notifying the 210 * DisplayPort driver that it is connected, then we wait until 211 * configuration is complete to signal HPD. 212 */ 213 if (dp->pending_hpd) { 214 drm_connector_oob_hotplug_event(dp->connector_fwnode, 215 connector_status_connected); 216 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); 217 dp->pending_hpd = false; 218 if (dp->pending_irq_hpd) { 219 dp->irq_hpd_count++; 220 sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd"); 221 dp->pending_irq_hpd = false; 222 } 223 } 224 225 return dp_altmode_notify(dp); 226} 227 228static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) 229{ 230 int svdm_version = typec_altmode_get_svdm_version(dp->alt); 231 u32 header; 232 int ret; 233 234 if (svdm_version < 0) 235 return svdm_version; 236 237 header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE); 238 ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); 239 if (ret) { 240 dev_err(&dp->alt->dev, 241 "unable to put to connector to safe mode\n"); 242 return ret; 243 } 244 245 ret = typec_altmode_vdm(dp->alt, header, &conf, 2); 246 if (ret) 247 dp_altmode_notify(dp); 248 249 return ret; 250} 251 252static int dp_altmode_configure_vdm_cable(struct dp_altmode *dp, u32 conf) 253{ 254 int svdm_version = typec_altmode_get_cable_svdm_version(dp->plug_prime); 255 u32 header; 256 257 if (svdm_version < 0) 258 return svdm_version; 259 260 header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE); 261 262 return typec_cable_altmode_vdm(dp->plug_prime, TYPEC_PLUG_SOP_P, header, &conf, 2); 263} 264 265static void dp_altmode_work(struct work_struct *work) 266{ 267 struct dp_altmode *dp = container_of(work, struct dp_altmode, work); 268 int svdm_version; 269 u32 header; 270 u32 vdo; 271 int ret; 272 273 mutex_lock(&dp->lock); 274 275 switch (dp->state) { 276 case DP_STATE_ENTER: 277 ret = typec_altmode_enter(dp->alt, NULL); 278 if (ret && ret != -EBUSY) 279 dev_err(&dp->alt->dev, "failed to enter mode: %d\n", ret); 280 break; 281 case DP_STATE_ENTER_PRIME: 282 ret = typec_cable_altmode_enter(dp->alt, TYPEC_PLUG_SOP_P, NULL); 283 /* 284 * If we fail to enter Alt Mode on SOP', then we should drop the 285 * plug from the driver and attempt to run the driver without 286 * it. 287 */ 288 if (ret && ret != -EBUSY) { 289 dev_err(&dp->alt->dev, "plug failed to enter mode\n"); 290 dp->state = DP_STATE_ENTER; 291 goto disable_prime; 292 } 293 break; 294 case DP_STATE_UPDATE: 295 svdm_version = typec_altmode_get_svdm_version(dp->alt); 296 if (svdm_version < 0) 297 break; 298 header = DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE); 299 vdo = 1; 300 ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); 301 if (ret) 302 dev_err(&dp->alt->dev, 303 "unable to send Status Update command (%d)\n", 304 ret); 305 break; 306 case DP_STATE_CONFIGURE: 307 ret = dp_altmode_configure_vdm(dp, dp->data.conf); 308 if (ret) 309 dev_err(&dp->alt->dev, 310 "unable to send Configure command (%d)\n", ret); 311 break; 312 case DP_STATE_CONFIGURE_PRIME: 313 ret = dp_altmode_configure_vdm_cable(dp, dp->data_prime.conf); 314 if (ret) { 315 dev_err(&dp->plug_prime->dev, 316 "unable to send Configure command (%d)\n", 317 ret); 318 dp->state = DP_STATE_CONFIGURE; 319 goto disable_prime; 320 } 321 break; 322 case DP_STATE_EXIT: 323 if (typec_altmode_exit(dp->alt)) 324 dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); 325 break; 326 case DP_STATE_EXIT_PRIME: 327 if (typec_cable_altmode_exit(dp->plug_prime, TYPEC_PLUG_SOP_P)) 328 dev_err(&dp->plug_prime->dev, "Exit Mode Failed!\n"); 329 break; 330 default: 331 break; 332 } 333 334 dp->state = DP_STATE_IDLE; 335 336 mutex_unlock(&dp->lock); 337 return; 338 339disable_prime: 340 typec_altmode_put_plug(dp->plug_prime); 341 dp->plug_prime = NULL; 342 schedule_work(&dp->work); 343 mutex_unlock(&dp->lock); 344} 345 346static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo) 347{ 348 struct dp_altmode *dp = typec_altmode_get_drvdata(alt); 349 u8 old_state; 350 351 mutex_lock(&dp->lock); 352 353 old_state = dp->state; 354 dp->data.status = vdo; 355 356 if (old_state != DP_STATE_IDLE) 357 dev_warn(&alt->dev, "ATTENTION while processing state %d\n", 358 old_state); 359 360 if (dp_altmode_status_update(dp)) 361 dev_warn(&alt->dev, "%s: status update failed\n", __func__); 362 363 if (dp_altmode_notify(dp)) 364 dev_err(&alt->dev, "%s: notification failed\n", __func__); 365 366 if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE) 367 schedule_work(&dp->work); 368 369 mutex_unlock(&dp->lock); 370} 371 372static int dp_altmode_vdm(struct typec_altmode *alt, 373 const u32 hdr, const u32 *vdo, int count) 374{ 375 struct dp_altmode *dp = typec_altmode_get_drvdata(alt); 376 int cmd_type = PD_VDO_CMDT(hdr); 377 int cmd = PD_VDO_CMD(hdr); 378 int ret = 0; 379 380 mutex_lock(&dp->lock); 381 382 if (dp->state != DP_STATE_IDLE) { 383 ret = -EBUSY; 384 goto err_unlock; 385 } 386 387 switch (cmd_type) { 388 case CMDT_RSP_ACK: 389 switch (cmd) { 390 case CMD_ENTER_MODE: 391 typec_altmode_update_active(alt, true); 392 dp->state = DP_STATE_UPDATE; 393 break; 394 case CMD_EXIT_MODE: 395 typec_altmode_update_active(alt, false); 396 dp->data.status = 0; 397 dp->data.conf = 0; 398 if (dp->hpd) { 399 drm_connector_oob_hotplug_event(dp->connector_fwnode, 400 connector_status_disconnected); 401 dp->hpd = false; 402 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); 403 } 404 if (dp->plug_prime) 405 dp->state = DP_STATE_EXIT_PRIME; 406 break; 407 case DP_CMD_STATUS_UPDATE: 408 dp->data.status = *vdo; 409 ret = dp_altmode_status_update(dp); 410 break; 411 case DP_CMD_CONFIGURE: 412 ret = dp_altmode_configured(dp); 413 break; 414 default: 415 break; 416 } 417 break; 418 case CMDT_RSP_NAK: 419 switch (cmd) { 420 case DP_CMD_STATUS_UPDATE: 421 dp->state = DP_STATE_EXIT; 422 break; 423 case DP_CMD_CONFIGURE: 424 dp->data.conf = 0; 425 ret = dp_altmode_configured(dp); 426 break; 427 default: 428 break; 429 } 430 break; 431 default: 432 break; 433 } 434 435 if (dp->state != DP_STATE_IDLE) 436 schedule_work(&dp->work); 437 438err_unlock: 439 mutex_unlock(&dp->lock); 440 return ret; 441} 442 443static int dp_cable_altmode_vdm(struct typec_altmode *alt, enum typec_plug_index sop, 444 const u32 hdr, const u32 *vdo, int count) 445{ 446 struct dp_altmode *dp = typec_altmode_get_drvdata(alt); 447 int cmd_type = PD_VDO_CMDT(hdr); 448 int cmd = PD_VDO_CMD(hdr); 449 int ret = 0; 450 451 mutex_lock(&dp->lock); 452 453 if (dp->state != DP_STATE_IDLE) { 454 ret = -EBUSY; 455 goto err_unlock; 456 } 457 458 switch (cmd_type) { 459 case CMDT_RSP_ACK: 460 switch (cmd) { 461 case CMD_ENTER_MODE: 462 typec_altmode_update_active(dp->plug_prime, true); 463 dp->state = DP_STATE_ENTER; 464 break; 465 case CMD_EXIT_MODE: 466 dp->data_prime.status = 0; 467 dp->data_prime.conf = 0; 468 typec_altmode_update_active(dp->plug_prime, false); 469 break; 470 case DP_CMD_CONFIGURE: 471 dp->state = DP_STATE_CONFIGURE; 472 break; 473 default: 474 break; 475 } 476 break; 477 case CMDT_RSP_NAK: 478 switch (cmd) { 479 case DP_CMD_CONFIGURE: 480 dp->data_prime.conf = 0; 481 /* Attempt to configure on SOP, drop plug */ 482 typec_altmode_put_plug(dp->plug_prime); 483 dp->plug_prime = NULL; 484 dp->state = DP_STATE_CONFIGURE; 485 break; 486 default: 487 break; 488 } 489 break; 490 default: 491 break; 492 } 493 494 if (dp->state != DP_STATE_IDLE) 495 schedule_work(&dp->work); 496 497err_unlock: 498 mutex_unlock(&dp->lock); 499 return ret; 500} 501 502static int dp_altmode_activate(struct typec_altmode *alt, int activate) 503{ 504 struct dp_altmode *dp = typec_altmode_get_drvdata(alt); 505 int ret; 506 507 if (activate) { 508 if (dp->plug_prime) { 509 ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL); 510 if (ret < 0) { 511 typec_altmode_put_plug(dp->plug_prime); 512 dp->plug_prime = NULL; 513 } else { 514 return ret; 515 } 516 } 517 return typec_altmode_enter(alt, NULL); 518 } else { 519 return typec_altmode_exit(alt); 520 } 521} 522 523static const struct typec_altmode_ops dp_altmode_ops = { 524 .attention = dp_altmode_attention, 525 .vdm = dp_altmode_vdm, 526 .activate = dp_altmode_activate, 527}; 528 529static const struct typec_cable_ops dp_cable_ops = { 530 .vdm = dp_cable_altmode_vdm, 531}; 532 533static const char * const configurations[] = { 534 [DP_CONF_USB] = "USB", 535 [DP_CONF_DFP_D] = "source", 536 [DP_CONF_UFP_D] = "sink", 537}; 538 539static ssize_t 540configuration_store(struct device *dev, struct device_attribute *attr, 541 const char *buf, size_t size) 542{ 543 struct dp_altmode *dp = dev_get_drvdata(dev); 544 u32 conf; 545 u32 cap; 546 int con; 547 int ret = 0; 548 549 con = sysfs_match_string(configurations, buf); 550 if (con < 0) 551 return con; 552 553 mutex_lock(&dp->lock); 554 555 if (dp->state != DP_STATE_IDLE) { 556 ret = -EBUSY; 557 goto err_unlock; 558 } 559 560 cap = DP_CAP_CAPABILITY(dp->alt->vdo); 561 562 if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) || 563 (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) { 564 ret = -EINVAL; 565 goto err_unlock; 566 } 567 568 conf = dp->data.conf & ~DP_CONF_DUAL_D; 569 conf |= con; 570 571 if (dp->alt->active) { 572 ret = dp_altmode_configure_vdm(dp, conf); 573 if (ret) 574 goto err_unlock; 575 } 576 577 dp->data.conf = conf; 578 579err_unlock: 580 mutex_unlock(&dp->lock); 581 582 return ret ? ret : size; 583} 584 585static ssize_t configuration_show(struct device *dev, 586 struct device_attribute *attr, char *buf) 587{ 588 struct dp_altmode *dp = dev_get_drvdata(dev); 589 int len; 590 u8 cap; 591 u8 cur; 592 int i; 593 594 mutex_lock(&dp->lock); 595 596 cap = DP_CAP_CAPABILITY(dp->alt->vdo); 597 cur = DP_CONF_CURRENTLY(dp->data.conf); 598 599 len = sprintf(buf, "%s ", cur ? "USB" : "[USB]"); 600 601 for (i = 1; i < ARRAY_SIZE(configurations); i++) { 602 if (i == cur) 603 len += sprintf(buf + len, "[%s] ", configurations[i]); 604 else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) || 605 (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D)) 606 len += sprintf(buf + len, "%s ", configurations[i]); 607 } 608 609 mutex_unlock(&dp->lock); 610 611 buf[len - 1] = '\n'; 612 return len; 613} 614static DEVICE_ATTR_RW(configuration); 615 616static const char * const pin_assignments[] = { 617 [DP_PIN_ASSIGN_A] = "A", 618 [DP_PIN_ASSIGN_B] = "B", 619 [DP_PIN_ASSIGN_C] = "C", 620 [DP_PIN_ASSIGN_D] = "D", 621 [DP_PIN_ASSIGN_E] = "E", 622 [DP_PIN_ASSIGN_F] = "F", 623}; 624 625/* 626 * Helper function to extract a peripheral's currently supported 627 * Pin Assignments from its DisplayPort alternate mode state. 628 */ 629static u8 get_current_pin_assignments(struct dp_altmode *dp) 630{ 631 if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_UFP_U_AS_DFP_D) 632 return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo); 633 else 634 return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo); 635} 636 637static ssize_t 638pin_assignment_store(struct device *dev, struct device_attribute *attr, 639 const char *buf, size_t size) 640{ 641 struct dp_altmode *dp = dev_get_drvdata(dev); 642 u8 assignments; 643 u32 conf; 644 int ret; 645 646 ret = sysfs_match_string(pin_assignments, buf); 647 if (ret < 0) 648 return ret; 649 650 conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret)); 651 ret = 0; 652 653 mutex_lock(&dp->lock); 654 655 if (conf & dp->data.conf) 656 goto out_unlock; 657 658 if (dp->state != DP_STATE_IDLE) { 659 ret = -EBUSY; 660 goto out_unlock; 661 } 662 663 assignments = get_current_pin_assignments(dp); 664 665 if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) { 666 ret = -EINVAL; 667 goto out_unlock; 668 } 669 670 conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK; 671 672 /* Only send Configure command if a configuration has been set */ 673 if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) { 674 /* todo: send manual configure over SOP'*/ 675 ret = dp_altmode_configure_vdm(dp, conf); 676 if (ret) 677 goto out_unlock; 678 } 679 680 dp->data.conf = conf; 681 682out_unlock: 683 mutex_unlock(&dp->lock); 684 685 return ret ? ret : size; 686} 687 688static ssize_t pin_assignment_show(struct device *dev, 689 struct device_attribute *attr, char *buf) 690{ 691 struct dp_altmode *dp = dev_get_drvdata(dev); 692 u8 assignments; 693 int len = 0; 694 u8 cur; 695 int i; 696 697 mutex_lock(&dp->lock); 698 699 cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); 700 701 assignments = get_current_pin_assignments(dp); 702 703 for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) { 704 if (assignments & 1) { 705 if (i == cur) 706 len += sprintf(buf + len, "[%s] ", 707 pin_assignments[i]); 708 else 709 len += sprintf(buf + len, "%s ", 710 pin_assignments[i]); 711 } 712 } 713 714 mutex_unlock(&dp->lock); 715 716 /* get_current_pin_assignments can return 0 when no matching pin assignments are found */ 717 if (len == 0) 718 len++; 719 720 buf[len - 1] = '\n'; 721 return len; 722} 723static DEVICE_ATTR_RW(pin_assignment); 724 725static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf) 726{ 727 struct dp_altmode *dp = dev_get_drvdata(dev); 728 729 return sysfs_emit(buf, "%d\n", dp->hpd); 730} 731static DEVICE_ATTR_RO(hpd); 732 733static ssize_t irq_hpd_show(struct device *dev, struct device_attribute *attr, char *buf) 734{ 735 struct dp_altmode *dp = dev_get_drvdata(dev); 736 737 return sysfs_emit(buf, "%d\n", dp->irq_hpd_count); 738} 739static DEVICE_ATTR_RO(irq_hpd); 740 741static struct attribute *displayport_attrs[] = { 742 &dev_attr_configuration.attr, 743 &dev_attr_pin_assignment.attr, 744 &dev_attr_hpd.attr, 745 &dev_attr_irq_hpd.attr, 746 NULL 747}; 748 749static const struct attribute_group displayport_group = { 750 .name = "displayport", 751 .attrs = displayport_attrs, 752}; 753 754static const struct attribute_group *displayport_groups[] = { 755 &displayport_group, 756 NULL, 757}; 758 759int dp_altmode_probe(struct typec_altmode *alt) 760{ 761 const struct typec_altmode *port = typec_altmode_get_partner(alt); 762 struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); 763 struct fwnode_handle *fwnode; 764 struct dp_altmode *dp; 765 766 /* Port can only be DFP_U. */ 767 if (typec_altmode_get_data_role(alt) != TYPEC_HOST) 768 return -EPROTO; 769 770 /* Make sure we have compatible pin configurations */ 771 if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & 772 DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && 773 !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & 774 DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) { 775 typec_altmode_put_plug(plug); 776 return -ENODEV; 777 } 778 779 dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); 780 if (!dp) { 781 typec_altmode_put_plug(plug); 782 return -ENOMEM; 783 } 784 785 INIT_WORK(&dp->work, dp_altmode_work); 786 mutex_init(&dp->lock); 787 dp->port = port; 788 dp->alt = alt; 789 790 alt->desc = "DisplayPort"; 791 typec_altmode_set_ops(alt, &dp_altmode_ops); 792 793 if (plug) { 794 plug->desc = "Displayport"; 795 plug->cable_ops = &dp_cable_ops; 796 } 797 798 dp->plug_prime = plug; 799 800 fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */ 801 if (fwnode_property_present(fwnode, "displayport")) 802 dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); 803 else 804 dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */ 805 if (IS_ERR(dp->connector_fwnode)) 806 dp->connector_fwnode = NULL; 807 808 typec_altmode_set_drvdata(alt, dp); 809 if (plug) 810 typec_altmode_set_drvdata(plug, dp); 811 812 dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; 813 schedule_work(&dp->work); 814 815 return 0; 816} 817EXPORT_SYMBOL_GPL(dp_altmode_probe); 818 819void dp_altmode_remove(struct typec_altmode *alt) 820{ 821 struct dp_altmode *dp = typec_altmode_get_drvdata(alt); 822 823 cancel_work_sync(&dp->work); 824 typec_altmode_put_plug(dp->plug_prime); 825 826 if (dp->connector_fwnode) { 827 drm_connector_oob_hotplug_event(dp->connector_fwnode, 828 connector_status_disconnected); 829 830 fwnode_handle_put(dp->connector_fwnode); 831 } 832} 833EXPORT_SYMBOL_GPL(dp_altmode_remove); 834 835static const struct typec_device_id dp_typec_id[] = { 836 { USB_TYPEC_DP_SID }, 837 { }, 838}; 839MODULE_DEVICE_TABLE(typec, dp_typec_id); 840 841static struct typec_altmode_driver dp_altmode_driver = { 842 .id_table = dp_typec_id, 843 .probe = dp_altmode_probe, 844 .remove = dp_altmode_remove, 845 .driver = { 846 .name = "typec_displayport", 847 .dev_groups = displayport_groups, 848 }, 849}; 850module_typec_altmode_driver(dp_altmode_driver); 851 852MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); 853MODULE_LICENSE("GPL v2"); 854MODULE_DESCRIPTION("DisplayPort Alternate Mode");
~/
Read-only file preview.