diff options
author | Thierry Reding <treding@nvidia.com> | 2013-12-12 03:57:53 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-02-26 11:21:34 -0500 |
commit | 88759686c702f1fbbb8e737e6231b64a9880db73 (patch) | |
tree | b803c33c31865165a95de0a7bfe68fb42082c1e3 | |
parent | 516c0f7c0a608833cc01d3f5b2a357ee806b78a1 (diff) |
drm/dp: Allow registering AUX channels as I2C busses
Implements an I2C-over-AUX I2C adapter on top of the generic drm_dp_aux
infrastructure. It extracts the retry logic from existing drivers, which
should help in porting those drivers to this new helper.
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v5:
- move comments partially to to header file
- keep MOT set between I2C messages
- return -EPROTO on short reads
Changes in v4:
- fix typo "bitrate" -> "bit rate"
Changes in v3:
- add back DRM_DEBUG_KMS and DRM_ERROR messages
- embed i2c_adapter within struct drm_dp_aux
- fix typo in comment
-rw-r--r-- | drivers/gpu/drm/drm_dp_helper.c | 186 | ||||
-rw-r--r-- | include/drm/drm_dp_helper.h | 12 |
2 files changed, 191 insertions, 7 deletions
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 177ac7bd1851..35251af3b14e 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c | |||
@@ -357,13 +357,6 @@ EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); | |||
357 | * Transactions are described using a hardware-independent drm_dp_aux_msg | 357 | * Transactions are described using a hardware-independent drm_dp_aux_msg |
358 | * structure, which is passed into a driver's .transfer() implementation. | 358 | * structure, which is passed into a driver's .transfer() implementation. |
359 | * Both native and I2C-over-AUX transactions are supported. | 359 | * Both native and I2C-over-AUX transactions are supported. |
360 | * | ||
361 | * An AUX channel can also be used to transport I2C messages to a sink. A | ||
362 | * typical application of that is to access an EDID that's present in the | ||
363 | * sink device. The .transfer() function can also be used to execute such | ||
364 | * transactions. The drm_dp_aux_register_i2c_bus() function registers an | ||
365 | * I2C adapter that can be passed to drm_probe_ddc(). Upon removal, drivers | ||
366 | * should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter. | ||
367 | */ | 360 | */ |
368 | 361 | ||
369 | static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, | 362 | static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, |
@@ -569,3 +562,182 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) | |||
569 | return 0; | 562 | return 0; |
570 | } | 563 | } |
571 | EXPORT_SYMBOL(drm_dp_link_configure); | 564 | EXPORT_SYMBOL(drm_dp_link_configure); |
565 | |||
566 | /* | ||
567 | * I2C-over-AUX implementation | ||
568 | */ | ||
569 | |||
570 | static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) | ||
571 | { | ||
572 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | | ||
573 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | | ||
574 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
575 | I2C_FUNC_10BIT_ADDR; | ||
576 | } | ||
577 | |||
578 | /* | ||
579 | * Transfer a single I2C-over-AUX message and handle various error conditions, | ||
580 | * retrying the transaction as appropriate. | ||
581 | */ | ||
582 | static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) | ||
583 | { | ||
584 | unsigned int retry; | ||
585 | int err; | ||
586 | |||
587 | /* | ||
588 | * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device | ||
589 | * is required to retry at least seven times upon receiving AUX_DEFER | ||
590 | * before giving up the AUX transaction. | ||
591 | */ | ||
592 | for (retry = 0; retry < 7; retry++) { | ||
593 | err = aux->transfer(aux, msg); | ||
594 | if (err < 0) { | ||
595 | if (err == -EBUSY) | ||
596 | continue; | ||
597 | |||
598 | DRM_DEBUG_KMS("transaction failed: %d\n", err); | ||
599 | return err; | ||
600 | } | ||
601 | |||
602 | if (err < msg->size) | ||
603 | return -EPROTO; | ||
604 | |||
605 | switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { | ||
606 | case DP_AUX_NATIVE_REPLY_ACK: | ||
607 | /* | ||
608 | * For I2C-over-AUX transactions this isn't enough, we | ||
609 | * need to check for the I2C ACK reply. | ||
610 | */ | ||
611 | break; | ||
612 | |||
613 | case DP_AUX_NATIVE_REPLY_NACK: | ||
614 | DRM_DEBUG_KMS("native nack\n"); | ||
615 | return -EREMOTEIO; | ||
616 | |||
617 | case DP_AUX_NATIVE_REPLY_DEFER: | ||
618 | DRM_DEBUG_KMS("native defer"); | ||
619 | /* | ||
620 | * We could check for I2C bit rate capabilities and if | ||
621 | * available adjust this interval. We could also be | ||
622 | * more careful with DP-to-legacy adapters where a | ||
623 | * long legacy cable may force very low I2C bit rates. | ||
624 | * | ||
625 | * For now just defer for long enough to hopefully be | ||
626 | * safe for all use-cases. | ||
627 | */ | ||
628 | usleep_range(500, 600); | ||
629 | continue; | ||
630 | |||
631 | default: | ||
632 | DRM_ERROR("invalid native reply %#04x\n", msg->reply); | ||
633 | return -EREMOTEIO; | ||
634 | } | ||
635 | |||
636 | switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { | ||
637 | case DP_AUX_I2C_REPLY_ACK: | ||
638 | /* | ||
639 | * Both native ACK and I2C ACK replies received. We | ||
640 | * can assume the transfer was successful. | ||
641 | */ | ||
642 | return 0; | ||
643 | |||
644 | case DP_AUX_I2C_REPLY_NACK: | ||
645 | DRM_DEBUG_KMS("I2C nack\n"); | ||
646 | return -EREMOTEIO; | ||
647 | |||
648 | case DP_AUX_I2C_REPLY_DEFER: | ||
649 | DRM_DEBUG_KMS("I2C defer\n"); | ||
650 | usleep_range(400, 500); | ||
651 | continue; | ||
652 | |||
653 | default: | ||
654 | DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); | ||
655 | return -EREMOTEIO; | ||
656 | } | ||
657 | } | ||
658 | |||
659 | DRM_ERROR("too many retries, giving up\n"); | ||
660 | return -EREMOTEIO; | ||
661 | } | ||
662 | |||
663 | static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, | ||
664 | int num) | ||
665 | { | ||
666 | struct drm_dp_aux *aux = adapter->algo_data; | ||
667 | unsigned int i, j; | ||
668 | |||
669 | for (i = 0; i < num; i++) { | ||
670 | struct drm_dp_aux_msg msg; | ||
671 | int err; | ||
672 | |||
673 | /* | ||
674 | * Many hardware implementations support FIFOs larger than a | ||
675 | * single byte, but it has been empirically determined that | ||
676 | * transferring data in larger chunks can actually lead to | ||
677 | * decreased performance. Therefore each message is simply | ||
678 | * transferred byte-by-byte. | ||
679 | */ | ||
680 | for (j = 0; j < msgs[i].len; j++) { | ||
681 | memset(&msg, 0, sizeof(msg)); | ||
682 | msg.address = msgs[i].addr; | ||
683 | |||
684 | msg.request = (msgs[i].flags & I2C_M_RD) ? | ||
685 | DP_AUX_I2C_READ : | ||
686 | DP_AUX_I2C_WRITE; | ||
687 | |||
688 | /* | ||
689 | * All messages except the last one are middle-of- | ||
690 | * transfer messages. | ||
691 | */ | ||
692 | if ((i < num - 1) || (j < msgs[i].len - 1)) | ||
693 | msg.request |= DP_AUX_I2C_MOT; | ||
694 | |||
695 | msg.buffer = msgs[i].buf + j; | ||
696 | msg.size = 1; | ||
697 | |||
698 | err = drm_dp_i2c_do_msg(aux, &msg); | ||
699 | if (err < 0) | ||
700 | return err; | ||
701 | } | ||
702 | } | ||
703 | |||
704 | return num; | ||
705 | } | ||
706 | |||
707 | static const struct i2c_algorithm drm_dp_i2c_algo = { | ||
708 | .functionality = drm_dp_i2c_functionality, | ||
709 | .master_xfer = drm_dp_i2c_xfer, | ||
710 | }; | ||
711 | |||
712 | /** | ||
713 | * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX | ||
714 | * @aux: DisplayPort AUX channel | ||
715 | * | ||
716 | * Returns 0 on success or a negative error code on failure. | ||
717 | */ | ||
718 | int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux) | ||
719 | { | ||
720 | aux->ddc.algo = &drm_dp_i2c_algo; | ||
721 | aux->ddc.algo_data = aux; | ||
722 | aux->ddc.retries = 3; | ||
723 | |||
724 | aux->ddc.class = I2C_CLASS_DDC; | ||
725 | aux->ddc.owner = THIS_MODULE; | ||
726 | aux->ddc.dev.parent = aux->dev; | ||
727 | aux->ddc.dev.of_node = aux->dev->of_node; | ||
728 | |||
729 | strncpy(aux->ddc.name, dev_name(aux->dev), sizeof(aux->ddc.name)); | ||
730 | |||
731 | return i2c_add_adapter(&aux->ddc); | ||
732 | } | ||
733 | EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus); | ||
734 | |||
735 | /** | ||
736 | * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter | ||
737 | * @aux: DisplayPort AUX channel | ||
738 | */ | ||
739 | void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux) | ||
740 | { | ||
741 | i2c_del_adapter(&aux->ddc); | ||
742 | } | ||
743 | EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus); | ||
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 28ab6f4e3b09..b7488c9849ad 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h | |||
@@ -421,6 +421,7 @@ struct drm_dp_aux_msg { | |||
421 | 421 | ||
422 | /** | 422 | /** |
423 | * struct drm_dp_aux - DisplayPort AUX channel | 423 | * struct drm_dp_aux - DisplayPort AUX channel |
424 | * @ddc: I2C adapter that can be used for I2C-over-AUX communication | ||
424 | * @dev: pointer to struct device that is the parent for this AUX channel | 425 | * @dev: pointer to struct device that is the parent for this AUX channel |
425 | * @transfer: transfers a message representing a single AUX transaction | 426 | * @transfer: transfers a message representing a single AUX transaction |
426 | * | 427 | * |
@@ -435,8 +436,16 @@ struct drm_dp_aux_msg { | |||
435 | * propagate errors from the .transfer() function, with the exception of | 436 | * propagate errors from the .transfer() function, with the exception of |
436 | * the -EBUSY error, which causes a transaction to be retried. On a short, | 437 | * the -EBUSY error, which causes a transaction to be retried. On a short, |
437 | * helpers will return -EPROTO to make it simpler to check for failure. | 438 | * helpers will return -EPROTO to make it simpler to check for failure. |
439 | * | ||
440 | * An AUX channel can also be used to transport I2C messages to a sink. A | ||
441 | * typical application of that is to access an EDID that's present in the | ||
442 | * sink device. The .transfer() function can also be used to execute such | ||
443 | * transactions. The drm_dp_aux_register_i2c_bus() function registers an | ||
444 | * I2C adapter that can be passed to drm_probe_ddc(). Upon removal, drivers | ||
445 | * should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter. | ||
438 | */ | 446 | */ |
439 | struct drm_dp_aux { | 447 | struct drm_dp_aux { |
448 | struct i2c_adapter ddc; | ||
440 | struct device *dev; | 449 | struct device *dev; |
441 | 450 | ||
442 | ssize_t (*transfer)(struct drm_dp_aux *aux, | 451 | ssize_t (*transfer)(struct drm_dp_aux *aux, |
@@ -497,4 +506,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link); | |||
497 | int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); | 506 | int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); |
498 | int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); | 507 | int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); |
499 | 508 | ||
509 | int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux); | ||
510 | void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux); | ||
511 | |||
500 | #endif /* _DRM_DP_HELPER_H_ */ | 512 | #endif /* _DRM_DP_HELPER_H_ */ |