All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matthew Garrett <mjg@redhat.com>
To: linux-kernel@vger.kernel.org
Cc: Matthew Garrett <mjg@redhat.com>,
	intel-gfx <intel-gfx@lists.freedesktop.org>
Subject: [PATCH] i915: Add native backlight control
Date: Wed,  8 Sep 2010 12:32:18 -0400	[thread overview]
Message-ID: <1283963539-4039-2-git-send-email-mjg@redhat.com> (raw)
In-Reply-To: <1283963539-4039-1-git-send-email-mjg@redhat.com>

Not all systems expose a firmware or platform mechanism for changing the
backlight intensity on i915, so add native driver support.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Cc: intel-gfx <intel-gfx@lists.freedesktop.org>
---
 drivers/gpu/drm/i915/i915_drv.h      |    3 +
 drivers/gpu/drm/i915/i915_opregion.c |   60 +-----------
 drivers/gpu/drm/i915/intel_drv.h     |    3 +
 drivers/gpu/drm/i915/intel_lvds.c    |  177 ++++++++++++++++++++++++++++++----
 4 files changed, 170 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index af4a263..36c4b407 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -34,6 +34,7 @@
 #include "intel_bios.h"
 #include "intel_ringbuffer.h"
 #include <linux/io-mapping.h>
+#include <linux/backlight.h>
 
 /* General customization:
  */
@@ -663,6 +664,8 @@ typedef struct drm_i915_private {
 
 	/* list of fbdev register on this device */
 	struct intel_fbdev *fbdev;
+
+	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 ea5d3fe..de199dd 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)
@@ -147,8 +147,7 @@ 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;
+	u32 max = intel_lvds_get_max_backlight(dev);
 
 	if (!(bclp & ASLE_BCLP_VALID))
 		return ASLE_BACKLIGHT_FAILED;
@@ -157,27 +156,8 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 	if (bclp < 0 || bclp > 255)
 		return ASLE_BACKLIGHT_FAILED;
 
-	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;
+	asle->cblv = (bclp * 100 / 255) | ASLE_CBLV_VALID;
+	intel_lvds_set_backlight(dev, max * bclp / 255);
 
 	return 0;
 }
@@ -243,36 +223,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;
@@ -296,7 +246,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 ad312ca..a0f870f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -201,6 +201,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 b819c10..0ab5891 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -39,6 +39,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 {
@@ -54,42 +55,178 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder)
 }
 
 /**
- * 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;
+
+	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;
 
-	return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
-		BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+	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.type = BACKLIGHT_RAW;
+	props.max_brightness = intel_lvds_get_max_backlight(dev);
+	dev_priv->backlight =
+		backlight_device_register("intel_backlight",
+					  &dev_priv->int_lvds_connector->kdev,
+					  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.
  */
@@ -394,8 +531,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);
 }
@@ -573,6 +709,8 @@ static void intel_lvds_destroy(struct drm_connector *connector)
 	struct drm_device *dev = connector->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	intel_lvds_backlight_destroy(dev);
+
 	if (dev_priv->lid_notifier.notifier_call)
 		acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
 	drm_sysfs_connector_remove(connector);
@@ -992,6 +1130,9 @@ out:
 	/* keep the LVDS connector */
 	dev_priv->int_lvds_connector = connector;
 	drm_sysfs_connector_add(connector);
+
+	intel_lvds_backlight_setup(dev);
+
 	return;
 
 failed:
-- 
1.7.2.1


WARNING: multiple messages have this Message-ID (diff)
From: Matthew Garrett <mjg@redhat.com>
To: linux-kernel@vger.kernel.org
Cc: intel-gfx <intel-gfx@lists.freedesktop.org>,
	Matthew Garrett <mjg@redhat.com>
Subject: [PATCH] i915: Add native backlight control
Date: Wed,  8 Sep 2010 12:32:18 -0400	[thread overview]
Message-ID: <1283963539-4039-2-git-send-email-mjg@redhat.com> (raw)
In-Reply-To: <1283963539-4039-1-git-send-email-mjg@redhat.com>

Not all systems expose a firmware or platform mechanism for changing the
backlight intensity on i915, so add native driver support.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Cc: intel-gfx <intel-gfx@lists.freedesktop.org>
---
 drivers/gpu/drm/i915/i915_drv.h      |    3 +
 drivers/gpu/drm/i915/i915_opregion.c |   60 +-----------
 drivers/gpu/drm/i915/intel_drv.h     |    3 +
 drivers/gpu/drm/i915/intel_lvds.c    |  177 ++++++++++++++++++++++++++++++----
 4 files changed, 170 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index af4a263..36c4b407 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -34,6 +34,7 @@
 #include "intel_bios.h"
 #include "intel_ringbuffer.h"
 #include <linux/io-mapping.h>
+#include <linux/backlight.h>
 
 /* General customization:
  */
@@ -663,6 +664,8 @@ typedef struct drm_i915_private {
 
 	/* list of fbdev register on this device */
 	struct intel_fbdev *fbdev;
+
+	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 ea5d3fe..de199dd 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)
@@ -147,8 +147,7 @@ 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;
+	u32 max = intel_lvds_get_max_backlight(dev);
 
 	if (!(bclp & ASLE_BCLP_VALID))
 		return ASLE_BACKLIGHT_FAILED;
@@ -157,27 +156,8 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 	if (bclp < 0 || bclp > 255)
 		return ASLE_BACKLIGHT_FAILED;
 
-	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;
+	asle->cblv = (bclp * 100 / 255) | ASLE_CBLV_VALID;
+	intel_lvds_set_backlight(dev, max * bclp / 255);
 
 	return 0;
 }
@@ -243,36 +223,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;
@@ -296,7 +246,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 ad312ca..a0f870f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -201,6 +201,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 b819c10..0ab5891 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -39,6 +39,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 {
@@ -54,42 +55,178 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder)
 }
 
 /**
- * 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;
+
+	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;
 
-	return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
-		BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+	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.type = BACKLIGHT_RAW;
+	props.max_brightness = intel_lvds_get_max_backlight(dev);
+	dev_priv->backlight =
+		backlight_device_register("intel_backlight",
+					  &dev_priv->int_lvds_connector->kdev,
+					  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.
  */
@@ -394,8 +531,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);
 }
@@ -573,6 +709,8 @@ static void intel_lvds_destroy(struct drm_connector *connector)
 	struct drm_device *dev = connector->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	intel_lvds_backlight_destroy(dev);
+
 	if (dev_priv->lid_notifier.notifier_call)
 		acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
 	drm_sysfs_connector_remove(connector);
@@ -992,6 +1130,9 @@ out:
 	/* keep the LVDS connector */
 	dev_priv->int_lvds_connector = connector;
 	drm_sysfs_connector_add(connector);
+
+	intel_lvds_backlight_setup(dev);
+
 	return;
 
 failed:
-- 
1.7.2.1

  reply	other threads:[~2010-09-08 16:32 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-09-08 16:32 [PATCH] Backlight: Add backlight type Matthew Garrett
2010-09-08 16:32 ` Matthew Garrett
2010-09-08 16:32 ` Matthew Garrett [this message]
2010-09-08 16:32   ` [PATCH] i915: Add native backlight control Matthew Garrett
2010-09-09 17:09   ` [Intel-gfx] " Chris Wilson
2010-09-09 17:09     ` Chris Wilson
2010-09-09 17:14     ` [Intel-gfx] " Matthew Garrett
2010-09-10 13:38   ` [PATCH] drm/i915: Expose a native backlight device Chris Wilson
2010-09-10 13:38     ` Chris Wilson
2010-09-10 13:47     ` Matthew Garrett
2010-09-10 13:47       ` Matthew Garrett
2010-09-08 16:32 ` [PATCH] radeon: Expose backlight class device for legacy LVDS encoder Matthew Garrett
2010-09-08 16:58   ` Alex Deucher
2010-09-08 17:03     ` Matthew Garrett
2010-09-08 17:03       ` Matthew Garrett
2010-09-08 17:04       ` Alex Deucher
2010-09-13  7:54   ` Michel Dänzer
2010-09-13  7:54     ` Michel Dänzer
2010-09-15 11:38     ` Michel Dänzer
2010-09-15 11:38       ` Michel Dänzer
2010-09-09 16:59 ` [PATCH] Backlight: Add backlight type Mike Frysinger
2010-11-15 15:48 ` [Intel-gfx] " Matthew Garrett
2010-11-15 16:44   ` Jesse Barnes
2010-11-15 16:44     ` Jesse Barnes
2011-03-24  6:53     ` [Intel-gfx] " Mike Frysinger
2011-03-24  7:13       ` Mike Frysinger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1283963539-4039-2-git-send-email-mjg@redhat.com \
    --to=mjg@redhat.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.