diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7537f57..2adad14 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -149,6 +149,11 @@ struct drm_i915_error_state {
 	struct timeval time;
 };
 
+struct blc_funcs {
+	int (*get)(struct drm_device *dev);
+	void (*set)(struct drm_device *dev, int level);
+};
+
 typedef struct drm_i915_private {
 	struct drm_device *dev;
 
@@ -212,6 +217,9 @@ typedef struct drm_i915_private {
 	struct drm_display_mode *panel_fixed_mode;
 	struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
 	struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
+	struct blc_struct blc_info;
+	struct blc_funcs *backlight;
+	struct backlight_device *backlight_device; /* for sysfs */
 
 	/* Feature bits from the VBIOS */
 	unsigned int int_tv_support:1;
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 300aee3..afc042b 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -315,6 +315,27 @@ parse_driver_features(struct drm_i915_private *dev_priv,
 		dev_priv->edp_support = 1;
 }
 
+static void
+parse_lvds_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+	struct bdb_lvds_options *lvds_options;
+	struct bdb_lvds_backlight *backlight;
+	int entry;
+
+	lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+	backlight = find_section(bdb, BDB_LVDS_BACKLIGHT);
+	if (!lvds_options || !backlight)
+		return;
+
+	/* Make sure we have a compatible layout */
+	if (backlight->blcstruct_size != sizeof(struct blc_struct))
+		return;
+
+	entry = lvds_options->panel_type;
+	memcpy(&dev_priv->blc_info, &backlight->panels[entry],
+	       sizeof(struct blc_struct));
+}
+
 /**
  * intel_init_bios - initialize VBIOS settings & find VBT
  * @dev: DRM device
@@ -366,6 +387,7 @@ intel_init_bios(struct drm_device *dev)
 	parse_sdvo_panel_data(dev_priv, bdb);
 	parse_sdvo_device_mapping(dev_priv, bdb);
 	parse_driver_features(dev_priv, bdb);
+	parse_lvds_backlight(dev_priv, bdb);
 
 	pci_unmap_rom(pdev, bios);
 
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 0f8e5f6..5145151 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -380,6 +380,48 @@ struct bdb_sdvo_lvds_options {
 	u8 panel_misc_bits_4;
 } __attribute__((packed));
 
+#define BLC_INVERTER_TYPE_NONE 0
+#define BLC_INVERTER_TYPE_I2C 1
+#define BLC_INVERTER_TYPE_PWM 2
+
+#define BLC_GPIO_NONE 0
+#define BLC_GPIO_I2C 1
+#define BLC_GPIO_CRT_DDC 2
+#define BLC_GPIO_DVI_DDC 3
+#define BLC_GPIO_SDVO_I2C 5
+
+#define BLC_GMBUS_100KHZ 0
+#define BLC_GMBUS_50KHZ 1
+#define BLC_GMBUS_400KHZ 2
+#define BLC_GMBUS_1MHZ 3
+
+struct blc_struct {
+	u8 inverter_type:2;
+	u8 inverter_polarity:1; /* 1 means inverted (0 = max brightness) */
+	u8 gpio_pins:3;
+	u8 gmbus_speed:2;
+	u16 pwm_freq; /* in Hz */
+	u8 min_brightness; /* (0-255) */
+	u8 i2c_slave_addr;
+	u8 i2c_cmd;
+} __attribute__((packed));
+
+struct bdb_lvds_backlight {
+	u8 blcstruct_size;
+	struct blc_struct panels[16];
+} __attribute__((packed));
+
+struct bdb_lvds_power {
+	u8 dpst_enabled:1;
+	u8 pwr_prefs:3;
+	u8 rsvd1:3;
+	u8 als_enabled:1;
+	u16 als_backlight1;
+	u16 als_backlight2;
+	u16 als_backlight3;
+	u16 als_backlight4;
+	u16 als_backlight5;
+} __attribute__((packed));
 
 #define BDB_DRIVER_FEATURE_NO_LVDS		0
 #define BDB_DRIVER_FEATURE_INT_LVDS		1
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 3f445a8..75237c1 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -27,6 +27,7 @@
  *      Jesse Barnes <jesse.barnes@intel.com>
  */
 
+#include <linux/backlight.h>
 #include <linux/dmi.h>
 #include <linux/i2c.h>
 #include "drmP.h"
@@ -60,7 +61,7 @@ struct intel_lvds_priv {
  *
  * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
  */
-static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+static void blc_pwm_set(struct drm_device *dev, int level)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 blc_pwm_ctl, reg;
@@ -75,6 +76,30 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
 				 (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
 }
 
+static int blc_pwm_get(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 blc_pwm_ctl, reg;
+
+	if (IS_IGDNG(dev))
+		reg = BLC_PWM_CPU_CTL;
+	else
+		reg = BLC_PWM_CTL;
+
+	blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+
+	return blc_pwm_ctl;
+}
+
+static void blc_i2c_set(struct drm_device *dev, int level)
+{
+}
+
+static int blc_i2c_get(struct drm_device *dev)
+{
+	return 0;
+}
+
 /**
  * Returns the maximum level of the backlight duty cycle field.
  */
@@ -114,11 +139,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on)
 		do {
 			pp_status = I915_READ(status_reg);
 		} while ((pp_status & PP_ON) == 0);
-
-		intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
 	} else {
-		intel_lvds_set_backlight(dev, 0);
-
 		I915_WRITE(ctl_reg, I915_READ(ctl_reg) &
 			   ~POWER_TARGET_ON);
 		do {
@@ -652,7 +673,11 @@ static int intel_lvds_get_modes(struct drm_connector *connector)
 static void intel_lvds_destroy(struct drm_connector *connector)
 {
 	struct intel_output *intel_output = to_intel_output(connector);
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	if (dev_priv->backlight_device)
+		backlight_device_unregister(dev_priv->backlight_device);
 	if (intel_output->ddc_bus)
 		intel_i2c_destroy(intel_output->ddc_bus);
 	drm_sysfs_connector_remove(connector);
@@ -856,6 +881,75 @@ static int intel_lid_present(void)
 }
 #endif
 
+static int intel_get_backlight_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int intel_update_backlight_status(struct backlight_device *bd)
+{
+	struct drm_device *dev = bl_get_data(bd);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	/* FIXME: check for FB blanking */
+	dev_priv->backlight->set(dev, bd->props.brightness);
+	return 0;
+}
+
+static struct backlight_ops intel_backlight_ops = {
+	.get_brightness = intel_get_backlight_brightness,
+	.update_status = intel_update_backlight_status,
+};
+
+/**
+ * intel_lvds_backlight_init - find & initialize backlight devices
+ * @dev: DRM device
+ *
+ * If we find a backlight controller (i.e. the backlight controller type
+ * isn't BLC_INVERTER_TYPE_NONE) we set it up here.  This either means
+ * a PWM attached controller or an I2C attached controller.  Control of the
+ * backlight is exposed through sysfs, unless an ACPI or other platform
+ * device has already been registered.
+ */
+static void intel_lvds_backlight_init(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct backlight_device *backlight_device;
+
+	/* FIXME: check whether /sys/class/backlight is populated or not */
+
+	switch (dev_priv->blc_info.inverter_type) {
+	case BLC_INVERTER_TYPE_I2C:
+		dev_priv->backlight->get = blc_i2c_get;
+		dev_priv->backlight->set = blc_i2c_set;
+		break;
+	case BLC_INVERTER_TYPE_PWM:
+		dev_priv->backlight->get = blc_pwm_get;
+		dev_priv->backlight->set = blc_pwm_set;
+		break;
+	default:
+		DRM_DEBUG("backlight inverter not available\n");
+		goto out;
+	}
+
+	backlight_device = backlight_device_register("i915", &dev->pdev->dev, dev,
+						     &intel_backlight_ops);
+	if (IS_ERR(backlight_device)) {
+		DRM_ERROR("failed to register backlight device\n");
+		goto out;
+	}
+
+	backlight_device->props.max_brightness =
+		dev_priv->blc_info.inverter_polarity ? 0 : 255;
+	backlight_device->props.brightness = dev_priv->backlight->get(dev);
+	backlight_device->props.power = FB_BLANK_UNBLANK;
+	backlight_update_status(backlight_device);
+
+	dev_priv->backlight_device = backlight_device;
+out:
+	return;
+}
+
 /**
  * intel_lvds_init - setup LVDS connectors on this device
  * @dev: drm device
@@ -1009,6 +1103,8 @@ void intel_lvds_init(struct drm_device *dev)
 	if (!dev_priv->panel_fixed_mode)
 		goto failed;
 
+	intel_lvds_backlight_init(dev);
+
 out:
 	if (IS_IGDNG(dev)) {
 		u32 pwm;
