From patchwork Tue Aug 18 21:51:27 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 42419 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7ILpZgD031166 for ; Tue, 18 Aug 2009 21:51:35 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 92E559F317; Tue, 18 Aug 2009 14:51:34 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from outbound-mail-04.bluehost.com (outbound-mail-04.bluehost.com [69.89.21.14]) by gabe.freedesktop.org (Postfix) with SMTP id 0D8DD9E752 for ; Tue, 18 Aug 2009 14:51:32 -0700 (PDT) Received: (qmail 14019 invoked by uid 0); 18 Aug 2009 21:51:31 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by outboundproxy1.bluehost.com with SMTP; 18 Aug 2009 21:51:31 -0000 Received: from [75.111.28.251] (helo=jbarnes-g45) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1MdWaZ-0005xy-Cf; Tue, 18 Aug 2009 15:51:31 -0600 Date: Tue, 18 Aug 2009 14:51:27 -0700 From: Jesse Barnes To: Mike Lothian Message-ID: <20090818145127.7d7f5ae4@jbarnes-g45> In-Reply-To: References: <20090814175405.GA31971@suse.de> <20090814105639.4406a04b@jbarnes-g45> <20090814181328.GB22721@kroah.com> <20090814112057.3d62d6b9@jbarnes-g45> <20090814182939.GA23578@kroah.com> <20090814190821.GA27325@kroah.com> <20090814192847.GA29073@kroah.com> <20090814221606.714ba266@jbarnes-x200> <20090816220827.GB20988@kroah.com> X-Mailer: Claws Mail 3.7.2 (GTK+ 2.17.5; i486-pc-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 75.111.28.251 authed with jbarnes@virtuousgeek.org} Cc: intel-gfx@lists.freedesktop.org Subject: Re: [Intel-gfx] Legacy backlight control with KMS and BACKLIGHT property X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org On Mon, 17 Aug 2009 01:41:03 +0100 Mike Lothian wrote: > You are an absolute STAR Greg!!! > > Finally I have brightness control for this laptop on Linux > > I needed to set the DMI names to "R510/P510" and the pci device to > 0x2a42 but apart from that it works!!! > > Now I just have to get the keys working :D Any chance I could get you to test this patch out? It should add an "i915-backlight" interface to sysfs instead, but hopefully will work similarly otherwise... Thanks, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7537f57..96fe37c 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/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2955083..377f10e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -895,6 +895,7 @@ /* Backlight control */ #define BLC_PWM_CTL 0x61254 #define BACKLIGHT_MODULATION_FREQ_SHIFT (17) +#define BACKLIGHT_MODULATION_FREQ_SHIFT2 (16) #define BLC_PWM_CTL2 0x61250 /* 965+ only */ #define BLM_COMBINATION_MODE (1 << 30) /* @@ -904,6 +905,7 @@ * The actual value is this field multiplied by two. */ #define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) +#define BACKLIGHT_MODULATION_FREQ_MASK2 (0xffff << 16) #define BLM_LEGACY_MODE (1 << 16) /* * This is the number of cycles out of the backlight modulation cycle for which 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..4c1515b 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -27,6 +27,7 @@ * Jesse Barnes */ +#include #include #include #include "drmP.h" @@ -56,11 +57,14 @@ struct intel_lvds_priv { }; /** - * Sets the backlight level. + * blc_pwm_set - set PWM backlight reg + * @dev: DRM device + * @level: brightness level (from 0-max) * - * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). + * Set the backlight to @level, scaling as necessary based on hw maximum + * and inversion. */ -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; @@ -70,9 +74,47 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level) else reg = BLC_PWM_CTL; + /* Adjust for min and invert if necessary */ + level += dev_priv->blc_info.min_brightness; + if (dev_priv->blc_info.inverter_polarity) + level = dev_priv->backlight_device->props.max_brightness - level; + /* Adjust for combo mode? + * - LBB (if not 0xff) * pwm is brightness + * - so divide incoming value by 255 and feed into pwm + * - if it overflows, put remainder in lbb + * - if lbb is already 0, we need to increase it + */ blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(reg, (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); + I915_WRITE(reg, (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); +} + +static int blc_pwm_get(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int level, reg; + + if (IS_IGDNG(dev)) + reg = BLC_PWM_CPU_CTL; + else + reg = BLC_PWM_CTL; + + level = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; + + /* Adjust for min and invert if necessary */ + if (dev_priv->blc_info.inverter_polarity) + level = dev_priv->backlight_device->props.max_brightness - level; + level -= dev_priv->blc_info.min_brightness; + + return level; +} + +static void blc_i2c_set(struct drm_device *dev, int level) +{ +} + +static int blc_i2c_get(struct drm_device *dev) +{ + return 0; } /** @@ -114,11 +156,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 +690,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 +898,104 @@ static int intel_lid_present(void) } #endif +static int intel_get_backlight_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + struct drm_i915_private *dev_priv = dev->dev_private; + + return dev_priv->backlight.get(dev); +} + +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; + u32 pwm_ctl, pwm_ctl2; + int max; + + /* FIXME: check whether /sys/class/backlight has a platform device */ + + /* ACPI already has one? */ + if (acpi_video_backlight_support()) + return; + + 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; + max = 255; + break; + case BLC_INVERTER_TYPE_PWM: + dev_priv->backlight.get = blc_pwm_get; + dev_priv->backlight.set = blc_pwm_set; + pwm_ctl = I915_READ(BLC_PWM_CTL); + if (IS_I965GM(dev) || IS_GM45(dev)) { + max = ((pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK2) >> + BACKLIGHT_MODULATION_FREQ_SHIFT2); + pwm_ctl2 = I915_READ(BLC_PWM_CTL2); + if (pwm_ctl2 & BLM_COMBINATION_MODE) + max *= 255; + } else { + max = ((pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + if (pwm_ctl & BLM_LEGACY_MODE) + max *= 255; + } + break; + default: + DRM_DEBUG("backlight inverter not available\n"); + goto out; + } + + backlight_device = backlight_device_register("i915-backlight", + &dev->pdev->dev, dev, + &intel_backlight_ops); + if (IS_ERR(backlight_device)) { + DRM_ERROR("failed to register backlight device\n"); + goto out; + } + + dev_priv->backlight_device = backlight_device; + + /* User exposed value always goes from 0-(max - min) */ + if (dev_priv->blc_info.inverter_polarity) + backlight_device->props.max_brightness = + dev_priv->blc_info.min_brightness - max; + else + backlight_device->props.max_brightness = + max - dev_priv->blc_info.min_brightness; + backlight_device->props.brightness = dev_priv->backlight.get(dev); + backlight_device->props.power = FB_BLANK_UNBLANK; + backlight_update_status(backlight_device); +out: + return; +} + /** * intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -1010,6 +1150,8 @@ void intel_lvds_init(struct drm_device *dev) goto failed; out: + intel_lvds_backlight_init(dev); + if (IS_IGDNG(dev)) { u32 pwm; /* make sure PWM is enabled */