2 * Copyright © 2006-2010 Intel Corporation
3 * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Eric Anholt <eric@anholt.net>
26 * Dave Airlie <airlied@linux.ie>
27 * Jesse Barnes <jesse.barnes@intel.com>
28 * Chris Wilson <chris@chris-wilson.co.uk>
32 #include <drm/i915_drm.h>
33 #include "intel_drv.h"
35 #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
38 intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
39 struct drm_display_mode *adjusted_mode)
41 adjusted_mode->hdisplay = fixed_mode->hdisplay;
42 adjusted_mode->hsync_start = fixed_mode->hsync_start;
43 adjusted_mode->hsync_end = fixed_mode->hsync_end;
44 adjusted_mode->htotal = fixed_mode->htotal;
46 adjusted_mode->vdisplay = fixed_mode->vdisplay;
47 adjusted_mode->vsync_start = fixed_mode->vsync_start;
48 adjusted_mode->vsync_end = fixed_mode->vsync_end;
49 adjusted_mode->vtotal = fixed_mode->vtotal;
51 adjusted_mode->clock = fixed_mode->clock;
54 /* adjusted_mode has been preset to be the panel's fixed mode */
56 intel_pch_panel_fitting(struct drm_device *dev,
58 const struct drm_display_mode *mode,
59 struct drm_display_mode *adjusted_mode)
61 struct drm_i915_private *dev_priv = dev->dev_private;
62 int x, y, width, height;
64 x = y = width = height = 0;
66 /* Native modes don't need fitting */
67 if (adjusted_mode->hdisplay == mode->hdisplay &&
68 adjusted_mode->vdisplay == mode->vdisplay)
71 switch (fitting_mode) {
72 case DRM_MODE_SCALE_CENTER:
73 width = mode->hdisplay;
74 height = mode->vdisplay;
75 x = (adjusted_mode->hdisplay - width + 1)/2;
76 y = (adjusted_mode->vdisplay - height + 1)/2;
79 case DRM_MODE_SCALE_ASPECT:
80 /* Scale but preserve the aspect ratio */
82 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
83 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
84 if (scaled_width > scaled_height) { /* pillar */
85 width = scaled_height / mode->vdisplay;
88 x = (adjusted_mode->hdisplay - width + 1) / 2;
90 height = adjusted_mode->vdisplay;
91 } else if (scaled_width < scaled_height) { /* letter */
92 height = scaled_width / mode->hdisplay;
95 y = (adjusted_mode->vdisplay - height + 1) / 2;
97 width = adjusted_mode->hdisplay;
100 width = adjusted_mode->hdisplay;
101 height = adjusted_mode->vdisplay;
107 case DRM_MODE_SCALE_FULLSCREEN:
109 width = adjusted_mode->hdisplay;
110 height = adjusted_mode->vdisplay;
115 dev_priv->pch_pf_pos = (x << 16) | y;
116 dev_priv->pch_pf_size = (width << 16) | height;
119 static int is_backlight_combination_mode(struct drm_device *dev)
121 struct drm_i915_private *dev_priv = dev->dev_private;
123 if (INTEL_INFO(dev)->gen >= 4)
124 return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
127 return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
132 static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
134 struct drm_i915_private *dev_priv = dev->dev_private;
137 /* Restore the CTL value if it lost, e.g. GPU reset */
139 if (HAS_PCH_SPLIT(dev_priv->dev)) {
140 val = I915_READ(BLC_PWM_PCH_CTL2);
141 if (dev_priv->regfile.saveBLC_PWM_CTL2 == 0) {
142 dev_priv->regfile.saveBLC_PWM_CTL2 = val;
143 } else if (val == 0) {
144 val = dev_priv->regfile.saveBLC_PWM_CTL2;
145 I915_WRITE(BLC_PWM_PCH_CTL2, val);
148 val = I915_READ(BLC_PWM_CTL);
149 if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
150 dev_priv->regfile.saveBLC_PWM_CTL = val;
151 if (INTEL_INFO(dev)->gen >= 4)
152 dev_priv->regfile.saveBLC_PWM_CTL2 =
153 I915_READ(BLC_PWM_CTL2);
154 } else if (val == 0) {
155 val = dev_priv->regfile.saveBLC_PWM_CTL;
156 I915_WRITE(BLC_PWM_CTL, val);
157 if (INTEL_INFO(dev)->gen >= 4)
158 I915_WRITE(BLC_PWM_CTL2,
159 dev_priv->regfile.saveBLC_PWM_CTL2);
166 static u32 _intel_panel_get_max_backlight(struct drm_device *dev)
170 max = i915_read_blc_pwm_ctl(dev);
172 if (HAS_PCH_SPLIT(dev)) {
175 if (INTEL_INFO(dev)->gen < 4)
180 if (is_backlight_combination_mode(dev))
187 u32 intel_panel_get_max_backlight(struct drm_device *dev)
191 max = _intel_panel_get_max_backlight(dev);
193 /* XXX add code here to query mode clock or hardware clock
194 * and program max PWM appropriately.
197 pr_warn_once("fixme: max PWM is zero\n");
202 DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
206 static u32 intel_panel_get_backlight(struct drm_device *dev)
208 struct drm_i915_private *dev_priv = dev->dev_private;
211 if (HAS_PCH_SPLIT(dev)) {
212 val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
214 val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
215 if (INTEL_INFO(dev)->gen < 4)
218 if (is_backlight_combination_mode(dev)) {
221 lbpc = pci_read_config(dev->dev, PCI_LBPC, 1);
226 DRM_DEBUG("get backlight PWM = %d\n", val);
230 static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
232 struct drm_i915_private *dev_priv = dev->dev_private;
233 u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
234 I915_WRITE(BLC_PWM_CPU_CTL, val | level);
237 static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
239 struct drm_i915_private *dev_priv = dev->dev_private;
242 DRM_DEBUG("set backlight PWM = %d\n", level);
244 if (HAS_PCH_SPLIT(dev))
245 return intel_pch_panel_set_backlight(dev, level);
247 if (is_backlight_combination_mode(dev)) {
248 u32 max = intel_panel_get_max_backlight(dev);
251 lbpc = level * 0xfe / max + 1;
253 pci_write_config(dev->dev, PCI_LBPC, lbpc, 4);
256 tmp = I915_READ(BLC_PWM_CTL);
257 if (INTEL_INFO(dev)->gen < 4)
259 tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
260 I915_WRITE(BLC_PWM_CTL, tmp | level);
263 void intel_panel_set_backlight(struct drm_device *dev, u32 level)
265 struct drm_i915_private *dev_priv = dev->dev_private;
267 dev_priv->backlight_level = level;
268 if (dev_priv->backlight_enabled)
269 intel_panel_actually_set_backlight(dev, level);
272 void intel_panel_disable_backlight(struct drm_device *dev)
274 struct drm_i915_private *dev_priv = dev->dev_private;
276 dev_priv->backlight_enabled = false;
277 intel_panel_actually_set_backlight(dev, 0);
279 if (INTEL_INFO(dev)->gen >= 4) {
282 reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
284 I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE);
288 void intel_panel_enable_backlight(struct drm_device *dev,
291 struct drm_i915_private *dev_priv = dev->dev_private;
293 if (dev_priv->backlight_level == 0)
294 dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
296 if (INTEL_INFO(dev)->gen >= 4) {
299 reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
302 tmp = I915_READ(reg);
304 /* Note that this can also get called through dpms changes. And
305 * we don't track the backlight dpms state, hence check whether
306 * we have to do anything first. */
307 if (tmp & BLM_PWM_ENABLE)
310 if (dev_priv->num_pipe == 3)
311 tmp &= ~BLM_PIPE_SELECT_IVB;
313 tmp &= ~BLM_PIPE_SELECT;
315 tmp |= BLM_PIPE(pipe);
316 tmp &= ~BLM_PWM_ENABLE;
318 I915_WRITE(reg, tmp);
320 I915_WRITE(reg, tmp | BLM_PWM_ENABLE);
322 if (HAS_PCH_SPLIT(dev)) {
323 tmp = I915_READ(BLC_PWM_PCH_CTL1);
324 tmp |= BLM_PCH_PWM_ENABLE;
325 tmp &= ~BLM_PCH_OVERRIDE_ENABLE;
326 I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
331 /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
332 * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
335 dev_priv->backlight_enabled = true;
336 intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
339 static void intel_panel_init_backlight(struct drm_device *dev)
341 struct drm_i915_private *dev_priv = dev->dev_private;
343 dev_priv->backlight_level = intel_panel_get_backlight(dev);
344 dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
347 enum drm_connector_status
348 intel_panel_detect(struct drm_device *dev)
351 struct drm_i915_private *dev_priv = dev->dev_private;
354 if (i915_panel_ignore_lid)
355 return i915_panel_ignore_lid > 0 ?
356 connector_status_connected :
357 connector_status_disconnected;
359 /* opregion lid state on HP 2540p is wrong at boot up,
360 * appears to be either the BIOS or Linux ACPI fault */
362 /* Assume that the BIOS does not lie through the OpRegion... */
363 if (dev_priv->opregion.lid_state)
364 return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
365 connector_status_connected :
366 connector_status_disconnected;
369 return connector_status_unknown;
372 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
373 static int intel_panel_update_status(struct backlight_device *bd)
375 struct drm_device *dev = bl_get_data(bd);
376 intel_panel_set_backlight(dev, bd->props.brightness);
380 static int intel_panel_get_brightness(struct backlight_device *bd)
382 struct drm_device *dev = bl_get_data(bd);
383 struct drm_i915_private *dev_priv = dev->dev_private;
384 return dev_priv->backlight_level;
387 static const struct backlight_ops intel_panel_bl_ops = {
388 .update_status = intel_panel_update_status,
389 .get_brightness = intel_panel_get_brightness,
392 int intel_panel_setup_backlight(struct drm_connector *connector)
394 struct drm_device *dev = connector->dev;
395 struct drm_i915_private *dev_priv = dev->dev_private;
396 struct backlight_properties props;
398 intel_panel_init_backlight(dev);
400 if (WARN_ON(dev_priv->backlight))
403 memset(&props, 0, sizeof(props));
404 props.type = BACKLIGHT_RAW;
405 props.max_brightness = _intel_panel_get_max_backlight(dev);
406 if (props.max_brightness == 0) {
407 DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n");
410 dev_priv->backlight =
411 backlight_device_register("intel_backlight",
412 &connector->kdev, dev,
413 &intel_panel_bl_ops, &props);
415 if (IS_ERR(dev_priv->backlight)) {
416 DRM_ERROR("Failed to register backlight: %ld\n",
417 PTR_ERR(dev_priv->backlight));
418 dev_priv->backlight = NULL;
421 dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
425 void intel_panel_destroy_backlight(struct drm_device *dev)
427 struct drm_i915_private *dev_priv = dev->dev_private;
428 if (dev_priv->backlight) {
429 backlight_device_unregister(dev_priv->backlight);
430 dev_priv->backlight = NULL;
434 int intel_panel_setup_backlight(struct drm_connector *connector)
436 intel_panel_init_backlight(connector->dev);
440 void intel_panel_destroy_backlight(struct drm_device *dev)
446 int intel_panel_init(struct intel_panel *panel,
447 struct drm_display_mode *fixed_mode)
449 panel->fixed_mode = fixed_mode;
454 void intel_panel_fini(struct intel_panel *panel)
456 struct intel_connector *intel_connector =
457 container_of(panel, struct intel_connector, panel);
459 if (panel->fixed_mode)
460 drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);