diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2014-09-02 15:04:00 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2014-09-03 06:37:00 -0400 |
commit | beb60608477ec4ae252ec16f9b4018c015b980cb (patch) | |
tree | 9f2e3b961bc43766817cb72c6baccf772e573b6a /drivers/gpu/drm/i915 | |
parent | d410b56d74bc706f414158cb0149e2a149ee1650 (diff) |
drm/i915/dp: Cache EDID for a detection cycle
As we may query the edid multiple times following a detect, record the
EDID found during output discovery and reuse it. This is a separate
issue from caching the output EDID across detection cycles.
v2: Implement connector->force() callback so that edid is associated
with the connector for user overrides as well (Ville)
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r-- | drivers/gpu/drm/i915/intel_dp.c | 156 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 1 |
2 files changed, 90 insertions, 67 deletions
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9421cb4badf9..0a684282991e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c | |||
@@ -3758,9 +3758,9 @@ g4x_dp_detect(struct intel_dp *intel_dp) | |||
3758 | } | 3758 | } |
3759 | 3759 | ||
3760 | static struct edid * | 3760 | static struct edid * |
3761 | intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) | 3761 | intel_dp_get_edid(struct intel_dp *intel_dp) |
3762 | { | 3762 | { |
3763 | struct intel_connector *intel_connector = to_intel_connector(connector); | 3763 | struct intel_connector *intel_connector = intel_dp->attached_connector; |
3764 | 3764 | ||
3765 | /* use cached edid if we have one */ | 3765 | /* use cached edid if we have one */ |
3766 | if (intel_connector->edid) { | 3766 | if (intel_connector->edid) { |
@@ -3769,27 +3769,55 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) | |||
3769 | return NULL; | 3769 | return NULL; |
3770 | 3770 | ||
3771 | return drm_edid_duplicate(intel_connector->edid); | 3771 | return drm_edid_duplicate(intel_connector->edid); |
3772 | } | 3772 | } else |
3773 | return drm_get_edid(&intel_connector->base, | ||
3774 | &intel_dp->aux.ddc); | ||
3775 | } | ||
3773 | 3776 | ||
3774 | return drm_get_edid(connector, adapter); | 3777 | static void |
3778 | intel_dp_set_edid(struct intel_dp *intel_dp) | ||
3779 | { | ||
3780 | struct intel_connector *intel_connector = intel_dp->attached_connector; | ||
3781 | struct edid *edid; | ||
3782 | |||
3783 | edid = intel_dp_get_edid(intel_dp); | ||
3784 | intel_connector->detect_edid = edid; | ||
3785 | |||
3786 | if (intel_dp->force_audio != HDMI_AUDIO_AUTO) | ||
3787 | intel_dp->has_audio = intel_dp->force_audio == HDMI_AUDIO_ON; | ||
3788 | else | ||
3789 | intel_dp->has_audio = drm_detect_monitor_audio(edid); | ||
3775 | } | 3790 | } |
3776 | 3791 | ||
3777 | static int | 3792 | static void |
3778 | intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter) | 3793 | intel_dp_unset_edid(struct intel_dp *intel_dp) |
3779 | { | 3794 | { |
3780 | struct intel_connector *intel_connector = to_intel_connector(connector); | 3795 | struct intel_connector *intel_connector = intel_dp->attached_connector; |
3781 | 3796 | ||
3782 | /* use cached edid if we have one */ | 3797 | kfree(intel_connector->detect_edid); |
3783 | if (intel_connector->edid) { | 3798 | intel_connector->detect_edid = NULL; |
3784 | /* invalid edid */ | ||
3785 | if (IS_ERR(intel_connector->edid)) | ||
3786 | return 0; | ||
3787 | 3799 | ||
3788 | return intel_connector_update_modes(connector, | 3800 | intel_dp->has_audio = false; |
3789 | intel_connector->edid); | 3801 | } |
3790 | } | ||
3791 | 3802 | ||
3792 | return intel_ddc_get_modes(connector, adapter); | 3803 | static enum intel_display_power_domain |
3804 | intel_dp_power_get(struct intel_dp *dp) | ||
3805 | { | ||
3806 | struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; | ||
3807 | enum intel_display_power_domain power_domain; | ||
3808 | |||
3809 | power_domain = intel_display_port_power_domain(encoder); | ||
3810 | intel_display_power_get(to_i915(encoder->base.dev), power_domain); | ||
3811 | |||
3812 | return power_domain; | ||
3813 | } | ||
3814 | |||
3815 | static void | ||
3816 | intel_dp_power_put(struct intel_dp *dp, | ||
3817 | enum intel_display_power_domain power_domain) | ||
3818 | { | ||
3819 | struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; | ||
3820 | intel_display_power_put(to_i915(encoder->base.dev), power_domain); | ||
3793 | } | 3821 | } |
3794 | 3822 | ||
3795 | static enum drm_connector_status | 3823 | static enum drm_connector_status |
@@ -3799,27 +3827,22 @@ intel_dp_detect(struct drm_connector *connector, bool force) | |||
3799 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); | 3827 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); |
3800 | struct intel_encoder *intel_encoder = &intel_dig_port->base; | 3828 | struct intel_encoder *intel_encoder = &intel_dig_port->base; |
3801 | struct drm_device *dev = connector->dev; | 3829 | struct drm_device *dev = connector->dev; |
3802 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
3803 | enum drm_connector_status status; | 3830 | enum drm_connector_status status; |
3804 | enum intel_display_power_domain power_domain; | 3831 | enum intel_display_power_domain power_domain; |
3805 | struct edid *edid = NULL; | ||
3806 | bool ret; | 3832 | bool ret; |
3807 | 3833 | ||
3808 | power_domain = intel_display_port_power_domain(intel_encoder); | ||
3809 | intel_display_power_get(dev_priv, power_domain); | ||
3810 | |||
3811 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", | 3834 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
3812 | connector->base.id, connector->name); | 3835 | connector->base.id, connector->name); |
3836 | intel_dp_unset_edid(intel_dp); | ||
3813 | 3837 | ||
3814 | if (intel_dp->is_mst) { | 3838 | if (intel_dp->is_mst) { |
3815 | /* MST devices are disconnected from a monitor POV */ | 3839 | /* MST devices are disconnected from a monitor POV */ |
3816 | if (intel_encoder->type != INTEL_OUTPUT_EDP) | 3840 | if (intel_encoder->type != INTEL_OUTPUT_EDP) |
3817 | intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; | 3841 | intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; |
3818 | status = connector_status_disconnected; | 3842 | return connector_status_disconnected; |
3819 | goto out; | ||
3820 | } | 3843 | } |
3821 | 3844 | ||
3822 | intel_dp->has_audio = false; | 3845 | power_domain = intel_dp_power_get(intel_dp); |
3823 | 3846 | ||
3824 | /* Can't disconnect eDP, but you can close the lid... */ | 3847 | /* Can't disconnect eDP, but you can close the lid... */ |
3825 | if (is_edp(intel_dp)) | 3848 | if (is_edp(intel_dp)) |
@@ -3843,82 +3866,78 @@ intel_dp_detect(struct drm_connector *connector, bool force) | |||
3843 | goto out; | 3866 | goto out; |
3844 | } | 3867 | } |
3845 | 3868 | ||
3846 | if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { | 3869 | intel_dp_set_edid(intel_dp); |
3847 | intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); | ||
3848 | } else { | ||
3849 | edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); | ||
3850 | if (edid) { | ||
3851 | intel_dp->has_audio = drm_detect_monitor_audio(edid); | ||
3852 | kfree(edid); | ||
3853 | } | ||
3854 | } | ||
3855 | 3870 | ||
3856 | if (intel_encoder->type != INTEL_OUTPUT_EDP) | 3871 | if (intel_encoder->type != INTEL_OUTPUT_EDP) |
3857 | intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; | 3872 | intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; |
3858 | status = connector_status_connected; | 3873 | status = connector_status_connected; |
3859 | 3874 | ||
3860 | out: | 3875 | out: |
3861 | intel_display_power_put(dev_priv, power_domain); | 3876 | intel_dp_power_put(intel_dp, power_domain); |
3862 | return status; | 3877 | return status; |
3863 | } | 3878 | } |
3864 | 3879 | ||
3865 | static int intel_dp_get_modes(struct drm_connector *connector) | 3880 | static void |
3881 | intel_dp_force(struct drm_connector *connector) | ||
3866 | { | 3882 | { |
3867 | struct intel_dp *intel_dp = intel_attached_dp(connector); | 3883 | struct intel_dp *intel_dp = intel_attached_dp(connector); |
3868 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); | 3884 | struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; |
3869 | struct intel_encoder *intel_encoder = &intel_dig_port->base; | ||
3870 | struct intel_connector *intel_connector = to_intel_connector(connector); | ||
3871 | struct drm_device *dev = connector->dev; | ||
3872 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
3873 | enum intel_display_power_domain power_domain; | 3885 | enum intel_display_power_domain power_domain; |
3874 | int ret; | ||
3875 | 3886 | ||
3876 | /* We should parse the EDID data and find out if it has an audio sink | 3887 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
3877 | */ | 3888 | connector->base.id, connector->name); |
3889 | intel_dp_unset_edid(intel_dp); | ||
3878 | 3890 | ||
3879 | power_domain = intel_display_port_power_domain(intel_encoder); | 3891 | if (connector->status != connector_status_connected) |
3880 | intel_display_power_get(dev_priv, power_domain); | 3892 | return; |
3881 | 3893 | ||
3882 | ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc); | 3894 | power_domain = intel_dp_power_get(intel_dp); |
3883 | intel_display_power_put(dev_priv, power_domain); | 3895 | |
3884 | if (ret) | 3896 | intel_dp_set_edid(intel_dp); |
3885 | return ret; | 3897 | |
3898 | intel_dp_power_put(intel_dp, power_domain); | ||
3899 | |||
3900 | if (intel_encoder->type != INTEL_OUTPUT_EDP) | ||
3901 | intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; | ||
3902 | } | ||
3903 | |||
3904 | static int intel_dp_get_modes(struct drm_connector *connector) | ||
3905 | { | ||
3906 | struct intel_connector *intel_connector = to_intel_connector(connector); | ||
3907 | struct edid *edid; | ||
3908 | |||
3909 | edid = intel_connector->detect_edid; | ||
3910 | if (edid) { | ||
3911 | int ret = intel_connector_update_modes(connector, edid); | ||
3912 | if (ret) | ||
3913 | return ret; | ||
3914 | } | ||
3886 | 3915 | ||
3887 | /* if eDP has no EDID, fall back to fixed mode */ | 3916 | /* if eDP has no EDID, fall back to fixed mode */ |
3888 | if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { | 3917 | if (is_edp(intel_attached_dp(connector)) && |
3918 | intel_connector->panel.fixed_mode) { | ||
3889 | struct drm_display_mode *mode; | 3919 | struct drm_display_mode *mode; |
3890 | mode = drm_mode_duplicate(dev, | 3920 | |
3921 | mode = drm_mode_duplicate(connector->dev, | ||
3891 | intel_connector->panel.fixed_mode); | 3922 | intel_connector->panel.fixed_mode); |
3892 | if (mode) { | 3923 | if (mode) { |
3893 | drm_mode_probed_add(connector, mode); | 3924 | drm_mode_probed_add(connector, mode); |
3894 | return 1; | 3925 | return 1; |
3895 | } | 3926 | } |
3896 | } | 3927 | } |
3928 | |||
3897 | return 0; | 3929 | return 0; |
3898 | } | 3930 | } |
3899 | 3931 | ||
3900 | static bool | 3932 | static bool |
3901 | intel_dp_detect_audio(struct drm_connector *connector) | 3933 | intel_dp_detect_audio(struct drm_connector *connector) |
3902 | { | 3934 | { |
3903 | struct intel_dp *intel_dp = intel_attached_dp(connector); | ||
3904 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); | ||
3905 | struct intel_encoder *intel_encoder = &intel_dig_port->base; | ||
3906 | struct drm_device *dev = connector->dev; | ||
3907 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
3908 | enum intel_display_power_domain power_domain; | ||
3909 | struct edid *edid; | ||
3910 | bool has_audio = false; | 3935 | bool has_audio = false; |
3936 | struct edid *edid; | ||
3911 | 3937 | ||
3912 | power_domain = intel_display_port_power_domain(intel_encoder); | 3938 | edid = to_intel_connector(connector)->detect_edid; |
3913 | intel_display_power_get(dev_priv, power_domain); | 3939 | if (edid) |
3914 | |||
3915 | edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); | ||
3916 | if (edid) { | ||
3917 | has_audio = drm_detect_monitor_audio(edid); | 3940 | has_audio = drm_detect_monitor_audio(edid); |
3918 | kfree(edid); | ||
3919 | } | ||
3920 | |||
3921 | intel_display_power_put(dev_priv, power_domain); | ||
3922 | 3941 | ||
3923 | return has_audio; | 3942 | return has_audio; |
3924 | } | 3943 | } |
@@ -4016,6 +4035,8 @@ intel_dp_connector_destroy(struct drm_connector *connector) | |||
4016 | { | 4035 | { |
4017 | struct intel_connector *intel_connector = to_intel_connector(connector); | 4036 | struct intel_connector *intel_connector = to_intel_connector(connector); |
4018 | 4037 | ||
4038 | intel_dp_unset_edid(intel_attached_dp(connector)); | ||
4039 | |||
4019 | if (!IS_ERR_OR_NULL(intel_connector->edid)) | 4040 | if (!IS_ERR_OR_NULL(intel_connector->edid)) |
4020 | kfree(intel_connector->edid); | 4041 | kfree(intel_connector->edid); |
4021 | 4042 | ||
@@ -4068,6 +4089,7 @@ static void intel_dp_encoder_reset(struct drm_encoder *encoder) | |||
4068 | static const struct drm_connector_funcs intel_dp_connector_funcs = { | 4089 | static const struct drm_connector_funcs intel_dp_connector_funcs = { |
4069 | .dpms = intel_connector_dpms, | 4090 | .dpms = intel_connector_dpms, |
4070 | .detect = intel_dp_detect, | 4091 | .detect = intel_dp_detect, |
4092 | .force = intel_dp_force, | ||
4071 | .fill_modes = drm_helper_probe_single_connector_modes, | 4093 | .fill_modes = drm_helper_probe_single_connector_modes, |
4072 | .set_property = intel_dp_set_property, | 4094 | .set_property = intel_dp_set_property, |
4073 | .destroy = intel_dp_connector_destroy, | 4095 | .destroy = intel_dp_connector_destroy, |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9570d688b5d8..d727d20fc512 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
@@ -214,6 +214,7 @@ struct intel_connector { | |||
214 | 214 | ||
215 | /* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */ | 215 | /* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */ |
216 | struct edid *edid; | 216 | struct edid *edid; |
217 | struct edid *detect_edid; | ||
217 | 218 | ||
218 | /* since POLL and HPD connectors may use the same HPD line keep the native | 219 | /* since POLL and HPD connectors may use the same HPD line keep the native |
219 | state of connector->polled in case hotplug storm detection changes it */ | 220 | state of connector->polled in case hotplug storm detection changes it */ |