diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 161 |
1 files changed, 123 insertions, 38 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index c846ef4acf5b..a8d56ebf06a2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -34,7 +34,8 @@ * for some reason. */ -#include "i915_utils.h" +#include <drm/drm_print.h> + #include "intel_backlight.h" #include "intel_display_core.h" #include "intel_display_types.h" @@ -144,10 +145,10 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) * ranges for such panels. */ if (display->params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL && - !(connector->base.hdr_sink_metadata.hdmi_type1.metadata_type & + !(connector->base.display_info.hdr_sink_metadata.hdmi_type1.metadata_type & BIT(HDMI_STATIC_METADATA_TYPE1))) { drm_info(display->drm, - "[CONNECTOR:%d:%s] Panel is missing HDR static metadata. Possible support for Intel HDR backlight interface is not used. If your backlight controls don't work try booting with i915.enable_dpcd_backlight=%d. needs this, please file a _new_ bug report on drm/i915, see " FDO_BUG_URL " for details.\n", + "[CONNECTOR:%d:%s] Panel is missing HDR static metadata. Possible support for Intel HDR backlight interface is not used. If your backlight controls don't work try booting with i915.enable_dpcd_backlight=%d.\n", connector->base.base.id, connector->base.name, INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL); return false; @@ -224,19 +225,6 @@ intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, connector->base.base.id, connector->base.name); } -static bool -intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state) -{ - struct hdr_output_metadata *hdr_metadata; - - if (!conn_state->hdr_output_metadata) - return false; - - hdr_metadata = conn_state->hdr_output_metadata->data; - - return hdr_metadata->hdmi_metadata_type1.eotf == HDMI_EOTF_SMPTE_ST2084; -} - static void intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level) { @@ -380,6 +368,16 @@ static const char *dpcd_vs_pwm_str(bool aux) return aux ? "DPCD" : "PWM"; } +static const char *backlight_unit_str(struct intel_panel *panel) +{ + if (panel->backlight.edp.vesa.info.luminance_set) + return "NITS"; + else if (panel->backlight.edp.vesa.info.aux_set) + return "Brightness %"; + else + return "PWM"; +} + static void intel_dp_aux_write_panel_luminance_override(struct intel_connector *connector) { @@ -451,7 +449,27 @@ intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pi /* VESA backlight callbacks */ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused) { - return connector->panel.backlight.level; + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + struct intel_panel *panel = &connector->panel; + u8 buf[3]; + u32 val = 0; + int ret; + + if (panel->backlight.edp.vesa.luminance_control_support) { + ret = drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE, buf, + sizeof(buf)); + if (ret < 0) { + drm_err(intel_dp->aux.drm_dev, + "[CONNECTOR:%d:%s] Failed to read Luminance from DPCD\n", + connector->base.base.id, connector->base.name); + return 0; + } + + val |= buf[0] | buf[1] << 8 | buf[2] << 16; + return val / 1000; + } + + return panel->backlight.level; } static void @@ -478,7 +496,8 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, struct intel_panel *panel = &connector->panel; struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); - if (!panel->backlight.edp.vesa.info.aux_enable) { + if (!(panel->backlight.edp.vesa.info.aux_enable || + panel->backlight.edp.vesa.info.luminance_set)) { u32 pwm_level; if (!panel->backlight.edp.vesa.info.aux_set) @@ -502,7 +521,8 @@ static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info); - if (!panel->backlight.edp.vesa.info.aux_enable) + if (!(panel->backlight.edp.vesa.info.aux_enable || + panel->backlight.edp.vesa.info.luminance_set)) panel->backlight.pwm_funcs->disable(old_conn_state, intel_backlight_invert_pwm_level(connector, 0)); } @@ -510,28 +530,36 @@ static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe) { struct intel_display *display = to_intel_display(connector); + struct drm_luminance_range_info *luminance_range = + &connector->base.display_info.luminance_range; struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_panel *panel = &connector->panel; - u16 current_level; + u32 current_level; u8 current_mode; int ret; ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info, - panel->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd, - ¤t_level, ¤t_mode); + luminance_range->max_luminance, + panel->vbt.backlight.pwm_freq_hz, + intel_dp->edp_dpcd, ¤t_level, ¤t_mode, + panel->backlight.edp.vesa.luminance_control_support); if (ret < 0) return ret; drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] AUX VESA backlight enable is controlled through %s\n", connector->base.base.id, connector->base.name, - dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_enable)); + dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_enable || + panel->backlight.edp.vesa.info.luminance_set)); drm_dbg_kms(display->drm, - "[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n", + "[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s using %s values\n", connector->base.base.id, connector->base.name, - dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set)); + dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set || + panel->backlight.edp.vesa.info.luminance_set), + backlight_unit_str(panel)); - if (!panel->backlight.edp.vesa.info.aux_set || !panel->backlight.edp.vesa.info.aux_enable) { + if (!panel->backlight.edp.vesa.info.aux_set || + !panel->backlight.edp.vesa.info.aux_enable) { ret = panel->backlight.pwm_funcs->setup(connector, pipe); if (ret < 0) { drm_err(display->drm, @@ -541,7 +569,17 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, } } - if (panel->backlight.edp.vesa.info.aux_set) { + if (panel->backlight.edp.vesa.info.luminance_set) { + if (luminance_range->max_luminance) { + panel->backlight.max = panel->backlight.edp.vesa.info.max; + panel->backlight.min = luminance_range->min_luminance; + } else { + panel->backlight.max = 512; + panel->backlight.min = 0; + } + panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, 0); + panel->backlight.enabled = panel->backlight.level != 0; + } else if (panel->backlight.edp.vesa.info.aux_set) { panel->backlight.max = panel->backlight.edp.vesa.info.max; panel->backlight.min = 0; if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { @@ -555,7 +593,8 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, panel->backlight.max = panel->backlight.pwm_level_max; panel->backlight.min = panel->backlight.pwm_level_min; if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_PWM) { - panel->backlight.level = panel->backlight.pwm_funcs->get(connector, pipe); + panel->backlight.level = + panel->backlight.pwm_funcs->get(connector, pipe); panel->backlight.enabled = panel->backlight.pwm_enabled; } else { panel->backlight.level = panel->backlight.max; @@ -571,17 +610,62 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, } static bool +check_if_vesa_backlight_possible(struct intel_dp *intel_dp) +{ + int ret; + u8 bit_min, bit_max; + + /* + * Since we only support Fully AUX Based VESA Backlight interface make sure + * backlight enable is possible via AUX along with backlight adjustment + */ + if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP && + intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + return false; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); + if (ret < 0) + return false; + + bit_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_min < 1) + return false; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &bit_max); + if (ret < 0) + return false; + + bit_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_max < bit_min) + return false; + + return true; +} + +static bool intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_panel *panel = &connector->panel; + + if ((intel_dp->edp_dpcd[3] & DP_EDP_PANEL_LUMINANCE_CONTROL_CAPABLE) && + (intel_dp->edp_dpcd[3] & DP_EDP_SMOOTH_BRIGHTNESS_CAPABLE)) { + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] AUX Luminance Based Backlight Control Supported!\n", + connector->base.base.id, connector->base.name); + panel->backlight.edp.vesa.luminance_control_support = true; + return true; + } - if (drm_edp_backlight_supported(intel_dp->edp_dpcd)) { + if (drm_edp_backlight_supported(intel_dp->edp_dpcd) && + check_if_vesa_backlight_possible(intel_dp)) { drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n", connector->base.base.id, connector->base.name); return true; } + return false; } @@ -604,11 +688,13 @@ static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = { int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = connector->base.dev; struct intel_panel *panel = &connector->panel; - bool try_intel_interface = false, try_vesa_interface = false; + bool try_intel_interface = false; - /* Check the VBT and user's module parameters to figure out which + /* + * Check the VBT and user's module parameters to figure out which * interfaces to probe */ switch (display->params.enable_dpcd_backlight) { @@ -617,7 +703,6 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) case INTEL_DP_AUX_BACKLIGHT_AUTO: switch (panel->vbt.backlight.type) { case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE: - try_vesa_interface = true; break; case INTEL_BACKLIGHT_DISPLAY_DDI: try_intel_interface = true; @@ -630,10 +715,6 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE) try_intel_interface = true; - try_vesa_interface = true; - break; - case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA: - try_vesa_interface = true; break; case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: try_intel_interface = true; @@ -652,15 +733,19 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) * panel with Intel's OUI - which is also required for us to be able to detect Intel's * backlight interface at all. This means that the only sensible way for us to detect both * interfaces is to probe for Intel's first, and VESA's second. + * + * Also there is a chance some VBTs may advertise false Intel backlight support even if the + * TCON DPCD says otherwise. This means we keep VESA interface as fallback in that case. */ - if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) { + if (try_intel_interface && intel_dp->edp_dpcd[0] <= DP_EDP_14b && + intel_dp_aux_supports_hdr_backlight(connector)) { drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using Intel proprietary eDP backlight controls\n", connector->base.base.id, connector->base.name); panel->backlight.funcs = &intel_dp_hdr_bl_funcs; return 0; } - if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) { + if (intel_dp_aux_supports_vesa_backlight(connector)) { drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using VESA eDP backlight controls\n", connector->base.base.id, connector->base.name); panel->backlight.funcs = &intel_dp_vesa_bl_funcs; |
