11d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson/*
21d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Copyright © 2006-2010 Intel Corporation
31d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
41d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *
51d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Permission is hereby granted, free of charge, to any person obtaining a
61d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * copy of this software and associated documentation files (the "Software"),
71d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * to deal in the Software without restriction, including without limitation
81d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * the rights to use, copy, modify, merge, publish, distribute, sublicense,
91d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * and/or sell copies of the Software, and to permit persons to whom the
101d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Software is furnished to do so, subject to the following conditions:
111d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *
121d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * The above copyright notice and this permission notice (including the next
131d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * paragraph) shall be included in all copies or substantial portions of the
141d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Software.
151d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *
161d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
191d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
211d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
221d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * DEALINGS IN THE SOFTWARE.
231d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *
241d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson * Authors:
251d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *	Eric Anholt <eric@anholt.net>
261d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *      Dave Airlie <airlied@linux.ie>
271d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *      Jesse Barnes <jesse.barnes@intel.com>
281d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson *      Chris Wilson <chris@chris-wilson.co.uk>
291d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson */
301d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
311d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson#include "intel_drv.h"
321d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
33ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
34ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
351d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilsonvoid
361d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilsonintel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
371d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		       struct drm_display_mode *adjusted_mode)
381d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson{
391d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->hdisplay = fixed_mode->hdisplay;
401d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->hsync_start = fixed_mode->hsync_start;
411d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->hsync_end = fixed_mode->hsync_end;
421d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->htotal = fixed_mode->htotal;
431d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
441d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->vdisplay = fixed_mode->vdisplay;
451d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->vsync_start = fixed_mode->vsync_start;
461d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->vsync_end = fixed_mode->vsync_end;
471d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->vtotal = fixed_mode->vtotal;
481d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
491d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	adjusted_mode->clock = fixed_mode->clock;
501d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson}
511d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
521d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson/* adjusted_mode has been preset to be the panel's fixed mode */
531d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilsonvoid
541d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilsonintel_pch_panel_fitting(struct drm_device *dev,
551d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			int fitting_mode,
561d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			struct drm_display_mode *mode,
571d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			struct drm_display_mode *adjusted_mode)
581d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson{
591d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
601d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	int x, y, width, height;
611d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
621d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	x = y = width = height = 0;
631d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
641d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	/* Native modes don't need fitting */
651d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	if (adjusted_mode->hdisplay == mode->hdisplay &&
661d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	    adjusted_mode->vdisplay == mode->vdisplay)
671d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		goto done;
681d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
691d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	switch (fitting_mode) {
701d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	case DRM_MODE_SCALE_CENTER:
711d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		width = mode->hdisplay;
721d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		height = mode->vdisplay;
731d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		x = (adjusted_mode->hdisplay - width + 1)/2;
741d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		y = (adjusted_mode->vdisplay - height + 1)/2;
751d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		break;
761d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
771d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	case DRM_MODE_SCALE_ASPECT:
781d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		/* Scale but preserve the aspect ratio */
791d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		{
801d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
811d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
821d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			if (scaled_width > scaled_height) { /* pillar */
831d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				width = scaled_height / mode->vdisplay;
84302983e9059e9ef5de3ca7671918eeb237c5971eAdam Jackson				if (width & 1)
850206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi					width++;
861d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				x = (adjusted_mode->hdisplay - width + 1) / 2;
871d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				y = 0;
881d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				height = adjusted_mode->vdisplay;
891d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			} else if (scaled_width < scaled_height) { /* letter */
901d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				height = scaled_width / mode->hdisplay;
91302983e9059e9ef5de3ca7671918eeb237c5971eAdam Jackson				if (height & 1)
92302983e9059e9ef5de3ca7671918eeb237c5971eAdam Jackson				    height++;
931d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				y = (adjusted_mode->vdisplay - height + 1) / 2;
941d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				x = 0;
951d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				width = adjusted_mode->hdisplay;
961d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			} else {
971d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				x = y = 0;
981d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				width = adjusted_mode->hdisplay;
991d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson				height = adjusted_mode->vdisplay;
1001d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson			}
1011d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		}
1021d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		break;
1031d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
1041d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	default:
1051d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	case DRM_MODE_SCALE_FULLSCREEN:
1061d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		x = y = 0;
1071d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		width = adjusted_mode->hdisplay;
1081d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		height = adjusted_mode->vdisplay;
1091d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson		break;
1101d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	}
1111d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson
1121d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilsondone:
1131d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	dev_priv->pch_pf_pos = (x << 16) | y;
1141d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson	dev_priv->pch_pf_size = (width << 16) | height;
1151d8e1c75ffa84400758aef9cc59298920b8801f9Chris Wilson}
116a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
117ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwaistatic int is_backlight_combination_mode(struct drm_device *dev)
118ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai{
119ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai	struct drm_i915_private *dev_priv = dev->dev_private;
120ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
121ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai	if (INTEL_INFO(dev)->gen >= 4)
122ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
123ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
124ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai	if (IS_GEN2(dev))
125ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
126ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
127ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai	return 0;
128ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai}
129ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
1300b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilsonstatic u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
1310b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson{
1320b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	u32 val;
1330b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson
1340b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	/* Restore the CTL value if it lost, e.g. GPU reset */
1350b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson
1360b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	if (HAS_PCH_SPLIT(dev_priv->dev)) {
1370b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		val = I915_READ(BLC_PWM_PCH_CTL2);
1380b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		if (dev_priv->saveBLC_PWM_CTL2 == 0) {
1390b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			dev_priv->saveBLC_PWM_CTL2 = val;
1400b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		} else if (val == 0) {
1410b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			I915_WRITE(BLC_PWM_PCH_CTL2,
1422aded1b6bb83cabe3ee5763e5c3834e36bf4a61fSimon Que				   dev_priv->saveBLC_PWM_CTL2);
1432aded1b6bb83cabe3ee5763e5c3834e36bf4a61fSimon Que			val = dev_priv->saveBLC_PWM_CTL2;
1440b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		}
1450b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	} else {
1460b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		val = I915_READ(BLC_PWM_CTL);
1470b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		if (dev_priv->saveBLC_PWM_CTL == 0) {
1480b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			dev_priv->saveBLC_PWM_CTL = val;
1490b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
1500b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		} else if (val == 0) {
1510b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			I915_WRITE(BLC_PWM_CTL,
1520b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson				   dev_priv->saveBLC_PWM_CTL);
1530b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			I915_WRITE(BLC_PWM_CTL2,
1540b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson				   dev_priv->saveBLC_PWM_CTL2);
1550b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson			val = dev_priv->saveBLC_PWM_CTL;
1560b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		}
1570b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	}
1580b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson
1590b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	return val;
1600b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson}
1610b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson
162a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilsonu32 intel_panel_get_max_backlight(struct drm_device *dev)
163a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson{
164a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
165a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	u32 max;
166a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
1670b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	max = i915_read_blc_pwm_ctl(dev_priv);
1680b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	if (max == 0) {
1690b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		/* XXX add code here to query mode clock or hardware clock
1700b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		 * and program max PWM appropriately.
1710b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		 */
1720b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		printk_once(KERN_WARNING "fixme: max PWM is zero.\n");
1730b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		return 1;
1740b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson	}
1750b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson
176a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	if (HAS_PCH_SPLIT(dev)) {
1770b0b053a3949f5c467c3b3ba135d4c161f9fbd00Chris Wilson		max >>= 16;
178a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	} else {
179ca88479c1c3b7b1a9f94320745f5331e1de77f80Keith Packard		if (INTEL_INFO(dev)->gen < 4)
180a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson			max >>= 17;
181ca88479c1c3b7b1a9f94320745f5331e1de77f80Keith Packard		else
182a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson			max >>= 16;
183ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
184ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		if (is_backlight_combination_mode(dev))
185ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai			max *= 0xff;
186a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	}
187a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
188a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
189a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	return max;
190a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson}
191a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
192a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilsonu32 intel_panel_get_backlight(struct drm_device *dev)
193a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson{
194a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
195a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	u32 val;
196a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
197a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	if (HAS_PCH_SPLIT(dev)) {
198a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson		val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
199a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	} else {
200a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson		val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
201ca88479c1c3b7b1a9f94320745f5331e1de77f80Keith Packard		if (INTEL_INFO(dev)->gen < 4)
202a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson			val >>= 1;
203ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
2040206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi		if (is_backlight_combination_mode(dev)) {
205ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai			u8 lbpc;
206ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
207ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai			pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
208ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai			val *= lbpc;
209ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		}
210a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	}
211a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
212a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
213a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	return val;
214a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson}
215a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
216a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilsonstatic void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
217a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson{
218a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
219a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
220a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	I915_WRITE(BLC_PWM_CPU_CTL, val | level);
221a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson}
222a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
223f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwaistatic void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
224a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson{
225a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
226a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	u32 tmp;
227a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
228a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
229a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson
230a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	if (HAS_PCH_SPLIT(dev))
231a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson		return intel_pch_panel_set_backlight(dev, level);
232ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
2330206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi	if (is_backlight_combination_mode(dev)) {
234ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		u32 max = intel_panel_get_max_backlight(dev);
235ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		u8 lbpc;
236ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
237ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		lbpc = level * 0xfe / max + 1;
238ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		level /= lbpc;
239ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai		pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
240ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai	}
241ba3820ade317ee36e496b9b40d2ec3987dd4aef0Takashi Iwai
242a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	tmp = I915_READ(BLC_PWM_CTL);
243ca88479c1c3b7b1a9f94320745f5331e1de77f80Keith Packard	if (INTEL_INFO(dev)->gen < 4)
244a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson		level <<= 1;
245ca88479c1c3b7b1a9f94320745f5331e1de77f80Keith Packard	tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
246a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson	I915_WRITE(BLC_PWM_CTL, tmp | level);
247a95735569312f2ab0c80425e2cd1e5cb0b4e1870Chris Wilson}
24847356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
249f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwaivoid intel_panel_set_backlight(struct drm_device *dev, u32 level)
25047356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson{
25147356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
25247356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
253f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	dev_priv->backlight_level = level;
254f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	if (dev_priv->backlight_enabled)
255f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai		intel_panel_actually_set_backlight(dev, level);
256f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai}
257f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai
258f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwaivoid intel_panel_disable_backlight(struct drm_device *dev)
259f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai{
260f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	struct drm_i915_private *dev_priv = dev->dev_private;
26147356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
262f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	dev_priv->backlight_enabled = false;
263f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	intel_panel_actually_set_backlight(dev, 0);
26447356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson}
26547356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
26647356eb67285014527a5ab87543ba1fae3d1e10aChris Wilsonvoid intel_panel_enable_backlight(struct drm_device *dev)
26747356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson{
26847356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
26947356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
27047356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	if (dev_priv->backlight_level == 0)
27147356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson		dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
27247356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
27347356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	dev_priv->backlight_enabled = true;
274f52c619a590fa75276c07dfcaf380dee53e4ea4cTakashi Iwai	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
27547356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson}
27647356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
277aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettstatic void intel_panel_init_backlight(struct drm_device *dev)
27847356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson{
27947356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
28047356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson
281c8303e7f3f3093c16ef0fa5f73280637c89d4368Indan Zupancic	dev_priv->backlight_level = intel_panel_get_backlight(dev);
28247356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson	dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
28347356eb67285014527a5ab87543ba1fae3d1e10aChris Wilson}
284fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson
285fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilsonenum drm_connector_status
286fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilsonintel_panel_detect(struct drm_device *dev)
287fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson{
288bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie#if 0
289fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson	struct drm_i915_private *dev_priv = dev->dev_private;
290bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie#endif
291fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson
292fca874092597ef946b8f07031d8c31c58b212144Chris Wilson	if (i915_panel_ignore_lid)
293fca874092597ef946b8f07031d8c31c58b212144Chris Wilson		return i915_panel_ignore_lid > 0 ?
294fca874092597ef946b8f07031d8c31c58b212144Chris Wilson			connector_status_connected :
295fca874092597ef946b8f07031d8c31c58b212144Chris Wilson			connector_status_disconnected;
296fca874092597ef946b8f07031d8c31c58b212144Chris Wilson
297bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie	/* opregion lid state on HP 2540p is wrong at boot up,
298bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie	 * appears to be either the BIOS or Linux ACPI fault */
299bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie#if 0
300fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson	/* Assume that the BIOS does not lie through the OpRegion... */
301fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson	if (dev_priv->opregion.lid_state)
302fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson		return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
303fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson			connector_status_connected :
304fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson			connector_status_disconnected;
305bcd5023c961a44c7149936553b6929b2b233dd27Dave Airlie#endif
306fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson
307fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson	return connector_status_unknown;
308fe16d949b45036d9f80e20e07bde1ddacc930b10Chris Wilson}
309aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
310aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
311aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettstatic int intel_panel_update_status(struct backlight_device *bd)
312aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
313aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct drm_device *dev = bl_get_data(bd);
314aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	intel_panel_set_backlight(dev, bd->props.brightness);
315aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	return 0;
316aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
317aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
318aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettstatic int intel_panel_get_brightness(struct backlight_device *bd)
319aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
320aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct drm_device *dev = bl_get_data(bd);
32104b38670cf46c096705f24e92a8747d1ab89e53cTakashi Iwai	struct drm_i915_private *dev_priv = dev->dev_private;
32204b38670cf46c096705f24e92a8747d1ab89e53cTakashi Iwai	return dev_priv->backlight_level;
323aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
324aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
325aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettstatic const struct backlight_ops intel_panel_bl_ops = {
326aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	.update_status = intel_panel_update_status,
327aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	.get_brightness = intel_panel_get_brightness,
328aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett};
329aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
330aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettint intel_panel_setup_backlight(struct drm_device *dev)
331aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
332aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct drm_i915_private *dev_priv = dev->dev_private;
333aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct backlight_properties props;
334aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct drm_connector *connector;
335aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
336aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	intel_panel_init_backlight(dev);
337aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
338aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	if (dev_priv->int_lvds_connector)
339aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		connector = dev_priv->int_lvds_connector;
340aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	else if (dev_priv->int_edp_connector)
341aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		connector = dev_priv->int_edp_connector;
342aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	else
343aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		return -ENODEV;
344aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
345aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	props.type = BACKLIGHT_RAW;
346aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	props.max_brightness = intel_panel_get_max_backlight(dev);
347aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	dev_priv->backlight =
348aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		backlight_device_register("intel_backlight",
349aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett					  &connector->kdev, dev,
350aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett					  &intel_panel_bl_ops, &props);
351aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
352aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	if (IS_ERR(dev_priv->backlight)) {
353aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		DRM_ERROR("Failed to register backlight: %ld\n",
354aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett			  PTR_ERR(dev_priv->backlight));
355aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		dev_priv->backlight = NULL;
356aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		return -ENODEV;
357aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	}
358aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
359aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	return 0;
360aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
361aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
362aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettvoid intel_panel_destroy_backlight(struct drm_device *dev)
363aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
364aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	struct drm_i915_private *dev_priv = dev->dev_private;
365aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	if (dev_priv->backlight)
366aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett		backlight_device_unregister(dev_priv->backlight);
367aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
368aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett#else
369aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettint intel_panel_setup_backlight(struct drm_device *dev)
370aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
371aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	intel_panel_init_backlight(dev);
372aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	return 0;
373aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
374aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett
375aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrettvoid intel_panel_destroy_backlight(struct drm_device *dev)
376aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett{
377aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett	return;
378aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett}
379aaa6fd2a004147bf32fce05720938236de3361d9Matthew Garrett#endif
380