Intel-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
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: 11+ 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 [this message]
2010-09-09 17:09   ` [PATCH] i915: Add native backlight control 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:47     ` Matthew Garrett
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
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox