On Mon, Apr 12, 2010 at 06:19:20PM +0100, Matthew Garrett wrote:
> Can you give this patch a try? It's not ready for upstream yet, but 
> ought to work.

It would, of course, be more useful if I included the patch.

commit bd599ffa48a679679434109ead29fcdf108ccc77
Author: Matthew Garrett <m...@redhat.com>
Date:   Wed Mar 31 16:36:00 2010 -0400

    intel: Add native backlight control

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index aba8260..40f6684 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -33,6 +33,7 @@
 #include "i915_reg.h"
 #include "intel_bios.h"
 #include <linux/io-mapping.h>
+#include <linux/backlight.h>
 
 /* General customization:
  */
@@ -628,6 +629,8 @@ typedef struct drm_i915_private {
        u8 max_delay;
 
        enum no_fbc_reason no_fbc_reason;
+
+       struct backlight_device *backlight;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
diff --git a/drivers/gpu/drm/i915/i915_opregion.c 
b/drivers/gpu/drm/i915/i915_opregion.c
index 7cc8410..ea71e65 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -31,9 +31,9 @@
 #include "drmP.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
+#include "intel_drv.h"
 
 #define PCI_ASLE 0xe4
-#define PCI_LBPC 0xf4
 #define PCI_ASLS 0xfc
 
 #define OPREGION_SZ            (8*1024)
@@ -151,37 +151,10 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 
bclp)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct opregion_asle *asle = dev_priv->opregion.asle;
-       u32 blc_pwm_ctl, blc_pwm_ctl2;
-       u32 max_backlight, level, shift;
-
-       if (!(bclp & ASLE_BCLP_VALID))
-               return ASLE_BACKLIGHT_FAIL;
-
-       bclp &= ASLE_BCLP_MSK;
-       if (bclp < 0 || bclp > 255)
-               return ASLE_BACKLIGHT_FAIL;
-
-       blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
-       blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2);
-
-       if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
-               pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
-       else {
-               if (IS_PINEVIEW(dev)) {
-                       blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
-                       max_backlight = (blc_pwm_ctl & 
BACKLIGHT_MODULATION_FREQ_MASK) >> 
-                                       BACKLIGHT_MODULATION_FREQ_SHIFT;
-                       shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1;
-               } else {
-                       blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
-                       max_backlight = ((blc_pwm_ctl & 
BACKLIGHT_MODULATION_FREQ_MASK) >> 
-                                       BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
-                       shift = BACKLIGHT_DUTY_CYCLE_SHIFT;
-               }
-               level = (bclp * max_backlight) / 255;
-               I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift));
-       }
-       asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
+       u32 max = intel_lvds_get_max_backlight(dev);
+
+       asle->cblv = (bclp * 100 / 255) | ASLE_CBLV_VALID;
+       intel_lvds_set_backlight(dev, max * bclp / 255);
 
        return 0;
 }
@@ -247,36 +220,6 @@ void opregion_asle_intr(struct drm_device *dev)
        asle->aslc = asle_stat;
 }
 
-static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct opregion_asle *asle = dev_priv->opregion.asle;
-       u32 cpu_pwm_ctl, pch_pwm_ctl2;
-       u32 max_backlight, level;
-
-       if (!(bclp & ASLE_BCLP_VALID))
-               return ASLE_BACKLIGHT_FAILED;
-
-       bclp &= ASLE_BCLP_MSK;
-       if (bclp < 0 || bclp > 255)
-               return ASLE_BACKLIGHT_FAILED;
-
-       cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL);
-       pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
-       /* get the max PWM frequency */
-       max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK;
-       /* calculate the expected PMW frequency */
-       level = (bclp * max_backlight) / 255;
-       /* reserve the high 16 bits */
-       cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK);
-       /* write the updated PWM frequency */
-       I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level);
-
-       asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
-
-       return 0;
-}
-
 void ironlake_opregion_gse_intr(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -300,7 +243,7 @@ void ironlake_opregion_gse_intr(struct drm_device *dev)
        }
 
        if (asle_req & ASLE_SET_BACKLIGHT)
-               asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp);
+               asle_stat |= asle_set_backlight(dev, asle->bclp);
 
        if (asle_req & ASLE_SET_PFIT) {
                DRM_DEBUG_DRIVER("Pfit is not supported\n");
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 3a467ca..1c35f67 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -171,6 +171,9 @@ extern void intel_dvo_init(struct drm_device *dev);
 extern void intel_tv_init(struct drm_device *dev);
 extern void intel_mark_busy(struct drm_device *dev, struct drm_gem_object 
*obj);
 extern void intel_lvds_init(struct drm_device *dev);
+extern u32 intel_lvds_get_max_backlight(struct drm_device *dev);
+extern u32 intel_lvds_get_backlight(struct drm_device *dev);
+extern void intel_lvds_set_backlight(struct drm_device *dev, int level);
 extern void intel_dp_init(struct drm_device *dev, int dp_reg);
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
diff --git a/drivers/gpu/drm/i915/intel_lvds.c 
b/drivers/gpu/drm/i915/intel_lvds.c
index 2b3fa7a..ee2a06f 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -38,6 +38,7 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 #include <linux/acpi.h>
+#include <linux/backlight.h>
 
 /* Private structure for the integrated LVDS support */
 struct intel_lvds_priv {
@@ -47,43 +48,174 @@ struct intel_lvds_priv {
 };
 
 /**
- * Sets the backlight level.
- *
- * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ * Returns the maximum level of the backlight duty cycle field.
  */
-static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+u32 intel_lvds_get_max_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 blc_pwm_ctl, reg;
+       u32 reg;
+       int value;
+       bool combo;
 
-       if (HAS_PCH_SPLIT(dev))
-               reg = BLC_PWM_CPU_CTL;
+       if (IS_I965G(dev))
+               combo = I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
+       else
+               combo = I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
+
+       if (IS_IRONLAKE(dev))
+               reg = BLC_PWM_PCH_CTL2;
        else
                reg = BLC_PWM_CTL;
 
-       blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
-       I915_WRITE(reg, (blc_pwm_ctl |
-                                (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+       value = ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+                BACKLIGHT_MODULATION_FREQ_SHIFT);
+
+       if (!IS_PINEVIEW(dev))
+               value *= 2;
+
+       if (combo) {
+               value *= 0xff;
+               value /= 2;
+       }
+
+       return value;
 }
 
 /**
- * Returns the maximum level of the backlight duty cycle field.
+ * Returns the level of the backlight duty cycle field.
  */
-static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+u32 intel_lvds_get_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
+       u8 lbpc;
+       int value;
+       bool combo;
 
-       if (HAS_PCH_SPLIT(dev))
-               reg = BLC_PWM_PCH_CTL2;
+       if (IS_I965G(dev))
+               combo = I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
+       else
+               combo = I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
+
+       if (IS_IRONLAKE(dev))
+               reg = BLC_PWM_CPU_CTL;
        else
                reg = BLC_PWM_CTL;
 
-       return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
-               BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+       value = I915_READ(reg) & BACKLIGHT_DUTY_CYCLE_MASK;
+
+       if (IS_PINEVIEW(dev))
+               value /= 2;
+
+       if (combo) {
+               value &= ~0x1;
+               pci_read_config_byte(dev->pdev, LBB, &lbpc);
+               value *= lbpc;
+               value /= 2;
+       }
+
+       return value;
 }
 
 /**
+ * Sets the backlight level.
+  *
+  * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+  */
+void intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+        struct drm_i915_private *dev_priv = dev->dev_private;
+        u32 blc_pwm_ctl, reg;
+       bool combo;
+       u8 lbpc;
+
+       if (IS_I965G(dev))
+               combo = I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
+       else
+               combo = I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
+
+       if (IS_IRONLAKE(dev))
+               reg = BLC_PWM_CPU_CTL;
+       else
+               reg = BLC_PWM_CTL;
+
+       if (combo) {
+               int maximum = intel_lvds_get_max_backlight(dev);
+               lbpc = level * 0xfe / maximum;
+               lbpc += 1;
+               pci_write_config_byte(dev->pdev, LBB, lbpc);
+               level /= lbpc;
+               level <<= 1;
+       }
+
+       if (IS_PINEVIEW(dev)) {
+               blc_pwm_ctl = I915_READ(reg) & ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
+               I915_WRITE(reg, (blc_pwm_ctl |
+                                (level << (BACKLIGHT_DUTY_CYCLE_SHIFT + 1))));
+       } else {
+               blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+               I915_WRITE(reg, blc_pwm_ctl |
+                          (level << BACKLIGHT_DUTY_CYCLE_SHIFT));
+       }
+}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+static int intel_lvds_update_status(struct backlight_device *bd)
+{
+       struct drm_device *dev = bl_get_data(bd);
+       intel_lvds_set_backlight(dev, bd->props.brightness);
+       return 0;
+}
+
+static int intel_lvds_get_brightness(struct backlight_device *bd)
+{
+       struct drm_device *dev = bl_get_data(bd);
+       return intel_lvds_get_backlight(dev);
+}
+
+static const struct backlight_ops intel_lvds_bl_ops = {
+       .update_status = intel_lvds_update_status,
+       .get_brightness = intel_lvds_get_brightness,
+};
+
+static int intel_lvds_backlight_setup(struct drm_device *dev)
+{
+        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct backlight_properties props;
+
+       props.max_brightness = intel_lvds_get_max_backlight(dev);
+       dev_priv->backlight = backlight_device_register("intel_backlight",
+                                                       &dev->pdev->dev, dev,
+                                                       &intel_lvds_bl_ops,
+                                                       &props);
+       if (IS_ERR(dev_priv->backlight)) {
+               DRM_ERROR("Failed to register backlight: %ld\n",
+                         PTR_ERR(dev_priv->backlight));
+               dev_priv->backlight = NULL;
+               return -ENODEV;
+       }
+       dev_priv->backlight->props.brightness = intel_lvds_get_backlight(dev);
+       return 0;
+}
+
+static void intel_lvds_backlight_destroy(struct drm_device *dev) {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       if (dev_priv->backlight)
+               backlight_device_unregister(dev_priv->backlight);
+}
+#else
+static int intel_lvds_backlight_setup(struct drm_device *dev)
+{
+       return 0;
+}
+
+static void intel_lvds_backlight_destroy(struct drm_device *dev)
+{
+       return;
+}
+#endif
+
+/**
  * Sets the power state for the panel.
  */
 static void intel_lvds_set_power(struct drm_device *dev, bool on)
@@ -164,8 +296,7 @@ static void intel_lvds_save(struct drm_connector *connector)
        dev_priv->savePP_CONTROL = I915_READ(pp_ctl_reg);
        dev_priv->savePP_DIVISOR = I915_READ(pp_div_reg);
        dev_priv->saveBLC_PWM_CTL = I915_READ(pwm_ctl_reg);
-       dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
-                                      BACKLIGHT_DUTY_CYCLE_MASK);
+       dev_priv->backlight_duty_cycle = intel_lvds_get_backlight(dev);
 
        /*
         * If the light is off at server startup, just make it full brightness
@@ -562,8 +693,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
                reg = BLC_PWM_CTL;
 
        dev_priv->saveBLC_PWM_CTL = I915_READ(reg);
-       dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
-                                      BACKLIGHT_DUTY_CYCLE_MASK);
+       dev_priv->backlight_duty_cycle = intel_lvds_get_backlight(dev);
 
        intel_lvds_set_power(dev, false);
 }
@@ -717,6 +847,8 @@ static void intel_lvds_destroy(struct drm_connector 
*connector)
        struct intel_output *intel_output = to_intel_output(connector);
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       intel_lvds_backlight_destroy(dev);
+
        if (intel_output->ddc_bus)
                intel_i2c_destroy(intel_output->ddc_bus);
        if (dev_priv->lid_notifier.notifier_call)
@@ -1128,6 +1260,9 @@ out:
        /* keep the LVDS connector */
        dev_priv->int_lvds_connector = connector;
        drm_sysfs_connector_add(connector);
+
+       intel_lvds_backlight_setup(dev);
+
        return;
 
 failed:

-- 
Matthew Garrett | mj...@srcf.ucam.org
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to