Commit 6dda730e authored by Jani Nikula's avatar Jani Nikula Committed by Daniel Vetter

drm/i915: respect the VBT minimum backlight brightness

Historically we've exposed the full backlight PWM duty cycle range to
the userspace, in the name of "mechanism, not policy". However, it turns
out there are both panels and board designs where there is a minimum
duty cycle that is required for proper operation. The minimum duty cycle
is available in the VBT.

The backlight class sysfs interface does not make any promises to the
userspace about the physical meaning of the range
0..max_brightness. Specifically there is no guarantee that 0 means off;
indeed for acpi_backlight 0 usually is not off, but the minimum
acceptable value.

Respect the minimum backlight, and expose the range acceptable to the
hardware as 0..max_brightness to the userspace via the backlight class
device; 0 means the minimum acceptable enabled value. To switch off the
backlight, the user must disable the encoder.

As a side effect, make the backlight class device max brightness and
physical PWM modulation frequency (i.e. max duty cycle)
independent. This allows a follow-up patch to virtualize the max value
exposed to the userspace.
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Reviewed-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[danvet: s/BUG_ON/WARN_ON/]
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 1de6068e
...@@ -165,6 +165,7 @@ struct intel_panel { ...@@ -165,6 +165,7 @@ struct intel_panel {
struct { struct {
bool present; bool present;
u32 level; u32 level;
u32 min;
u32 max; u32 max;
bool enabled; bool enabled;
bool combination_mode; /* gen 2/4 only */ bool combination_mode; /* gen 2/4 only */
...@@ -969,8 +970,8 @@ void intel_pch_panel_fitting(struct intel_crtc *crtc, ...@@ -969,8 +970,8 @@ void intel_pch_panel_fitting(struct intel_crtc *crtc,
void intel_gmch_panel_fitting(struct intel_crtc *crtc, void intel_gmch_panel_fitting(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config, struct intel_crtc_config *pipe_config,
int fitting_mode); int fitting_mode);
void intel_panel_set_backlight(struct intel_connector *connector, u32 level, void intel_panel_set_backlight_acpi(struct intel_connector *connector,
u32 max); u32 level, u32 max);
int intel_panel_setup_backlight(struct drm_connector *connector); int intel_panel_setup_backlight(struct drm_connector *connector);
void intel_panel_enable_backlight(struct intel_connector *connector); void intel_panel_enable_backlight(struct intel_connector *connector);
void intel_panel_disable_backlight(struct intel_connector *connector); void intel_panel_disable_backlight(struct intel_connector *connector);
......
...@@ -418,7 +418,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) ...@@ -418,7 +418,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
*/ */
DRM_DEBUG_KMS("updating opregion backlight %d/255\n", bclp); DRM_DEBUG_KMS("updating opregion backlight %d/255\n", bclp);
list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head)
intel_panel_set_backlight(intel_connector, bclp, 255); intel_panel_set_backlight_acpi(intel_connector, bclp, 255);
iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv); iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
drm_modeset_unlock(&dev->mode_config.connection_mutex); drm_modeset_unlock(&dev->mode_config.connection_mutex);
......
...@@ -398,6 +398,69 @@ intel_panel_detect(struct drm_device *dev) ...@@ -398,6 +398,69 @@ intel_panel_detect(struct drm_device *dev)
} }
} }
/**
* scale - scale values from one range to another
*
* @source_val: value in range [@source_min..@source_max]
*
* Return @source_val in range [@source_min..@source_max] scaled to range
* [@target_min..@target_max].
*/
static uint32_t scale(uint32_t source_val,
uint32_t source_min, uint32_t source_max,
uint32_t target_min, uint32_t target_max)
{
uint64_t target_val;
WARN_ON(source_min > source_max);
WARN_ON(target_min > target_max);
/* defensive */
source_val = clamp(source_val, source_min, source_max);
/* avoid overflows */
target_val = (uint64_t)(source_val - source_min) *
(target_max - target_min);
do_div(target_val, source_max - source_min);
target_val += target_min;
return target_val;
}
/* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */
static inline u32 scale_user_to_hw(struct intel_connector *connector,
u32 user_level, u32 user_max)
{
struct intel_panel *panel = &connector->panel;
return scale(user_level, 0, user_max,
panel->backlight.min, panel->backlight.max);
}
/* Scale user_level in range [0..user_max] to [0..hw_max], clamping the result
* to [hw_min..hw_max]. */
static inline u32 clamp_user_to_hw(struct intel_connector *connector,
u32 user_level, u32 user_max)
{
struct intel_panel *panel = &connector->panel;
u32 hw_level;
hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max);
hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max);
return hw_level;
}
/* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */
static inline u32 scale_hw_to_user(struct intel_connector *connector,
u32 hw_level, u32 user_max)
{
struct intel_panel *panel = &connector->panel;
return scale(hw_level, panel->backlight.min, panel->backlight.max,
0, user_max);
}
static u32 intel_panel_compute_brightness(struct intel_connector *connector, static u32 intel_panel_compute_brightness(struct intel_connector *connector,
u32 val) u32 val)
{ {
...@@ -557,17 +620,16 @@ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) ...@@ -557,17 +620,16 @@ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level)
dev_priv->display.set_backlight(connector, level); dev_priv->display.set_backlight(connector, level);
} }
/* set backlight brightness to level in range [0..max] */ /* set backlight brightness to level in range [0..max], scaling wrt hw min */
void intel_panel_set_backlight(struct intel_connector *connector, u32 level, static void intel_panel_set_backlight(struct intel_connector *connector,
u32 max) u32 user_level, u32 user_max)
{ {
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_panel *panel = &connector->panel; struct intel_panel *panel = &connector->panel;
enum pipe pipe = intel_get_pipe_from_connector(connector); enum pipe pipe = intel_get_pipe_from_connector(connector);
u32 freq; u32 hw_level;
unsigned long flags; unsigned long flags;
u64 n;
if (!panel->backlight.present || pipe == INVALID_PIPE) if (!panel->backlight.present || pipe == INVALID_PIPE)
return; return;
...@@ -576,18 +638,46 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, ...@@ -576,18 +638,46 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
WARN_ON(panel->backlight.max == 0); WARN_ON(panel->backlight.max == 0);
/* scale to hardware max, but be careful to not overflow */ hw_level = scale_user_to_hw(connector, user_level, user_max);
freq = panel->backlight.max; panel->backlight.level = hw_level;
n = (u64)level * freq;
do_div(n, max); if (panel->backlight.enabled)
level = n; intel_panel_actually_set_backlight(connector, hw_level);
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
}
/* set backlight brightness to level in range [0..max], assuming hw min is
* respected.
*/
void intel_panel_set_backlight_acpi(struct intel_connector *connector,
u32 user_level, u32 user_max)
{
struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_panel *panel = &connector->panel;
enum pipe pipe = intel_get_pipe_from_connector(connector);
u32 hw_level;
unsigned long flags;
if (!panel->backlight.present || pipe == INVALID_PIPE)
return;
spin_lock_irqsave(&dev_priv->backlight_lock, flags);
WARN_ON(panel->backlight.max == 0);
hw_level = clamp_user_to_hw(connector, user_level, user_max);
panel->backlight.level = hw_level;
panel->backlight.level = level;
if (panel->backlight.device) if (panel->backlight.device)
panel->backlight.device->props.brightness = level; panel->backlight.device->props.brightness =
scale_hw_to_user(connector,
panel->backlight.level,
panel->backlight.device->props.max_brightness);
if (panel->backlight.enabled) if (panel->backlight.enabled)
intel_panel_actually_set_backlight(connector, level); intel_panel_actually_set_backlight(connector, hw_level);
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
} }
...@@ -860,7 +950,9 @@ void intel_panel_enable_backlight(struct intel_connector *connector) ...@@ -860,7 +950,9 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
panel->backlight.level = panel->backlight.max; panel->backlight.level = panel->backlight.max;
if (panel->backlight.device) if (panel->backlight.device)
panel->backlight.device->props.brightness = panel->backlight.device->props.brightness =
panel->backlight.level; scale_hw_to_user(connector,
panel->backlight.level,
panel->backlight.device->props.max_brightness);
} }
dev_priv->display.enable_backlight(connector); dev_priv->display.enable_backlight(connector);
...@@ -889,11 +981,15 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd) ...@@ -889,11 +981,15 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
struct intel_connector *connector = bl_get_data(bd); struct intel_connector *connector = bl_get_data(bd);
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
u32 hw_level;
int ret; int ret;
intel_runtime_pm_get(dev_priv); intel_runtime_pm_get(dev_priv);
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
ret = intel_panel_get_backlight(connector);
hw_level = intel_panel_get_backlight(connector);
ret = scale_hw_to_user(connector, hw_level, bd->props.max_brightness);
drm_modeset_unlock(&dev->mode_config.connection_mutex); drm_modeset_unlock(&dev->mode_config.connection_mutex);
intel_runtime_pm_put(dev_priv); intel_runtime_pm_put(dev_priv);
...@@ -913,12 +1009,19 @@ static int intel_backlight_device_register(struct intel_connector *connector) ...@@ -913,12 +1009,19 @@ static int intel_backlight_device_register(struct intel_connector *connector)
if (WARN_ON(panel->backlight.device)) if (WARN_ON(panel->backlight.device))
return -ENODEV; return -ENODEV;
BUG_ON(panel->backlight.max == 0); WARN_ON(panel->backlight.max == 0);
memset(&props, 0, sizeof(props)); memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW; props.type = BACKLIGHT_RAW;
props.brightness = panel->backlight.level;
/*
* Note: Everything should work even if the backlight device max
* presented to the userspace is arbitrarily chosen.
*/
props.max_brightness = panel->backlight.max; props.max_brightness = panel->backlight.max;
props.brightness = scale_hw_to_user(connector,
panel->backlight.level,
props.max_brightness);
/* /*
* Note: using the same name independent of the connector prevents * Note: using the same name independent of the connector prevents
...@@ -964,6 +1067,19 @@ static void intel_backlight_device_unregister(struct intel_connector *connector) ...@@ -964,6 +1067,19 @@ static void intel_backlight_device_unregister(struct intel_connector *connector)
* XXX: Query mode clock or hardware clock and program PWM modulation frequency * XXX: Query mode clock or hardware clock and program PWM modulation frequency
* appropriately when it's 0. Use VBT and/or sane defaults. * appropriately when it's 0. Use VBT and/or sane defaults.
*/ */
static u32 get_backlight_min_vbt(struct intel_connector *connector)
{
struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_panel *panel = &connector->panel;
WARN_ON(panel->backlight.max == 0);
/* vbt value is a coefficient in range [0..255] */
return scale(dev_priv->vbt.backlight.min_brightness, 0, 255,
0, panel->backlight.max);
}
static int bdw_setup_backlight(struct intel_connector *connector) static int bdw_setup_backlight(struct intel_connector *connector)
{ {
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
...@@ -979,6 +1095,8 @@ static int bdw_setup_backlight(struct intel_connector *connector) ...@@ -979,6 +1095,8 @@ static int bdw_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max) if (!panel->backlight.max)
return -ENODEV; return -ENODEV;
panel->backlight.min = get_backlight_min_vbt(connector);
val = bdw_get_backlight(connector); val = bdw_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.level = intel_panel_compute_brightness(connector, val);
...@@ -1003,6 +1121,8 @@ static int pch_setup_backlight(struct intel_connector *connector) ...@@ -1003,6 +1121,8 @@ static int pch_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max) if (!panel->backlight.max)
return -ENODEV; return -ENODEV;
panel->backlight.min = get_backlight_min_vbt(connector);
val = pch_get_backlight(connector); val = pch_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.level = intel_panel_compute_brightness(connector, val);
...@@ -1035,6 +1155,8 @@ static int i9xx_setup_backlight(struct intel_connector *connector) ...@@ -1035,6 +1155,8 @@ static int i9xx_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max) if (!panel->backlight.max)
return -ENODEV; return -ENODEV;
panel->backlight.min = get_backlight_min_vbt(connector);
val = i9xx_get_backlight(connector); val = i9xx_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.level = intel_panel_compute_brightness(connector, val);
...@@ -1062,6 +1184,8 @@ static int i965_setup_backlight(struct intel_connector *connector) ...@@ -1062,6 +1184,8 @@ static int i965_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max) if (!panel->backlight.max)
return -ENODEV; return -ENODEV;
panel->backlight.min = get_backlight_min_vbt(connector);
val = i9xx_get_backlight(connector); val = i9xx_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.level = intel_panel_compute_brightness(connector, val);
...@@ -1099,6 +1223,8 @@ static int vlv_setup_backlight(struct intel_connector *connector) ...@@ -1099,6 +1223,8 @@ static int vlv_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max) if (!panel->backlight.max)
return -ENODEV; return -ENODEV;
panel->backlight.min = get_backlight_min_vbt(connector);
val = _vlv_get_backlight(dev, PIPE_A); val = _vlv_get_backlight(dev, PIPE_A);
panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.level = intel_panel_compute_brightness(connector, val);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment