From 241a9e233e7cf1001d09426c9fc520768f8961bf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 8 Jun 2021 16:22:11 +0200 Subject: [PATCH 0001/1318] drm/bridge: ti-sn65dsi83: Fix sparse warnings Fix the following sparse warnings generated by "make C=1": drivers/gpu/drm/bridge/ti-sn65dsi83.c:429:13: warning: incorrect type in assignment (different base types) drivers/gpu/drm/bridge/ti-sn65dsi83.c:429:13: expected unsigned short [assigned] [usertype] val drivers/gpu/drm/bridge/ti-sn65dsi83.c:429:13: got restricted __le16 [usertype] drivers/gpu/drm/bridge/ti-sn65dsi83.c:432:13: warning: incorrect type in assignment (different base types) drivers/gpu/drm/bridge/ti-sn65dsi83.c:432:13: expected unsigned short [addressable] [assigned] [usertype] val drivers/gpu/drm/bridge/ti-sn65dsi83.c:432:13: got restricted __le16 [usertype] drivers/gpu/drm/bridge/ti-sn65dsi83.c:436:13: warning: incorrect type in assignment (different base types) drivers/gpu/drm/bridge/ti-sn65dsi83.c:436:13: expected unsigned short [addressable] [assigned] [usertype] val drivers/gpu/drm/bridge/ti-sn65dsi83.c:436:13: got restricted __le16 [usertype] drivers/gpu/drm/bridge/ti-sn65dsi83.c:438:13: warning: incorrect type in assignment (different base types) drivers/gpu/drm/bridge/ti-sn65dsi83.c:438:13: expected unsigned short [addressable] [assigned] [usertype] val drivers/gpu/drm/bridge/ti-sn65dsi83.c:438:13: got restricted __le16 [usertype] drivers/gpu/drm/bridge/ti-sn65dsi83.c:441:13: warning: incorrect type in assignment (different base types) drivers/gpu/drm/bridge/ti-sn65dsi83.c:441:13: expected unsigned short [addressable] [assigned] [usertype] val drivers/gpu/drm/bridge/ti-sn65dsi83.c:441:13: got restricted __le16 [usertype] Signed-off-by: Marek Vasut Cc: Adam Ford Cc: Douglas Anderson Cc: Frieder Schrempf Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Loic Poulain Cc: Marek Vasut Cc: Philippe Schenker Cc: Robert Foss Cc: Sam Ravnborg Cc: Stephen Boyd Cc: Valentin Raevsky To: dri-devel@lists.freedesktop.org Signed-off-by: Robert Foss Reviewed-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20210608142211.82333-1-marex@denx.de --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 750f2172ef08..8f3158f5281a 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -368,6 +368,7 @@ static void sn65dsi83_enable(struct drm_bridge *bridge) { struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); unsigned int pval; + __le16 le16val; u16 val; int ret; @@ -426,21 +427,21 @@ static void sn65dsi83_enable(struct drm_bridge *bridge) REG_LVDS_LANE_CHB_LVDS_TERM); regmap_write(ctx->regmap, REG_LVDS_CM, 0x00); - val = cpu_to_le16(ctx->mode.hdisplay); + le16val = cpu_to_le16(ctx->mode.hdisplay); regmap_bulk_write(ctx->regmap, REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW, - &val, 2); - val = cpu_to_le16(ctx->mode.vdisplay); + &le16val, 2); + le16val = cpu_to_le16(ctx->mode.vdisplay); regmap_bulk_write(ctx->regmap, REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW, - &val, 2); + &le16val, 2); /* 32 + 1 pixel clock to ensure proper operation */ - val = cpu_to_le16(32 + 1); - regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &val, 2); - val = cpu_to_le16(ctx->mode.hsync_end - ctx->mode.hsync_start); + le16val = cpu_to_le16(32 + 1); + regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2); + le16val = cpu_to_le16(ctx->mode.hsync_end - ctx->mode.hsync_start); regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW, - &val, 2); - val = cpu_to_le16(ctx->mode.vsync_end - ctx->mode.vsync_start); + &le16val, 2); + le16val = cpu_to_le16(ctx->mode.vsync_end - ctx->mode.vsync_start); regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW, - &val, 2); + &le16val, 2); regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH, ctx->mode.htotal - ctx->mode.hsync_end); regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH, From 4154fa0e2688118ba3dbc67aa834435463f9ea68 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:14:55 -0400 Subject: [PATCH 0002/1318] drm/i915/dpcd_bl: Remove redundant AUX backlight frequency calculations Noticed this while moving all of the VESA backlight code in i915 over to DRM helpers: it would appear that we calculate the frequency value we want to write to DP_EDP_BACKLIGHT_FREQ_SET twice even though this value never actually changes during runtime. So, let's simplify things by just caching this value in intel_panel.backlight, and re-writing it as-needed. Changes since v1: * Wrap panel->backlight.edp.vesa.pwm_freq_pre_divider in DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP check - Jani Signed-off-by: Lyude Paul Cc: Jani Nikula Cc: Dave Airlie Cc: greg.depoire@gmail.com Reviewed-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-2-lyude@redhat.com --- .../drm/i915/display/intel_display_types.h | 1 + .../drm/i915/display/intel_dp_aux_backlight.c | 65 ++++++------------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 9c0adfc60c6f..7054a37363fb 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -311,6 +311,7 @@ struct intel_panel { union { struct { u8 pwmgen_bit_count; + u8 pwm_freq_pre_divider; } vesa; struct { bool sdr_uses_aux; 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 8e9ac9ba1d38..68bfe50ada59 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -373,50 +373,6 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, } } -/* - * Set PWM Frequency divider to match desired frequency in vbt. - * The PWM Frequency is calculated as 27Mhz / (F x P). - * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the - * EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h) - * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the - * EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h) - */ -static bool intel_dp_aux_vesa_set_pwm_freq(struct intel_connector *connector) -{ - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_dp *intel_dp = intel_attached_dp(connector); - const u8 pn = connector->panel.backlight.edp.vesa.pwmgen_bit_count; - int freq, fxp, f, fxp_actual, fxp_min, fxp_max; - - freq = dev_priv->vbt.backlight.pwm_freq_hz; - if (!freq) { - drm_dbg_kms(&dev_priv->drm, - "Use panel default backlight frequency\n"); - return false; - } - - fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq); - f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255); - fxp_actual = f << pn; - - /* Ensure frequency is within 25% of desired value */ - fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); - fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); - - if (fxp_min > fxp_actual || fxp_actual > fxp_max) { - drm_dbg_kms(&dev_priv->drm, "Actual frequency out of range\n"); - return false; - } - - if (drm_dp_dpcd_writeb(&intel_dp->aux, - DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) { - drm_dbg_kms(&dev_priv->drm, - "Failed to write aux backlight freq\n"); - return false; - } - return true; -} - static void intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state, u32 level) @@ -459,9 +415,13 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, break; } - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) - if (intel_dp_aux_vesa_set_pwm_freq(connector)) + if (panel->backlight.edp.vesa.pwm_freq_pre_divider) { + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_FREQ_SET, + panel->backlight.edp.vesa.pwm_freq_pre_divider) == 1) new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE; + else + drm_dbg_kms(&i915->drm, "Failed to write aux backlight frequency\n"); + } if (new_dpcd_buf != dpcd_buf) { if (drm_dp_dpcd_writeb(&intel_dp->aux, @@ -482,6 +442,14 @@ static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state false); } +/* + * Compute PWM frequency divider value based off the frequency provided to us by the vbt. + * The PWM Frequency is calculated as 27Mhz / (F x P). + * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the + * EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h) + * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the + * EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h) + */ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connector) { struct drm_i915_private *i915 = to_i915(connector->base.dev); @@ -533,8 +501,10 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + /* Ensure frequency is within 25% of desired value */ fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); + if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) { drm_dbg_kms(&i915->drm, "VBT defined backlight frequency out of range\n"); @@ -555,7 +525,10 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto "Failed to write aux pwmgen bit count\n"); return max_backlight; } + panel->backlight.edp.vesa.pwmgen_bit_count = pn; + if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) + panel->backlight.edp.vesa.pwm_freq_pre_divider = f; max_backlight = (1 << pn) - 1; From 3faea9939a3d1eb5a40f3f6100c24792865b6445 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:14:56 -0400 Subject: [PATCH 0003/1318] drm/i915/dpcd_bl: Handle drm_dpcd_read/write() return values correctly This is kind of an annoying aspect of DRM's DP helpers: drm_dp_dpcd_readb/writeb() return the size of bytes read/written on success, thus we want to check against that instead of checking if the return value is less than 0. I'll probably be fixing this in the near future once I start doing DP work again, also because I'd rather not mix a tree-wide refactor like that in with a patch series intended to be around introducing DP backlight helpers. So, for now let's just handle the return values from each function correctly. Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-3-lyude@redhat.com --- .../drm/i915/display/intel_dp_aux_backlight.c | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 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 68bfe50ada59..1dbe38282ebe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -107,7 +107,7 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) u8 tcon_cap[4]; ret = drm_dp_dpcd_read(aux, INTEL_EDP_HDR_TCON_CAP0, tcon_cap, sizeof(tcon_cap)); - if (ret < 0) + if (ret != sizeof(tcon_cap)) return false; if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP)) @@ -137,7 +137,7 @@ intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe u8 tmp; u8 buf[2] = { 0 }; - if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) < 0) { + if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) != 1) { drm_err(&i915->drm, "Failed to read current backlight mode from DPCD\n"); return 0; } @@ -153,7 +153,8 @@ intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe return panel->backlight.max; } - if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, sizeof(buf)) < 0) { + if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, + sizeof(buf)) != sizeof(buf)) { drm_err(&i915->drm, "Failed to read brightness from DPCD\n"); return 0; } @@ -172,7 +173,8 @@ intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, buf[0] = level & 0xFF; buf[1] = (level & 0xFF00) >> 8; - if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, 4) < 0) + if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, + sizeof(buf)) != sizeof(buf)) drm_err(dev, "Failed to write brightness level to DPCD\n"); } @@ -203,7 +205,7 @@ intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, u8 old_ctrl, ctrl; ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl); - if (ret < 0) { + if (ret != 1) { drm_err(&i915->drm, "Failed to read current backlight control mode: %d\n", ret); return; } @@ -221,7 +223,7 @@ intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, } if (ctrl != old_ctrl) - if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) < 0) + if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) != 1) drm_err(&i915->drm, "Failed to configure DPCD brightness controls\n"); } @@ -277,8 +279,7 @@ static void set_vesa_backlight_enable(struct intel_dp *intel_dp, bool enable) if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)) return; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, - ®_val) < 0) { + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) != 1) { drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", DP_EDP_DISPLAY_CONTROL_REGISTER); return; @@ -332,8 +333,8 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en if (!intel_dp_aux_vesa_backlight_dpcd_mode(connector)) return connector->panel.backlight.max; - if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, - &read_val, sizeof(read_val)) < 0) { + if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, &read_val, + sizeof(read_val)) != sizeof(read_val)) { drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", DP_EDP_BACKLIGHT_BRIGHTNESS_MSB); return 0; @@ -365,8 +366,8 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, vals[0] = (level & 0xFF00) >> 8; vals[1] = (level & 0xFF); } - if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, - vals, sizeof(vals)) < 0) { + if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals, + sizeof(vals)) != sizeof(vals)) { drm_dbg_kms(&i915->drm, "Failed to write aux backlight level\n"); return; @@ -401,9 +402,8 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; - if (drm_dp_dpcd_writeb(&intel_dp->aux, - DP_EDP_PWMGEN_BIT_COUNT, - pwmgen_bit_count) < 0) + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, + pwmgen_bit_count) != 1) drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count\n"); @@ -424,11 +424,9 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, } if (new_dpcd_buf != dpcd_buf) { - if (drm_dp_dpcd_writeb(&intel_dp->aux, - DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) { - drm_dbg_kms(&i915->drm, - "Failed to write aux backlight mode\n"); - } + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, + new_dpcd_buf) != 1) + drm_dbg_kms(&i915->drm, "Failed to write aux backlight mode\n"); } intel_dp_aux_vesa_set_backlight(conn_state, level); @@ -519,8 +517,7 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto } drm_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn); - if (drm_dp_dpcd_writeb(&intel_dp->aux, - DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) { + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pn) != 1) { drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count\n"); return max_backlight; From 46e745a8edc8c2a2fe4fdc491d7e39598e104441 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:14:57 -0400 Subject: [PATCH 0004/1318] drm/i915/dpcd_bl: Cleanup intel_dp_aux_vesa_enable_backlight() a bit Get rid of the extraneous switch case in here, and just open code edp_backlight_mode as we only ever use it once. v4: * Check that backlight mode is DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD, not DP_EDP_BACKLIGHT_CONTROL_MODE_MASK - imirkin Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-4-lyude@redhat.com --- .../gpu/drm/i915/display/intel_dp_aux_backlight.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 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 1dbe38282ebe..1ab82933afb5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -382,7 +382,7 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, 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; - u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode; + u8 dpcd_buf, new_dpcd_buf; u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count; if (drm_dp_dpcd_readb(&intel_dp->aux, @@ -393,12 +393,8 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, } new_dpcd_buf = dpcd_buf; - edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; - switch (edp_backlight_mode) { - case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM: - case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET: - case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT: + if ((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) != DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; @@ -406,13 +402,6 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, pwmgen_bit_count) != 1) drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count\n"); - - break; - - /* Do nothing when it is already DPCD mode */ - case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD: - default: - break; } if (panel->backlight.edp.vesa.pwm_freq_pre_divider) { From 3b51c2bb6f5f3f8a88fe9f4ab62e38496b2d200b Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:14:58 -0400 Subject: [PATCH 0005/1318] drm/i915/dpcd_bl: Cache some backlight capabilities in intel_panel.backlight Since we're about to be moving this code into shared DRM helpers, we might as well start to cache certain backlight capabilities that can be determined from the EDP DPCD, and are likely to be relevant to the majority of drivers using said helpers. The main purpose of this is just to prevent every driver from having to check everything against the eDP DPCD using DP macros, which makes the code slightly easier to read (especially since the names of some of the eDP capabilities don't exactly match up with what we actually need to use them for, like DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT for instance). Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-5-lyude@redhat.com --- .../drm/i915/display/intel_display_types.h | 2 ++ .../drm/i915/display/intel_dp_aux_backlight.c | 29 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 7054a37363fb..d4a57bce71b1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -312,6 +312,8 @@ struct intel_panel { struct { u8 pwmgen_bit_count; u8 pwm_freq_pre_divider; + bool lsb_reg_used; + bool aux_enable; } vesa; struct { bool sdr_uses_aux; 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 1ab82933afb5..1d31c33f4867 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -270,13 +270,14 @@ intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pi } /* VESA backlight callbacks */ -static void set_vesa_backlight_enable(struct intel_dp *intel_dp, bool enable) +static void set_vesa_backlight_enable(struct intel_connector *connector, bool enable) { + struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); u8 reg_val = 0; /* Early return when display use other mechanism to enable backlight. */ - if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)) + if (!connector->panel.backlight.edp.vesa.aux_enable) return; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) != 1) { @@ -339,9 +340,11 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en DP_EDP_BACKLIGHT_BRIGHTNESS_MSB); return 0; } - level = read_val[0]; - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) + + if (connector->panel.backlight.edp.vesa.lsb_reg_used) level = (read_val[0] << 8 | read_val[1]); + else + level = read_val[0]; return level; } @@ -359,13 +362,14 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, struct drm_i915_private *i915 = dp_to_i915(intel_dp); u8 vals[2] = { 0x0 }; - vals[0] = level; - /* Write the MSB and/or LSB */ - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) { + if (connector->panel.backlight.edp.vesa.lsb_reg_used) { vals[0] = (level & 0xFF00) >> 8; vals[1] = (level & 0xFF); + } else { + vals[0] = level; } + if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals, sizeof(vals)) != sizeof(vals)) { drm_dbg_kms(&i915->drm, @@ -419,14 +423,13 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, } intel_dp_aux_vesa_set_backlight(conn_state, level); - set_vesa_backlight_enable(intel_dp, true); + set_vesa_backlight_enable(connector, true); } static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level) { - set_vesa_backlight_enable(enc_to_intel_dp(to_intel_encoder(old_conn_state->best_encoder)), - false); + set_vesa_backlight_enable(to_intel_connector(old_conn_state->connector), false); } /* @@ -524,8 +527,14 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe) { + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_panel *panel = &connector->panel; + if (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) + panel->backlight.edp.vesa.aux_enable = true; + if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) + panel->backlight.edp.vesa.lsb_reg_used = true; + panel->backlight.max = intel_dp_aux_vesa_calc_max_backlight(connector); if (!panel->backlight.max) return -ENODEV; From ade673bb2ba496a66250c5c2006c502c3d734ea2 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:14:59 -0400 Subject: [PATCH 0006/1318] drm/i915/dpcd_bl: Move VESA backlight enabling code closer together No functional changes, just move set_vesa_backlight_enable() closer to it's only caller: intel_dp_aux_vesa_enable_backlight(). Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-6-lyude@redhat.com --- .../drm/i915/display/intel_dp_aux_backlight.c | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 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 1d31c33f4867..781c7cd98d75 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -270,33 +270,6 @@ intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pi } /* VESA backlight callbacks */ -static void set_vesa_backlight_enable(struct intel_connector *connector, bool enable) -{ - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - u8 reg_val = 0; - - /* Early return when display use other mechanism to enable backlight. */ - if (!connector->panel.backlight.edp.vesa.aux_enable) - return; - - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) != 1) { - drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", - DP_EDP_DISPLAY_CONTROL_REGISTER); - return; - } - if (enable) - reg_val |= DP_EDP_BACKLIGHT_ENABLE; - else - reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); - - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, - reg_val) != 1) { - drm_dbg_kms(&i915->drm, "Failed to %s aux backlight\n", - enabledisable(enable)); - } -} - static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); @@ -378,6 +351,33 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, } } +static void set_vesa_backlight_enable(struct intel_connector *connector, bool enable) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + u8 reg_val = 0; + + /* Early return when display use other mechanism to enable backlight. */ + if (!connector->panel.backlight.edp.vesa.aux_enable) + return; + + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) != 1) { + drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", + DP_EDP_DISPLAY_CONTROL_REGISTER); + return; + } + if (enable) + reg_val |= DP_EDP_BACKLIGHT_ENABLE; + else + reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); + + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, + reg_val) != 1) { + drm_dbg_kms(&i915->drm, "Failed to %s aux backlight\n", + enabledisable(enable)); + } +} + static void intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state, u32 level) From 17917ff62443414569ea76e151e7a7ef44812264 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:15:00 -0400 Subject: [PATCH 0007/1318] drm/i915/dpcd_bl: Return early in vesa_calc_max_backlight if we can't read PWMGEN_BIT_COUNT If we can't read DP_EDP_PWMGEN_BIT_COUNT in intel_dp_aux_vesa_calc_max_backlight() but do have a valid PWM frequency defined in the VBT, we'll keep going in the function until we inevitably fail on reading DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN. There's not much point in doing this, so just return early. Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-7-lyude@redhat.com --- drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 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 781c7cd98d75..bf8e4ed56847 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -449,11 +449,14 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1; u8 pn, pn_min, pn_max; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn) == 1) { - pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - max_backlight = (1 << pn) - 1; + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn) != 1) { + drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap\n"); + return 0; } + pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + max_backlight = (1 << pn) - 1; + /* Find desired value of (F x P) * Note that, if F x P is out of supported range, the maximum value or * minimum value will applied automatically. So no need to check that. From 837f93247634b12e5eebb3d707405b701a4f3479 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:15:01 -0400 Subject: [PATCH 0008/1318] drm/i915/dpcd_bl: Print return codes for VESA backlight failures Also, stop printing the DPCD register that failed, and just describe it instead. Saves us from having to look up each register offset when reading through kernel logs (plus, DPCD dumping with drm.debug |= 0x100 will give us that anyway). Signed-off-by: Lyude Paul Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-8-lyude@redhat.com --- .../drm/i915/display/intel_dp_aux_backlight.c | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 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 bf8e4ed56847..95f2df631052 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -274,14 +274,12 @@ static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connec { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int ret; u8 mode_reg; - if (drm_dp_dpcd_readb(&intel_dp->aux, - DP_EDP_BACKLIGHT_MODE_SET_REGISTER, - &mode_reg) != 1) { - drm_dbg_kms(&i915->drm, - "Failed to read the DPCD register 0x%x\n", - DP_EDP_BACKLIGHT_MODE_SET_REGISTER); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read backlight mode: %d\n", ret); return false; } @@ -297,6 +295,7 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int ret; u8 read_val[2] = { 0x0 }; u16 level = 0; @@ -307,10 +306,10 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en if (!intel_dp_aux_vesa_backlight_dpcd_mode(connector)) return connector->panel.backlight.max; - if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, &read_val, - sizeof(read_val)) != sizeof(read_val)) { - drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", - DP_EDP_BACKLIGHT_BRIGHTNESS_MSB); + ret = drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, &read_val, + sizeof(read_val)); + if (ret != sizeof(read_val)) { + drm_dbg_kms(&i915->drm, "Failed to read brightness level: %d\n", ret); return 0; } @@ -333,6 +332,7 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int ret; u8 vals[2] = { 0x0 }; /* Write the MSB and/or LSB */ @@ -343,10 +343,10 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, vals[0] = level; } - if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals, - sizeof(vals)) != sizeof(vals)) { - drm_dbg_kms(&i915->drm, - "Failed to write aux backlight level\n"); + ret = drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals, + sizeof(vals)); + if (ret != sizeof(vals)) { + drm_dbg_kms(&i915->drm, "Failed to write aux backlight level: %d\n", ret); return; } } @@ -355,26 +355,28 @@ static void set_vesa_backlight_enable(struct intel_connector *connector, bool en { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int ret; u8 reg_val = 0; /* Early return when display use other mechanism to enable backlight. */ if (!connector->panel.backlight.edp.vesa.aux_enable) return; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) != 1) { - drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", - DP_EDP_DISPLAY_CONTROL_REGISTER); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read eDP display control register: %d\n", ret); return; } + if (enable) reg_val |= DP_EDP_BACKLIGHT_ENABLE; else reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, - reg_val) != 1) { - drm_dbg_kms(&i915->drm, "Failed to %s aux backlight\n", - enabledisable(enable)); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, reg_val); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to %s aux backlight: %d\n", + enabledisable(enable), ret); } } @@ -386,13 +388,13 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, 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; + int ret; u8 dpcd_buf, new_dpcd_buf; u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count; - if (drm_dp_dpcd_readb(&intel_dp->aux, - DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) { - drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n", - DP_EDP_BACKLIGHT_MODE_SET_REGISTER); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read backlight mode: %d\n", ret); return; } @@ -402,24 +404,26 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, - pwmgen_bit_count) != 1) - drm_dbg_kms(&i915->drm, - "Failed to write aux pwmgen bit count\n"); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pwmgen_bit_count); + if (ret != 1) + drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count: %d\n", ret); } if (panel->backlight.edp.vesa.pwm_freq_pre_divider) { - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_FREQ_SET, - panel->backlight.edp.vesa.pwm_freq_pre_divider) == 1) + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_FREQ_SET, + panel->backlight.edp.vesa.pwm_freq_pre_divider); + if (ret == 1) new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE; else - drm_dbg_kms(&i915->drm, "Failed to write aux backlight frequency\n"); + drm_dbg_kms(&i915->drm, "Failed to write aux backlight frequency: %d\n", + ret); } if (new_dpcd_buf != dpcd_buf) { - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, - new_dpcd_buf) != 1) - drm_dbg_kms(&i915->drm, "Failed to write aux backlight mode\n"); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, + new_dpcd_buf); + if (ret != 1) + drm_dbg_kms(&i915->drm, "Failed to write aux backlight mode: %d\n", ret); } intel_dp_aux_vesa_set_backlight(conn_state, level); @@ -446,11 +450,12 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_panel *panel = &connector->panel; u32 max_backlight = 0; - int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1; + int ret, freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1; u8 pn, pn_min, pn_max; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn) != 1) { - drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap\n"); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap: %d\n", ret); return 0; } @@ -479,16 +484,14 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto * - FxP is within 25% of desired value. * Note: 25% is arbitrary value and may need some tweak. */ - if (drm_dp_dpcd_readb(&intel_dp->aux, - DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) { - drm_dbg_kms(&i915->drm, - "Failed to read pwmgen bit count cap min\n"); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap min: %d\n", ret); return max_backlight; } - if (drm_dp_dpcd_readb(&intel_dp->aux, - DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) { - drm_dbg_kms(&i915->drm, - "Failed to read pwmgen bit count cap max\n"); + ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap max: %d\n", ret); return max_backlight; } pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; @@ -512,9 +515,9 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto } drm_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn); - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pn) != 1) { - drm_dbg_kms(&i915->drm, - "Failed to write aux pwmgen bit count\n"); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pn); + if (ret != 1) { + drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count: %d\n", ret); return max_backlight; } From 867cf9cd73c3d31666e4b480aa4f52828d25ac94 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:15:02 -0400 Subject: [PATCH 0009/1318] drm/dp: Extract i915's eDP backlight code into DRM helpers Since we're about to implement eDP backlight support in nouveau using the standard protocol from VESA, we might as well just take the code that's already written for this and move it into a set of shared DRM helpers. Note that these helpers are intended to handle DPCD related backlight control bits such as setting the brightness level over AUX, probing the backlight's TCON, enabling/disabling the backlight over AUX if supported, etc. Any PWM-related portions of backlight control are explicitly left up to the driver, as these will vary from platform to platform. The only exception to this is the calculation of the PWM frequency pre-divider value. This is because the only platform-specific information required for this is the PWM frequency of the panel, which the driver is expected to provide if available. The actual algorithm for calculating this value is standard and is defined in the eDP specification from VESA. Note that these helpers do not yet implement the full range of features the VESA backlight interface provides, and only provide the following functionality (all of which was already present in i915's DPCD backlight support): * Basic control of brightness levels * Basic probing of backlight capabilities * Helpers for enabling and disabling the backlight v3: * Split out changes to i915's backlight code to separate patches to make it easier to review v4: * Style/spelling changes from Thomas Zimmermann v5: * Start using new drm_dbg_*() functions Signed-off-by: Lyude Paul Cc: Jani Nikula Cc: Dave Airlie Cc: greg.depoire@gmail.com Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-9-lyude@redhat.com --- drivers/gpu/drm/drm_dp_helper.c | 347 ++++++++++++++++++ .../drm/i915/display/intel_display_types.h | 5 +- .../drm/i915/display/intel_dp_aux_backlight.c | 285 ++------------ include/drm/drm_dp_helper.h | 48 +++ 4 files changed, 427 insertions(+), 258 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 55b53df6ce34..24bbc710c825 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -3115,3 +3115,350 @@ int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc) return 0; } EXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr); + +/** + * drm_edp_backlight_set_level() - Set the backlight level of an eDP panel via AUX + * @aux: The DP AUX channel to use + * @bl: Backlight capability info from drm_edp_backlight_init() + * @level: The brightness level to set + * + * Sets the brightness level of an eDP panel's backlight. Note that the panel's backlight must + * already have been enabled by the driver by calling drm_edp_backlight_enable(). + * + * Returns: %0 on success, negative error code on failure + */ +int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + u16 level) +{ + int ret; + u8 buf[2] = { 0 }; + + if (bl->lsb_reg_used) { + buf[0] = (level & 0xff00) >> 8; + buf[1] = (level & 0x00ff); + } else { + buf[0] = level; + } + + ret = drm_dp_dpcd_write(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + drm_err(aux->drm_dev, + "%s: Failed to write aux backlight level: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} +EXPORT_SYMBOL(drm_edp_backlight_set_level); + +static int +drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + bool enable) +{ + int ret; + u8 buf; + + /* The panel uses something other then DPCD for enabling its backlight */ + if (!bl->aux_enable) + return 0; + + ret = drm_dp_dpcd_readb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, &buf); + if (ret != 1) { + drm_err(aux->drm_dev, "%s: Failed to read eDP display control register: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + if (enable) + buf |= DP_EDP_BACKLIGHT_ENABLE; + else + buf &= ~DP_EDP_BACKLIGHT_ENABLE; + + ret = drm_dp_dpcd_writeb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, buf); + if (ret != 1) { + drm_err(aux->drm_dev, "%s: Failed to write eDP display control register: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/** + * drm_edp_backlight_enable() - Enable an eDP panel's backlight using DPCD + * @aux: The DP AUX channel to use + * @bl: Backlight capability info from drm_edp_backlight_init() + * @level: The initial backlight level to set via AUX, if there is one + * + * This function handles enabling DPCD backlight controls on a panel over DPCD, while additionally + * restoring any important backlight state such as the given backlight level, the brightness byte + * count, backlight frequency, etc. + * + * Note that certain panels, while supporting brightness level controls over DPCD, may not support + * having their backlights enabled via the standard %DP_EDP_DISPLAY_CONTROL_REGISTER. On such panels + * &drm_edp_backlight_info.aux_enable will be set to %false, this function will skip the step of + * programming the %DP_EDP_DISPLAY_CONTROL_REGISTER, and the driver must perform the required + * implementation specific step for enabling the backlight after calling this function. + * + * Returns: %0 on success, negative error code on failure. + */ +int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + const u16 level) +{ + int ret; + u8 dpcd_buf, new_dpcd_buf; + + ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, + "%s: Failed to read backlight mode: %d\n", aux->name, ret); + return ret < 0 ? ret : -EIO; + } + + new_dpcd_buf = dpcd_buf; + + if ((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) != DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { + new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; + new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; + + ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count); + if (ret != 1) + drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", + aux->name, ret); + } + + if (bl->pwm_freq_pre_divider) { + ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_FREQ_SET, bl->pwm_freq_pre_divider); + if (ret != 1) + drm_dbg_kms(aux->drm_dev, + "%s: Failed to write aux backlight frequency: %d\n", + aux->name, ret); + else + new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE; + } + + if (new_dpcd_buf != dpcd_buf) { + ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux backlight mode: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + } + + ret = drm_edp_backlight_set_level(aux, bl, level); + if (ret < 0) + return ret; + ret = drm_edp_backlight_set_enable(aux, bl, true); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_edp_backlight_enable); + +/** + * drm_edp_backlight_disable() - Disable an eDP backlight using DPCD, if supported + * @aux: The DP AUX channel to use + * @bl: Backlight capability info from drm_edp_backlight_init() + * + * This function handles disabling DPCD backlight controls on a panel over AUX. Note that some + * panels have backlights that are enabled/disabled by other means, despite having their brightness + * values controlled through DPCD. On such panels &drm_edp_backlight_info.aux_enable will be set to + * %false, this function will become a no-op (and we will skip updating + * %DP_EDP_DISPLAY_CONTROL_REGISTER), and the driver must take care to perform it's own + * implementation specific step for disabling the backlight. + * + * Returns: %0 on success or no-op, negative error code on failure. + */ +int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl) +{ + int ret; + + ret = drm_edp_backlight_set_enable(aux, bl, false); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_edp_backlight_disable); + +static inline int +drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, + u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]) +{ + int fxp, fxp_min, fxp_max, fxp_actual, f = 1; + int ret; + u8 pn, pn_min, pn_max; + + ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n", + aux->name, ret); + return -ENODEV; + } + + pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + bl->max = (1 << pn) - 1; + if (!driver_pwm_freq_hz) + return 0; + + /* + * Set PWM Frequency divider to match desired frequency provided by the driver. + * The PWM Frequency is calculated as 27Mhz / (F x P). + * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the + * EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h) + * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the + * EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h) + */ + + /* Find desired value of (F x P) + * Note that, if F x P is out of supported range, the maximum value or minimum value will + * applied automatically. So no need to check that. + */ + fxp = DIV_ROUND_CLOSEST(1000 * DP_EDP_BACKLIGHT_FREQ_BASE_KHZ, driver_pwm_freq_hz); + + /* Use highest possible value of Pn for more granularity of brightness adjustment while + * satifying the conditions below. + * - Pn is in the range of Pn_min and Pn_max + * - F is in the range of 1 and 255 + * - FxP is within 25% of desired value. + * Note: 25% is arbitrary value and may need some tweak. + */ + ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", + aux->name, ret); + return 0; + } + ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", + aux->name, ret); + return 0; + } + pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + /* Ensure frequency is within 25% of desired value */ + fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); + fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); + if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) { + drm_dbg_kms(aux->drm_dev, + "%s: Driver defined backlight frequency (%d) out of range\n", + aux->name, driver_pwm_freq_hz); + return 0; + } + + for (pn = pn_max; pn >= pn_min; pn--) { + f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255); + fxp_actual = f << pn; + if (fxp_min <= fxp_actual && fxp_actual <= fxp_max) + break; + } + + ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, pn); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", + aux->name, ret); + return 0; + } + bl->pwmgen_bit_count = pn; + bl->max = (1 << pn) - 1; + + if (edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) { + bl->pwm_freq_pre_divider = f; + drm_dbg_kms(aux->drm_dev, "%s: Using backlight frequency from driver (%dHz)\n", + aux->name, driver_pwm_freq_hz); + } + + return 0; +} + +static inline int +drm_edp_backlight_probe_level(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, + u8 *current_mode) +{ + int ret; + u8 buf[2]; + u8 mode_reg; + + ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg); + if (ret != 1) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight mode: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + + *current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK); + if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { + int size = 1 + bl->lsb_reg_used; + + ret = drm_dp_dpcd_read(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, size); + if (ret != size) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n", + aux->name, ret); + return ret < 0 ? ret : -EIO; + } + + if (bl->lsb_reg_used) + return (buf[0] << 8) | buf[1]; + else + return buf[0]; + } + + /* + * If we're not in DPCD control mode yet, the programmed brightness value is meaningless and + * the driver should assume max brightness + */ + return bl->max; +} + +/** + * drm_edp_backlight_init() - Probe a display panel's TCON using the standard VESA eDP backlight + * interface. + * @aux: The DP aux device to use for probing + * @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight + * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz + * @edp_dpcd: A cached copy of the eDP DPCD + * @current_level: Where to store the probed brightness level + * @current_mode: Where to store the currently set backlight control mode + * + * Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities, + * along with also probing the current and maximum supported brightness levels. + * + * If @driver_pwm_freq_hz is non-zero, this will be used as the backlight frequency. Otherwise, the + * default frequency from the panel is used. + * + * Returns: %0 on success, negative error code on failure. + */ +int +drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, + u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], + u16 *current_level, u8 *current_mode) +{ + int ret; + + if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) + bl->aux_enable = true; + if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) + bl->lsb_reg_used = true; + + ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd); + if (ret < 0) + return ret; + + ret = drm_edp_backlight_probe_level(aux, bl, current_mode); + if (ret < 0) + return ret; + *current_level = ret; + + drm_dbg_kms(aux->drm_dev, + "%s: Found backlight level=%d/%d pwm_freq_pre_divider=%d mode=%x\n", + aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider, *current_mode); + drm_dbg_kms(aux->drm_dev, + "%s: Backlight caps: pwmgen_bit_count=%d lsb_reg_used=%d aux_enable=%d\n", + aux->name, bl->pwmgen_bit_count, bl->lsb_reg_used, bl->aux_enable); + return 0; +} +EXPORT_SYMBOL(drm_edp_backlight_init); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index d4a57bce71b1..8696b39da2fe 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -310,10 +310,7 @@ struct intel_panel { /* DPCD backlight */ union { struct { - u8 pwmgen_bit_count; - u8 pwm_freq_pre_divider; - bool lsb_reg_used; - bool aux_enable; + struct drm_edp_backlight_info info; } vesa; struct { bool sdr_uses_aux; 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 95f2df631052..6ac568617ef3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -270,114 +270,19 @@ intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pi } /* VESA backlight callbacks */ -static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connector) -{ - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - int ret; - u8 mode_reg; - - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read backlight mode: %d\n", ret); - return false; - } - - return (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) == - DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; -} - -/* - * Read the current backlight value from DPCD register(s) based - * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported - */ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused) { - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - int ret; - u8 read_val[2] = { 0x0 }; - u16 level = 0; - - /* - * If we're not in DPCD control mode yet, the programmed brightness - * value is meaningless and we should assume max brightness - */ - if (!intel_dp_aux_vesa_backlight_dpcd_mode(connector)) - return connector->panel.backlight.max; - - ret = drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, &read_val, - sizeof(read_val)); - if (ret != sizeof(read_val)) { - drm_dbg_kms(&i915->drm, "Failed to read brightness level: %d\n", ret); - return 0; - } - - if (connector->panel.backlight.edp.vesa.lsb_reg_used) - level = (read_val[0] << 8 | read_val[1]); - else - level = read_val[0]; - - return level; + return connector->panel.backlight.level; } -/* - * Sends the current backlight level over the aux channel, checking if its using - * 8-bit or 16 bit value (MSB and LSB) - */ static void -intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, - u32 level) +intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_connector *connector = to_intel_connector(conn_state->connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - int ret; - u8 vals[2] = { 0x0 }; + struct intel_panel *panel = &connector->panel; + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); - /* Write the MSB and/or LSB */ - if (connector->panel.backlight.edp.vesa.lsb_reg_used) { - vals[0] = (level & 0xFF00) >> 8; - vals[1] = (level & 0xFF); - } else { - vals[0] = level; - } - - ret = drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals, - sizeof(vals)); - if (ret != sizeof(vals)) { - drm_dbg_kms(&i915->drm, "Failed to write aux backlight level: %d\n", ret); - return; - } -} - -static void set_vesa_backlight_enable(struct intel_connector *connector, bool enable) -{ - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - int ret; - u8 reg_val = 0; - - /* Early return when display use other mechanism to enable backlight. */ - if (!connector->panel.backlight.edp.vesa.aux_enable) - return; - - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read eDP display control register: %d\n", ret); - return; - } - - if (enable) - reg_val |= DP_EDP_BACKLIGHT_ENABLE; - else - reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); - - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, reg_val); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to %s aux backlight: %d\n", - enabledisable(enable), ret); - } + drm_edp_backlight_set_level(&intel_dp->aux, &panel->backlight.edp.vesa.info, level); } static void @@ -385,170 +290,46 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state, u32 level) { struct intel_connector *connector = to_intel_connector(conn_state->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; - int ret; - u8 dpcd_buf, new_dpcd_buf; - u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count; + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read backlight mode: %d\n", ret); - return; - } - - new_dpcd_buf = dpcd_buf; - - if ((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) != DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { - new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; - new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; - - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pwmgen_bit_count); - if (ret != 1) - drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count: %d\n", ret); - } - - if (panel->backlight.edp.vesa.pwm_freq_pre_divider) { - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_FREQ_SET, - panel->backlight.edp.vesa.pwm_freq_pre_divider); - if (ret == 1) - new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE; - else - drm_dbg_kms(&i915->drm, "Failed to write aux backlight frequency: %d\n", - ret); - } - - if (new_dpcd_buf != dpcd_buf) { - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, - new_dpcd_buf); - if (ret != 1) - drm_dbg_kms(&i915->drm, "Failed to write aux backlight mode: %d\n", ret); - } - - intel_dp_aux_vesa_set_backlight(conn_state, level); - set_vesa_backlight_enable(connector, true); + drm_edp_backlight_enable(&intel_dp->aux, &panel->backlight.edp.vesa.info, level); } static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state, u32 level) { - set_vesa_backlight_enable(to_intel_connector(old_conn_state->connector), false); -} - -/* - * Compute PWM frequency divider value based off the frequency provided to us by the vbt. - * The PWM Frequency is calculated as 27Mhz / (F x P). - * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the - * EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h) - * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the - * EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h) - */ -static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connector) -{ - struct drm_i915_private *i915 = to_i915(connector->base.dev); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_connector *connector = to_intel_connector(old_conn_state->connector); struct intel_panel *panel = &connector->panel; - u32 max_backlight = 0; - int ret, freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1; - u8 pn, pn_min, pn_max; + struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap: %d\n", ret); - return 0; - } - - pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - max_backlight = (1 << pn) - 1; - - /* Find desired value of (F x P) - * Note that, if F x P is out of supported range, the maximum value or - * minimum value will applied automatically. So no need to check that. - */ - freq = i915->vbt.backlight.pwm_freq_hz; - drm_dbg_kms(&i915->drm, "VBT defined backlight frequency %u Hz\n", - freq); - if (!freq) { - drm_dbg_kms(&i915->drm, - "Use panel default backlight frequency\n"); - return max_backlight; - } - - fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq); - - /* Use highest possible value of Pn for more granularity of brightness - * adjustment while satifying the conditions below. - * - Pn is in the range of Pn_min and Pn_max - * - F is in the range of 1 and 255 - * - FxP is within 25% of desired value. - * Note: 25% is arbitrary value and may need some tweak. - */ - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap min: %d\n", ret); - return max_backlight; - } - ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap max: %d\n", ret); - return max_backlight; - } - pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - - /* Ensure frequency is within 25% of desired value */ - fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); - fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); - - if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) { - drm_dbg_kms(&i915->drm, - "VBT defined backlight frequency out of range\n"); - return max_backlight; - } - - for (pn = pn_max; pn >= pn_min; pn--) { - f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255); - fxp_actual = f << pn; - if (fxp_min <= fxp_actual && fxp_actual <= fxp_max) - break; - } - - drm_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn); - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, pn); - if (ret != 1) { - drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit count: %d\n", ret); - return max_backlight; - } - - panel->backlight.edp.vesa.pwmgen_bit_count = pn; - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) - panel->backlight.edp.vesa.pwm_freq_pre_divider = f; - - max_backlight = (1 << pn) - 1; - - return max_backlight; + drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info); } -static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, - enum pipe pipe) +static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe) { 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; - if (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) - panel->backlight.edp.vesa.aux_enable = true; - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) - panel->backlight.edp.vesa.lsb_reg_used = true; - - panel->backlight.max = intel_dp_aux_vesa_calc_max_backlight(connector); - if (!panel->backlight.max) - return -ENODEV; + ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info, + i915->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd, + ¤t_level, ¤t_mode); + if (ret < 0) + return ret; + panel->backlight.max = panel->backlight.edp.vesa.info.max; panel->backlight.min = 0; - panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, pipe); - panel->backlight.enabled = intel_dp_aux_vesa_backlight_dpcd_mode(connector) && - panel->backlight.level != 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; + } return 0; } @@ -559,16 +340,12 @@ intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); - /* Check the eDP Display control capabilities registers to determine if - * the panel can support backlight control over the aux channel. - * - * TODO: We currently only support AUX only backlight configurations, not backlights which + /* TODO: We currently only support AUX only backlight configurations, not backlights which * require a mix of PWM and AUX controls to work. In the mean time, these machines typically * work just fine using normal PWM controls anyway. */ - if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP && - (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) && - (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) { + if ((intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) && + drm_edp_backlight_supported(intel_dp->edp_dpcd)) { drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n"); return true; } diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 06681bf46d81..0bd6396648b4 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1801,6 +1801,24 @@ drm_dp_sink_can_do_video_without_timing_msa(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) DP_MSA_TIMING_PAR_IGNORED; } +/** + * drm_edp_backlight_supported() - Check an eDP DPCD for VESA backlight support + * @edp_dpcd: The DPCD to check + * + * Note that currently this function will return %false for panels which support various DPCD + * backlight features but which require the brightness be set through PWM, and don't support setting + * the brightness level via the DPCD. This is a TODO. + * + * Returns: %True if @edp_dpcd indicates that VESA backlight controls are supported, %false + * otherwise + */ +static inline bool +drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]) +{ + return (edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP) && + (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP); +} + /* * DisplayPort AUX channel */ @@ -2107,6 +2125,36 @@ drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk) return desc->quirks & BIT(quirk); } +/** + * struct drm_edp_backlight_info - Probed eDP backlight info struct + * @pwmgen_bit_count: The pwmgen bit count + * @pwm_freq_pre_divider: The PWM frequency pre-divider value being used for this backlight, if any + * @max: The maximum backlight level that may be set + * @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register? + * @aux_enable: Does the panel support the AUX enable cap? + * + * This structure contains various data about an eDP backlight, which can be populated by using + * drm_edp_backlight_init(). + */ +struct drm_edp_backlight_info { + u8 pwmgen_bit_count; + u8 pwm_freq_pre_divider; + u16 max; + + bool lsb_reg_used : 1; + bool aux_enable : 1; +}; + +int +drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, + u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], + u16 *current_level, u8 *current_mode); +int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + u16 level); +int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + u16 level); +int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl); + #ifdef CONFIG_DRM_DP_CEC void drm_dp_cec_irq(struct drm_dp_aux *aux); void drm_dp_cec_register_connector(struct drm_dp_aux *aux, From 6eca310e8924ab98c73b81dfd39e7dd553ceddfd Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 14 May 2021 14:15:03 -0400 Subject: [PATCH 0010/1318] drm/nouveau/kms/nv50-: Add basic DPCD backlight support for nouveau This adds support for controlling panel backlights over eDP using VESA's standard backlight control interface. Luckily, Nvidia was cool enough to never come up with their own proprietary backlight control interface (at least, not any that I or the laptop manufacturers I've talked to are aware of), so this should work for any laptop panels which support the VESA backlight control interface. Note that we don't yet provide the panel backlight frequency to the DRM DP backlight helpers. This should be fine for the time being, since it's not required to get basic backlight controls working. For reference: there's some mentions of PWM backlight values in nouveau_reg.h, but I'm not sure these are the values we would want to use. If we figure out how to get this information in the future, we'll have the benefit of more granular backlight control. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Cc: Jani Nikula Cc: Dave Airlie Cc: greg.depoire@gmail.com Link: https://patchwork.freedesktop.org/patch/msgid/20210514181504.565252-10-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 28 ++++ drivers/gpu/drm/nouveau/nouveau_backlight.c | 166 ++++++++++++++++++-- drivers/gpu/drm/nouveau/nouveau_connector.h | 9 +- drivers/gpu/drm/nouveau/nouveau_encoder.h | 1 + 4 files changed, 186 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index f949767698fc..093e1f7163b3 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1649,15 +1650,30 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, core->func->sor->ctrl(core, nv_encoder->or, nv_encoder->ctrl, asyh); } +/* TODO: Should we extend this to PWM-only backlights? + * As well, should we add a DRM helper for waiting for the backlight to acknowledge + * the panel backlight has been shut off? Intel doesn't seem to do this, and uses a + * fixed time delay from the vbios… + */ static void nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder); + struct nouveau_backlight *backlight = nv_connector->backlight; struct drm_dp_aux *aux = &nv_connector->aux; + int ret; u8 pwr; + if (backlight && backlight->uses_dpcd) { + ret = drm_edp_backlight_disable(aux, &backlight->edp_info); + if (ret < 0) + NV_ERROR(drm, "Failed to disable backlight on [CONNECTOR:%d:%s]: %d\n", + nv_connector->base.base.id, nv_connector->base.name, ret); + } + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); @@ -1696,6 +1712,9 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_connector *nv_connector; +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT + struct nouveau_backlight *backlight; +#endif struct nvbios *bios = &drm->vbios; bool hda = false; u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM; @@ -1770,6 +1789,14 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode); + +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT + backlight = nv_connector->backlight; + if (backlight && backlight->uses_dpcd) + drm_edp_backlight_enable(&nv_connector->aux, &backlight->edp_info, + (u16)backlight->dev->props.brightness); +#endif + break; default: BUG(); @@ -2295,6 +2322,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_crc_atomic_start_reporting(state); if (!flushed) nv50_crc_atomic_release_notifier_contexts(state); + drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_commit_cleanup_done(state); diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 72f35a2babcb..1cbd71abc80a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -42,11 +42,6 @@ static struct ida bl_ida; #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0' -struct nouveau_backlight { - struct backlight_device *dev; - int id; -}; - static bool nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct nouveau_backlight *bl) @@ -148,6 +143,98 @@ static const struct backlight_ops nv50_bl_ops = { .update_status = nv50_set_intensity, }; +/* + * eDP brightness callbacks need to happen under lock, since we need to + * enable/disable the backlight ourselves for modesets + */ +static int +nv50_edp_get_brightness(struct backlight_device *bd) +{ + struct drm_connector *connector = dev_get_drvdata(bd->dev.parent); + struct drm_device *dev = connector->dev; + struct drm_crtc *crtc; + struct drm_modeset_acquire_ctx ctx; + int ret = 0; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx); + if (ret == -EDEADLK) + goto deadlock; + else if (ret < 0) + goto out; + + crtc = connector->state->crtc; + if (!crtc) + goto out; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret == -EDEADLK) + goto deadlock; + else if (ret < 0) + goto out; + + if (!crtc->state->active) + goto out; + + ret = bd->props.brightness; +out: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return ret; +deadlock: + drm_modeset_backoff(&ctx); + goto retry; +} + +static int +nv50_edp_set_brightness(struct backlight_device *bd) +{ + struct drm_connector *connector = dev_get_drvdata(bd->dev.parent); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct drm_device *dev = connector->dev; + struct drm_crtc *crtc; + struct drm_dp_aux *aux = &nv_connector->aux; + struct nouveau_backlight *nv_bl = nv_connector->backlight; + struct drm_modeset_acquire_ctx ctx; + int ret = 0; + + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx); + if (ret == -EDEADLK) + goto deadlock; + else if (ret < 0) + goto out; + + crtc = connector->state->crtc; + if (!crtc) + goto out; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret == -EDEADLK) + goto deadlock; + else if (ret < 0) + goto out; + + if (crtc->state->active) + ret = drm_edp_backlight_set_level(aux, &nv_bl->edp_info, bd->props.brightness); + +out: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return ret; +deadlock: + drm_modeset_backoff(&ctx); + goto retry; +} + +static const struct backlight_ops nv50_edp_bl_ops = { + .get_brightness = nv50_edp_get_brightness, + .update_status = nv50_edp_set_brightness, +}; + static int nva3_get_intensity(struct backlight_device *bd) { @@ -194,8 +281,13 @@ static const struct backlight_ops nva3_bl_ops = { .update_status = nva3_set_intensity, }; +/* FIXME: perform backlight probing for eDP _before_ this, this only gets called after connector + * registration which happens after the initial modeset + */ static int -nv50_backlight_init(struct nouveau_encoder *nv_encoder, +nv50_backlight_init(struct nouveau_backlight *bl, + struct nouveau_connector *nv_conn, + struct nouveau_encoder *nv_encoder, struct backlight_properties *props, const struct backlight_ops **ops) { @@ -205,6 +297,41 @@ nv50_backlight_init(struct nouveau_encoder *nv_encoder, if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1))) return -ENODEV; + if (nv_conn->type == DCB_CONNECTOR_eDP) { + int ret; + u16 current_level; + u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]; + u8 current_mode; + + ret = drm_dp_dpcd_read(&nv_conn->aux, DP_EDP_DPCD_REV, edp_dpcd, + EDP_DISPLAY_CTL_CAP_SIZE); + if (ret < 0) + return ret; + + if (drm_edp_backlight_supported(edp_dpcd)) { + NV_DEBUG(drm, "DPCD backlight controls supported on %s\n", + nv_conn->base.name); + + ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info, 0, edp_dpcd, + ¤t_level, ¤t_mode); + if (ret < 0) + return ret; + + ret = drm_edp_backlight_enable(&nv_conn->aux, &bl->edp_info, current_level); + if (ret < 0) { + NV_ERROR(drm, "Failed to enable backlight on %s: %d\n", + nv_conn->base.name, ret); + return ret; + } + + *ops = &nv50_edp_bl_ops; + props->brightness = current_level; + props->max_brightness = bl->edp_info.max; + bl->uses_dpcd = true; + return 0; + } + } + if (drm->client.device.info.chipset <= 0xa0 || drm->client.device.info.chipset == 0xaa || drm->client.device.info.chipset == 0xac) @@ -245,6 +372,10 @@ nouveau_backlight_init(struct drm_connector *connector) if (!nv_encoder) return 0; + bl = kzalloc(sizeof(*bl), GFP_KERNEL); + if (!bl) + return -ENOMEM; + switch (device->info.family) { case NV_DEVICE_INFO_V0_CURIE: ret = nv40_backlight_init(nv_encoder, &props, &ops); @@ -257,20 +388,19 @@ nouveau_backlight_init(struct drm_connector *connector) case NV_DEVICE_INFO_V0_VOLTA: case NV_DEVICE_INFO_V0_TURING: case NV_DEVICE_INFO_V0_AMPERE: //XXX: not confirmed - ret = nv50_backlight_init(nv_encoder, &props, &ops); + ret = nv50_backlight_init(bl, nouveau_connector(connector), + nv_encoder, &props, &ops); break; default: - return 0; + ret = 0; + goto fail_alloc; } - if (ret == -ENODEV) - return 0; - else if (ret) - return ret; - - bl = kzalloc(sizeof(*bl), GFP_KERNEL); - if (!bl) - return -ENOMEM; + if (ret) { + if (ret == -ENODEV) + ret = 0; + goto fail_alloc; + } if (!nouveau_get_backlight_name(backlight_name, bl)) { NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); @@ -287,7 +417,9 @@ nouveau_backlight_init(struct drm_connector *connector) } nouveau_connector(connector)->backlight = bl; - bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev); + if (!bl->dev->props.brightness) + bl->dev->props.brightness = + bl->dev->ops->get_brightness(bl->dev); backlight_update_status(bl->dev); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index d0b859c4a80e..40f90e353540 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -46,7 +46,14 @@ struct nvkm_i2c_port; struct dcb_output; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT -struct nouveau_backlight; +struct nouveau_backlight { + struct backlight_device *dev; + + struct drm_edp_backlight_info edp_info; + bool uses_dpcd : 1; + + int id; +}; #endif #define nouveau_conn_atom(p) \ diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 1ffcc0a491fd..77c2fed76e8b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -30,6 +30,7 @@ #include #include +#include #include #include "dispnv04/disp.h" struct nv50_head_atom; From 16c243e99d335e1ef3059871897119affc98b493 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Wed, 9 Jun 2021 11:29:15 -0700 Subject: [PATCH 0011/1318] udmabuf: Add support for mapping hugepages (v4) If the VMM's (Qemu) memory backend is backed up by memfd + Hugepages (hugetlbfs and not THP), we have to first find the hugepage(s) where the Guest allocations are located and then extract the regular 4k sized subpages from them. v2: Ensure that the subpage and hugepage offsets are calculated correctly when the range of subpage allocations cuts across multiple hugepages. v3: Instead of repeatedly looking up the hugepage for each subpage, only do it when the subpage allocation crosses over into a different hugepage. (suggested by Gerd and DW) v4: Fix the following warning identified by checkpatch: CHECK:OPEN_ENDED_LINE: Lines should not end with a '(' Cc: Gerd Hoffmann Signed-off-by: Vivek Kasireddy Signed-off-by: Dongwon Kim Link: http://patchwork.freedesktop.org/patch/msgid/20210609182915.592743-1-vivek.kasireddy@intel.com [ kraxel: one more checkpatch format tweak ] Signed-off-by: Gerd Hoffmann --- drivers/dma-buf/udmabuf.c | 50 +++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index db732f71e59a..1a79ce899b0f 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -11,6 +11,7 @@ #include #include #include +#include static const u32 list_limit = 1024; /* udmabuf_create_list->count limit */ static const size_t size_limit_mb = 64; /* total dmabuf size, in megabytes */ @@ -160,10 +161,13 @@ static long udmabuf_create(struct miscdevice *device, { DEFINE_DMA_BUF_EXPORT_INFO(exp_info); struct file *memfd = NULL; + struct address_space *mapping = NULL; struct udmabuf *ubuf; struct dma_buf *buf; pgoff_t pgoff, pgcnt, pgidx, pgbuf = 0, pglimit; - struct page *page; + struct page *page, *hpage = NULL; + pgoff_t subpgoff, maxsubpgs; + struct hstate *hpstate; int seals, ret = -EINVAL; u32 i, flags; @@ -194,7 +198,8 @@ static long udmabuf_create(struct miscdevice *device, memfd = fget(list[i].memfd); if (!memfd) goto err; - if (!shmem_mapping(file_inode(memfd)->i_mapping)) + mapping = file_inode(memfd)->i_mapping; + if (!shmem_mapping(mapping) && !is_file_hugepages(memfd)) goto err; seals = memfd_fcntl(memfd, F_GET_SEALS, 0); if (seals == -EINVAL) @@ -205,17 +210,48 @@ static long udmabuf_create(struct miscdevice *device, goto err; pgoff = list[i].offset >> PAGE_SHIFT; pgcnt = list[i].size >> PAGE_SHIFT; + if (is_file_hugepages(memfd)) { + hpstate = hstate_file(memfd); + pgoff = list[i].offset >> huge_page_shift(hpstate); + subpgoff = (list[i].offset & + ~huge_page_mask(hpstate)) >> PAGE_SHIFT; + maxsubpgs = huge_page_size(hpstate) >> PAGE_SHIFT; + } for (pgidx = 0; pgidx < pgcnt; pgidx++) { - page = shmem_read_mapping_page( - file_inode(memfd)->i_mapping, pgoff + pgidx); - if (IS_ERR(page)) { - ret = PTR_ERR(page); - goto err; + if (is_file_hugepages(memfd)) { + if (!hpage) { + hpage = find_get_page_flags(mapping, pgoff, + FGP_ACCESSED); + if (IS_ERR(hpage)) { + ret = PTR_ERR(hpage); + goto err; + } + } + page = hpage + subpgoff; + get_page(page); + subpgoff++; + if (subpgoff == maxsubpgs) { + put_page(hpage); + hpage = NULL; + subpgoff = 0; + pgoff++; + } + } else { + page = shmem_read_mapping_page(mapping, + pgoff + pgidx); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + goto err; + } } ubuf->pages[pgbuf++] = page; } fput(memfd); memfd = NULL; + if (hpage) { + put_page(hpage); + hpage = NULL; + } } exp_info.ops = &udmabuf_ops; From d098775ed44021293b1962dea61efb19297b8d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 9 Jun 2021 19:25:56 +0200 Subject: [PATCH 0012/1318] drm/nouveau: init the base GEM fields for internal BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TTMs buffer objects are based on GEM objects for quite a while and rely on initializing those fields before initializing the TTM BO. Nouveau now doesn't init the GEM object for internally allocated BOs, so make sure that we at least initialize some necessary fields. Signed-off-by: Christian König Tested-by: Mikko Perttunen Reviewed-by: Matthew Auld Reviewed-by: Huang Rui Link: https://patchwork.freedesktop.org/patch/msgid/20210609172902.1937-1-christian.koenig@amd.com --- drivers/gpu/drm/nouveau/nouveau_bo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 520b1ea9d16c..085023624fb0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -149,6 +149,8 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) */ if (bo->base.dev) drm_gem_object_release(&bo->base); + else + dma_resv_fini(&bo->base._resv); kfree(nvbo); } @@ -330,6 +332,10 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align, if (IS_ERR(nvbo)) return PTR_ERR(nvbo); + nvbo->bo.base.size = size; + dma_resv_init(&nvbo->bo.base._resv); + drm_vma_node_reset(&nvbo->bo.base.vma_node); + ret = nouveau_bo_init(nvbo, size, align, domain, sg, robj); if (ret) return ret; From a3af0140663dc335e5c18277a61bb99bfcb60694 Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Fri, 4 Jun 2021 16:49:05 +0100 Subject: [PATCH 0013/1318] drm/i915: apply WaEnableVGAAccessThroughIOPort as needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently as the workaround is applied the screen flickers. As a result we do not achieve seamless boot experience. Avoiding the issue in the common use-case might be hard, although we can resolve it for dual GPU setups - when the "other" GPU is primary and/or outputs are connected to it. With this I was able to get seamless experience on my Intel/Nvidia box, running systemd-boot and sddm/Xorg. Note that the i915 driver is within initrd while the Nvidia one is not. Without this patch, the splash presented by systemd-boot (UEFI BGRT) is torn down as the code-path kicks in, leaving the monitor blank until the login manager starts. Same issue were reported with plymouth/grub, although personally I wasn't able to get them to behave on my setup. Suggested-by: Ville Syrjälä Signed-off-by: Emil Velikov Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20210604154905.660142-1-emil.l.velikov@gmail.com --- drivers/gpu/drm/i915/display/intel_vga.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index f002b82ba9c0..3385b45ecd4b 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -29,6 +29,9 @@ void intel_vga_disable(struct drm_i915_private *dev_priv) i915_reg_t vga_reg = intel_vga_cntrl_reg(dev_priv); u8 sr1; + if (intel_de_read(dev_priv, vga_reg) & VGA_DISP_DISABLE) + return; + /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */ vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); outb(SR01, VGA_SR_INDEX); From 26594678d00f94c62f2e43162bd6d10fd0b74917 Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Wed, 9 Jun 2021 20:00:38 -0300 Subject: [PATCH 0014/1318] drm/doc: document how userspace should find out CRTC index In this patch we add a section to document what userspace should do to find out the CRTC index. This is important as they may be many places in the documentation that need this, so it's better to just point to this section and avoid repetition. Signed-off-by: Leandro Ribeiro Reviewed-by: Pekka Paalanen Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20210609230039.73307-2-leandro.ribeiro@collabora.com --- Documentation/gpu/drm-uapi.rst | 13 +++++++++++++ drivers/gpu/drm/drm_debugfs_crc.c | 8 ++++---- include/uapi/drm/drm.h | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 04bdc7a91d53..7e51dd40bf6e 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -457,6 +457,19 @@ Userspace API Structures .. kernel-doc:: include/uapi/drm/drm_mode.h :doc: overview +.. _crtc_index: + +CRTC index +---------- + +CRTC's have both an object ID and an index, and they are not the same thing. +The index is used in cases where a densely packed identifier for a CRTC is +needed, for instance a bitmask of CRTC's. The member possible_crtcs of struct +drm_mode_get_plane is an example. + +DRM_IOCTL_MODE_GETRESOURCES populates a structure with an array of CRTC ID's, +and the CRTC index is its position in this array. + .. kernel-doc:: include/uapi/drm/drm.h :internal: diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index 3dd70d813f69..bbc3bc4ba844 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -46,10 +46,10 @@ * it reached a given hardware component (a CRC sampling "source"). * * Userspace can control generation of CRCs in a given CRTC by writing to the - * file dri/0/crtc-N/crc/control in debugfs, with N being the index of the CRTC. - * Accepted values are source names (which are driver-specific) and the "auto" - * keyword, which will let the driver select a default source of frame CRCs - * for this CRTC. + * file dri/0/crtc-N/crc/control in debugfs, with N being the :ref:`index of + * the CRTC`. Accepted values are source names (which are + * driver-specific) and the "auto" keyword, which will let the driver select a + * default source of frame CRCs for this CRTC. * * Once frame CRC generation is enabled, userspace can capture them by reading * the dri/0/crtc-N/crc/data file. Each line in that file contains the frame diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index d043752a74cf..e1f49dd241f7 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -635,8 +635,8 @@ struct drm_gem_open { /** * DRM_CAP_VBLANK_HIGH_CRTC * - * If set to 1, the kernel supports specifying a CRTC index in the high bits of - * &drm_wait_vblank_request.type. + * If set to 1, the kernel supports specifying a :ref:`CRTC index` + * in the high bits of &drm_wait_vblank_request.type. * * Starting kernel version 2.6.39, this capability is always set to 1. */ From a3a5f9d0fb15da90820254ba735491887cc12099 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Jun 2021 00:05:27 +0200 Subject: [PATCH 0015/1318] drm/panel: db7430: Add driver for Samsung DB7430 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a new driver for the Samsung DB7430 DPI display controller as controlled over SPI. Right now the only panel product we know that is using this display controller is the LMS397KF04 but there may be more. This is the first regular panel driver making use of the MIPI DBI helper library. The DBI "device" portions can not be used because that code assumes the use of a single regulator and specific timings around the reset pulse that do not match the DB7430 datasheet. Cc: Paul Cercueil Cc: Doug Anderson Acked-by: Noralf Trønnes Signed-off-by: Linus Walleij Reviewed-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20210610220527.366432-1-linus.walleij@linaro.org --- MAINTAINERS | 7 + drivers/gpu/drm/panel/Kconfig | 10 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-samsung-db7430.c | 347 +++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-samsung-db7430.c diff --git a/MAINTAINERS b/MAINTAINERS index bc336961ed52..a4ba46fb803a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5857,6 +5857,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml F: drivers/gpu/drm/panel/panel-raydium-rm67191.c +DRM DRIVER FOR SAMSUNG DB7430 PANELS +M: Linus Walleij +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml +F: drivers/gpu/drm/panel/panel-samsung-db7430.c + DRM DRIVER FOR SITRONIX ST7703 PANELS M: Guido Günther R: Purism Kernel Team diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index ef87d92cdf49..1352dffda705 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -343,6 +343,16 @@ config DRM_PANEL_RONBO_RB070D30 Say Y here if you want to enable support for Ronbo Electronics RB070D30 1024x600 DSI panel. +config DRM_PANEL_SAMSUNG_DB7430 + tristate "Samsung DB7430-based DPI panels" + depends on OF && SPI && GPIOLIB + depends on BACKLIGHT_CLASS_DEVICE + select DRM_MIPI_DBI + help + Say Y here if you want to enable support for the Samsung + DB7430 DPI display controller used in such devices as the + LMS397KF04 480x800 DPI panel. + config DRM_PANEL_SAMSUNG_S6D16D0 tristate "Samsung S6D16D0 DSI video mode panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index cae4d976c069..a350e0990d17 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o diff --git a/drivers/gpu/drm/panel/panel-samsung-db7430.c b/drivers/gpu/drm/panel/panel-samsung-db7430.c new file mode 100644 index 000000000000..ead479719f00 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-db7430.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Panel driver for the Samsung LMS397KF04 480x800 DPI RGB panel. + * According to the data sheet the display controller is called DB7430. + * Found in the Samsung Galaxy Beam GT-I8350 mobile phone. + * Linus Walleij + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include