|
@@ -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,
|
|
|
u32 val)
|
|
|
{
|
|
@@ -557,17 +620,16 @@ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level)
|
|
|
dev_priv->display.set_backlight(connector, level);
|
|
|
}
|
|
|
|
|
|
-/* set backlight brightness to level in range [0..max] */
|
|
|
-void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
|
|
|
- u32 max)
|
|
|
+/* set backlight brightness to level in range [0..max], scaling wrt hw min */
|
|
|
+static void intel_panel_set_backlight(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 freq;
|
|
|
+ u32 hw_level;
|
|
|
unsigned long flags;
|
|
|
- u64 n;
|
|
|
|
|
|
if (!panel->backlight.present || pipe == INVALID_PIPE)
|
|
|
return;
|
|
@@ -576,18 +638,46 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
|
|
|
|
|
|
WARN_ON(panel->backlight.max == 0);
|
|
|
|
|
|
- /* scale to hardware max, but be careful to not overflow */
|
|
|
- freq = panel->backlight.max;
|
|
|
- n = (u64)level * freq;
|
|
|
- do_div(n, max);
|
|
|
- level = n;
|
|
|
+ hw_level = scale_user_to_hw(connector, user_level, user_max);
|
|
|
+ panel->backlight.level = hw_level;
|
|
|
+
|
|
|
+ if (panel->backlight.enabled)
|
|
|
+ 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)
|
|
|
- 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)
|
|
|
- intel_panel_actually_set_backlight(connector, level);
|
|
|
+ intel_panel_actually_set_backlight(connector, hw_level);
|
|
|
|
|
|
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
|
|
|
}
|
|
@@ -860,7 +950,9 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
|
|
|
panel->backlight.level = panel->backlight.max;
|
|
|
if (panel->backlight.device)
|
|
|
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);
|
|
@@ -889,11 +981,15 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
|
|
|
struct intel_connector *connector = bl_get_data(bd);
|
|
|
struct drm_device *dev = connector->base.dev;
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ u32 hw_level;
|
|
|
int ret;
|
|
|
|
|
|
intel_runtime_pm_get(dev_priv);
|
|
|
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);
|
|
|
intel_runtime_pm_put(dev_priv);
|
|
|
|
|
@@ -913,12 +1009,19 @@ static int intel_backlight_device_register(struct intel_connector *connector)
|
|
|
if (WARN_ON(panel->backlight.device))
|
|
|
return -ENODEV;
|
|
|
|
|
|
- BUG_ON(panel->backlight.max == 0);
|
|
|
+ WARN_ON(panel->backlight.max == 0);
|
|
|
|
|
|
memset(&props, 0, sizeof(props));
|
|
|
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.brightness = scale_hw_to_user(connector,
|
|
|
+ panel->backlight.level,
|
|
|
+ props.max_brightness);
|
|
|
|
|
|
/*
|
|
|
* Note: using the same name independent of the connector prevents
|
|
@@ -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
|
|
|
* 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)
|
|
|
{
|
|
|
struct drm_device *dev = connector->base.dev;
|
|
@@ -979,6 +1095,8 @@ static int bdw_setup_backlight(struct intel_connector *connector)
|
|
|
if (!panel->backlight.max)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ panel->backlight.min = get_backlight_min_vbt(connector);
|
|
|
+
|
|
|
val = bdw_get_backlight(connector);
|
|
|
panel->backlight.level = intel_panel_compute_brightness(connector, val);
|
|
|
|
|
@@ -1003,6 +1121,8 @@ static int pch_setup_backlight(struct intel_connector *connector)
|
|
|
if (!panel->backlight.max)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ panel->backlight.min = get_backlight_min_vbt(connector);
|
|
|
+
|
|
|
val = pch_get_backlight(connector);
|
|
|
panel->backlight.level = intel_panel_compute_brightness(connector, val);
|
|
|
|
|
@@ -1035,6 +1155,8 @@ static int i9xx_setup_backlight(struct intel_connector *connector)
|
|
|
if (!panel->backlight.max)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ panel->backlight.min = get_backlight_min_vbt(connector);
|
|
|
+
|
|
|
val = i9xx_get_backlight(connector);
|
|
|
panel->backlight.level = intel_panel_compute_brightness(connector, val);
|
|
|
|
|
@@ -1062,6 +1184,8 @@ static int i965_setup_backlight(struct intel_connector *connector)
|
|
|
if (!panel->backlight.max)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ panel->backlight.min = get_backlight_min_vbt(connector);
|
|
|
+
|
|
|
val = i9xx_get_backlight(connector);
|
|
|
panel->backlight.level = intel_panel_compute_brightness(connector, val);
|
|
|
|
|
@@ -1099,6 +1223,8 @@ static int vlv_setup_backlight(struct intel_connector *connector)
|
|
|
if (!panel->backlight.max)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ panel->backlight.min = get_backlight_min_vbt(connector);
|
|
|
+
|
|
|
val = _vlv_get_backlight(dev, PIPE_A);
|
|
|
panel->backlight.level = intel_panel_compute_brightness(connector, val);
|
|
|
|