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 | 385 |
1 files changed, 304 insertions, 81 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 4f58efdc688a..271b27c9de51 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -34,17 +34,14 @@ * for some reason. */ -#include "i915_drv.h" +#include <drm/drm_print.h> + #include "intel_backlight.h" +#include "intel_display_core.h" #include "intel_display_types.h" #include "intel_dp.h" #include "intel_dp_aux_backlight.h" -/* TODO: - * Implement HDR, right now we just implement the bare minimum to bring us back into SDR mode so we - * can make people's backlights work in the mean time - */ - /* * DP AUX registers for Intel's proprietary HDR backlight interface. We define * them here since we'll likely be the only driver to ever use these. @@ -69,14 +66,14 @@ #define INTEL_EDP_HDR_GETSET_CTRL_PARAMS 0x344 # define INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE BIT(0) # define INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE BIT(1) -# define INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE BIT(2) /* Pre-TGL+ */ +# define INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE BIT(2) # define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE BIT(3) # define INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE BIT(4) # define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_ENABLE BIT(5) /* Bit 6 is reserved */ -# define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_ENABLE BIT(7) +# define INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX BIT(7) -#define INTEL_EDP_HDR_CONTENT_LUMINANCE 0x346 /* Pre-TGL+ */ +#define INTEL_EDP_HDR_CONTENT_LUMINANCE 0x346 #define INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE 0x34A #define INTEL_EDP_SDR_LUMINANCE_LEVEL 0x352 #define INTEL_EDP_BRIGHTNESS_NITS_LSB 0x354 @@ -114,7 +111,7 @@ static bool is_intel_tcon_cap(const u8 tcon_cap[4]) static bool intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) { - struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); struct drm_dp_aux *aux = &intel_dp->aux; struct intel_panel *panel = &connector->panel; @@ -127,16 +124,17 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) if (ret != sizeof(tcon_cap)) return false; - if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP)) - return false; - - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] Detected %s HDR backlight interface version %d\n", + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Detected %s HDR backlight interface version %d\n", connector->base.base.id, connector->base.name, is_intel_tcon_cap(tcon_cap) ? "Intel" : "unsupported", tcon_cap[0]); if (!is_intel_tcon_cap(tcon_cap)) return false; + if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP)) + return false; + /* * If we don't have HDR static metadata there is no way to * runtime detect used range for nits based control. For now @@ -146,18 +144,28 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) * HDR static metadata we need to start maintaining table of * ranges for such panels. */ - if (i915->display.params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL && + if (display->params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL && !(connector->base.hdr_sink_metadata.hdmi_type1.metadata_type & BIT(HDMI_STATIC_METADATA_TYPE1))) { - drm_info(&i915->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", + 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.\n", connector->base.base.id, connector->base.name, INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL); return false; } - panel->backlight.edp.intel.sdr_uses_aux = + panel->backlight.edp.intel_cap.sdr_uses_aux = tcon_cap[2] & INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP; + panel->backlight.edp.intel_cap.supports_2084_decode = + tcon_cap[1] & INTEL_EDP_HDR_TCON_2084_DECODE_CAP; + panel->backlight.edp.intel_cap.supports_2020_gamut = + tcon_cap[1] & INTEL_EDP_HDR_TCON_2020_GAMUT_CAP; + panel->backlight.edp.intel_cap.supports_segmented_backlight = + tcon_cap[1] & INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_CAP; + panel->backlight.edp.intel_cap.supports_sdp_colorimetry = + tcon_cap[1] & INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_CAP; + panel->backlight.edp.intel_cap.supports_tone_mapping = + tcon_cap[1] & INTEL_EDP_HDR_TCON_TONE_MAPPING_CAP; return true; } @@ -165,20 +173,21 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) static u32 intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe) { - struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_display *display = to_intel_display(connector); struct intel_panel *panel = &connector->panel; struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); u8 tmp; u8 buf[2] = {}; if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) != 1) { - drm_err(&i915->drm, "[CONNECTOR:%d:%s] Failed to read current backlight mode from DPCD\n", + drm_err(display->drm, + "[CONNECTOR:%d:%s] Failed to read current backlight mode from DPCD\n", connector->base.base.id, connector->base.name); return 0; } if (!(tmp & INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE)) { - if (!panel->backlight.edp.intel.sdr_uses_aux) { + if (!panel->backlight.edp.intel_cap.sdr_uses_aux) { u32 pwm_level = panel->backlight.pwm_funcs->get(connector, pipe); return intel_backlight_level_from_pwm(connector, pwm_level); @@ -190,7 +199,8 @@ intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, sizeof(buf)) != sizeof(buf)) { - drm_err(&i915->drm, "[CONNECTOR:%d:%s] Failed to read brightness from DPCD\n", + drm_err(display->drm, + "[CONNECTOR:%d:%s] Failed to read brightness from DPCD\n", connector->base.base.id, connector->base.name); return 0; } @@ -215,13 +225,27 @@ 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) { struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = &connector->panel; - if (panel->backlight.edp.intel.sdr_uses_aux) { + if (intel_dp_in_hdr_mode(conn_state) || + panel->backlight.edp.intel_cap.sdr_uses_aux) { intel_dp_aux_hdr_set_aux_backlight(conn_state, level); } else { const u32 pwm_level = intel_backlight_level_to_pwm(connector, level); @@ -231,13 +255,72 @@ intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 } static void +intel_dp_aux_write_content_luminance(struct intel_connector *connector, + struct hdr_output_metadata *hdr_metadata) +{ + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + int ret; + u8 buf[4]; + + if (!intel_dp_has_gamut_metadata_dip(connector->encoder)) + return; + + buf[0] = hdr_metadata->hdmi_metadata_type1.max_cll & 0xFF; + buf[1] = (hdr_metadata->hdmi_metadata_type1.max_cll & 0xFF00) >> 8; + buf[2] = hdr_metadata->hdmi_metadata_type1.max_fall & 0xFF; + buf[3] = (hdr_metadata->hdmi_metadata_type1.max_fall & 0xFF00) >> 8; + + ret = drm_dp_dpcd_write(&intel_dp->aux, + INTEL_EDP_HDR_CONTENT_LUMINANCE, + buf, sizeof(buf)); + if (ret < 0) + drm_dbg_kms(display->drm, + "Content Luminance DPCD reg write failed, err:-%d\n", + ret); +} + +static void +intel_dp_aux_fill_hdr_tcon_params(const struct drm_connector_state *conn_state, u8 *ctrl) +{ + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_panel *panel = &connector->panel; + struct intel_display *display = to_intel_display(connector); + + /* + * According to spec segmented backlight needs to be set whenever panel is in + * HDR mode. + */ + if (intel_dp_in_hdr_mode(conn_state)) { + *ctrl |= INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE; + *ctrl |= INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE; + } + + if (DISPLAY_VER(display) < 11) + *ctrl &= ~INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE; + + if (panel->backlight.edp.intel_cap.supports_2020_gamut && + (conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_RGB || + conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_YCC || + conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_CYCC)) + *ctrl |= INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE; + + if (panel->backlight.edp.intel_cap.supports_sdp_colorimetry && + intel_dp_has_gamut_metadata_dip(connector->encoder)) + *ctrl |= INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX; + else + *ctrl &= ~INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX; +} + +static void intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state, u32 level) { + struct intel_display *display = to_intel_display(crtc_state); struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = &connector->panel; - struct drm_i915_private *i915 = to_i915(connector->base.dev); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + struct hdr_output_metadata *hdr_metadata; int ret; u8 old_ctrl, ctrl; @@ -245,14 +328,17 @@ intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl); if (ret != 1) { - drm_err(&i915->drm, "[CONNECTOR:%d:%s] Failed to read current backlight control mode: %d\n", + drm_err(display->drm, + "[CONNECTOR:%d:%s] Failed to read current backlight control mode: %d\n", connector->base.base.id, connector->base.name, ret); return; } ctrl = old_ctrl; - if (panel->backlight.edp.intel.sdr_uses_aux) { + if (intel_dp_in_hdr_mode(conn_state) || + panel->backlight.edp.intel_cap.sdr_uses_aux) { ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE; + intel_dp_aux_hdr_set_aux_backlight(conn_state, level); } else { u32 pwm_level = intel_backlight_level_to_pwm(connector, level); @@ -262,10 +348,18 @@ intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE; } + intel_dp_aux_fill_hdr_tcon_params(conn_state, &ctrl); + if (ctrl != old_ctrl && drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) != 1) - drm_err(&i915->drm, "[CONNECTOR:%d:%s] Failed to configure DPCD brightness controls\n", + drm_err(display->drm, + "[CONNECTOR:%d:%s] Failed to configure DPCD brightness controls\n", connector->base.base.id, connector->base.name); + + if (intel_dp_in_hdr_mode(conn_state)) { + hdr_metadata = conn_state->hdr_output_metadata->data; + intel_dp_aux_write_content_luminance(connector, hdr_metadata); + } } static void @@ -275,7 +369,7 @@ intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, struct intel_panel *panel = &connector->panel; /* Nothing to do for AUX based backlight controls */ - if (panel->backlight.edp.intel.sdr_uses_aux) + if (panel->backlight.edp.intel_cap.sdr_uses_aux) return; /* Note we want the actual pwm_level to be 0, regardless of pwm_min */ @@ -287,23 +381,47 @@ static const char *dpcd_vs_pwm_str(bool aux) return aux ? "DPCD" : "PWM"; } +static void +intel_dp_aux_write_panel_luminance_override(struct intel_connector *connector) +{ + struct intel_display *display = to_intel_display(connector); + struct intel_panel *panel = &connector->panel; + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + int ret; + u8 buf[4] = {}; + + buf[0] = panel->backlight.min & 0xFF; + buf[1] = (panel->backlight.min & 0xFF00) >> 8; + buf[2] = panel->backlight.max & 0xFF; + buf[3] = (panel->backlight.max & 0xFF00) >> 8; + + ret = drm_dp_dpcd_write(&intel_dp->aux, + INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE, + buf, sizeof(buf)); + if (ret < 0) + drm_dbg_kms(display->drm, + "Panel Luminance DPCD reg write failed, err:-%d\n", + ret); +} + static int intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe) { - struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_display *display = to_intel_display(connector); struct intel_panel *panel = &connector->panel; struct drm_luminance_range_info *luminance_range = &connector->base.display_info.luminance_range; int ret; - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] SDR backlight is controlled through %s\n", + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] SDR backlight is controlled through %s\n", connector->base.base.id, connector->base.name, - dpcd_vs_pwm_str(panel->backlight.edp.intel.sdr_uses_aux)); + dpcd_vs_pwm_str(panel->backlight.edp.intel_cap.sdr_uses_aux)); - if (!panel->backlight.edp.intel.sdr_uses_aux) { + if (!panel->backlight.edp.intel_cap.sdr_uses_aux) { ret = panel->backlight.pwm_funcs->setup(connector, pipe); if (ret < 0) { - drm_err(&i915->drm, + drm_err(display->drm, "[CONNECTOR:%d:%s] Failed to setup SDR backlight controls through PWM: %d\n", connector->base.base.id, connector->base.name, ret); return ret; @@ -318,11 +436,13 @@ intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pi panel->backlight.min = 0; } - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] Using AUX HDR interface for backlight control (range %d..%d)\n", + intel_dp_aux_write_panel_luminance_override(connector); + + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Using AUX HDR interface for backlight control (range %d..%d)\n", connector->base.base.id, connector->base.name, panel->backlight.min, panel->backlight.max); - panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe); panel->backlight.enabled = panel->backlight.level != 0; @@ -332,9 +452,54 @@ 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) { + 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 connector->panel.backlight.level; } +static int +intel_dp_aux_vesa_set_luminance(struct intel_connector *connector, u32 level) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + u8 buf[3]; + int ret; + + level = level * 1000; + level &= 0xffffff; + buf[0] = (level & 0x0000ff); + buf[1] = (level & 0x00ff00) >> 8; + buf[2] = (level & 0xff0000) >> 16; + + ret = drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE, + buf, sizeof(buf)); + if (ret != sizeof(buf)) { + drm_err(intel_dp->aux.drm_dev, + "%s: Failed to set VESA Aux Luminance: %d\n", + intel_dp->aux.name, ret); + return -EINVAL; + } else { + return 0; + } +} + static void intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level) { @@ -342,6 +507,11 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u3 struct intel_panel *panel = &connector->panel; struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + if (panel->backlight.edp.vesa.luminance_control_support) { + if (!intel_dp_aux_vesa_set_luminance(connector, level)) + return; + } + if (!panel->backlight.edp.vesa.info.aux_set) { const u32 pwm_level = intel_backlight_level_to_pwm(connector, level); @@ -358,6 +528,18 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = &connector->panel; struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + int ret; + + if (panel->backlight.edp.vesa.luminance_control_support) { + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, + DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE); + + if (ret == 1) + return; + + if (!intel_dp_aux_vesa_set_luminance(connector, level)) + return; + } if (!panel->backlight.edp.vesa.info.aux_enable) { u32 pwm_level; @@ -381,6 +563,9 @@ static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state struct intel_panel *panel = &connector->panel; struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); + if (panel->backlight.edp.vesa.luminance_control_support) + return; + drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info); if (!panel->backlight.edp.vesa.info.aux_enable) @@ -390,59 +575,81 @@ 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; - struct drm_i915_private *i915 = dp_to_i915(intel_dp); u16 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); - if (ret < 0) - return ret; - - drm_dbg_kms(&i915->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)); - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n", - connector->base.base.id, connector->base.name, - dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set)); - - 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(&i915->drm, - "[CONNECTOR:%d:%s] Failed to setup PWM backlight controls for eDP backlight: %d\n", - connector->base.base.id, connector->base.name, ret); - return ret; - } - } - - 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) { - panel->backlight.level = current_level; - panel->backlight.enabled = panel->backlight.level != 0; + if (panel->backlight.edp.vesa.luminance_control_support) { + if (luminance_range->max_luminance) { + panel->backlight.max = luminance_range->max_luminance; + panel->backlight.min = luminance_range->min_luminance; } else { - panel->backlight.level = panel->backlight.max; - panel->backlight.enabled = false; + 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; + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] AUX VESA Nits backlight level is controlled through DPCD\n", + connector->base.base.id, connector->base.name); } else { - 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.enabled = panel->backlight.pwm_enabled; + 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); + 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)); + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n", + connector->base.base.id, connector->base.name, + dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set)); + + 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, + "[CONNECTOR:%d:%s] Failed to setup PWM backlight controls for eDP backlight: %d\n", + connector->base.base.id, connector->base.name, ret); + return ret; + } + } + + 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) { + panel->backlight.level = current_level; + panel->backlight.enabled = panel->backlight.level != 0; + } else { + panel->backlight.level = panel->backlight.max; + panel->backlight.enabled = false; + } } else { - panel->backlight.level = panel->backlight.max; - panel->backlight.enabled = false; + 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.enabled = panel->backlight.pwm_enabled; + } else { + panel->backlight.level = panel->backlight.max; + panel->backlight.enabled = false; + } } } - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] Using AUX VESA interface for backlight control\n", + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Using AUX VESA interface for backlight control\n", connector->base.base.id, connector->base.name); return 0; @@ -451,11 +658,22 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, 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 drm_i915_private *i915 = dp_to_i915(intel_dp); + 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)) { - drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n", + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n", connector->base.base.id, connector->base.name); return true; } @@ -480,16 +698,16 @@ 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; - struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); bool try_intel_interface = false, try_vesa_interface = false; /* Check the VBT and user's module parameters to figure out which * interfaces to probe */ - switch (i915->display.params.enable_dpcd_backlight) { + switch (display->params.enable_dpcd_backlight) { case INTEL_DP_AUX_BACKLIGHT_OFF: return -ENODEV; case INTEL_DP_AUX_BACKLIGHT_AUTO: @@ -518,6 +736,10 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) break; } + /* For eDP 1.5 and above we are supposed to use VESA interface for brightness control */ + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + try_vesa_interface = true; + /* * Since Intel has their own backlight control interface, the majority of machines out there * using DPCD backlight controls with Intel GPUs will be using this interface as opposed to @@ -531,7 +753,8 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) * 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. */ - if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) { + if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector) && + intel_dp->edp_dpcd[0] <= DP_EDP_14b) { 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; |