intel_panel.c revision 254797
1235783Skib/*
2235783Skib * Copyright �� 2006-2010 Intel Corporation
3235783Skib * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4235783Skib *
5235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
6235783Skib * copy of this software and associated documentation files (the "Software"),
7235783Skib * to deal in the Software without restriction, including without limitation
8235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9235783Skib * and/or sell copies of the Software, and to permit persons to whom the
10235783Skib * Software is furnished to do so, subject to the following conditions:
11235783Skib *
12235783Skib * The above copyright notice and this permission notice (including the next
13235783Skib * paragraph) shall be included in all copies or substantial portions of the
14235783Skib * Software.
15235783Skib *
16235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19235783Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20235783Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21235783Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22235783Skib * DEALINGS IN THE SOFTWARE.
23235783Skib *
24235783Skib * Authors:
25235783Skib *	Eric Anholt <eric@anholt.net>
26235783Skib *      Dave Airlie <airlied@linux.ie>
27235783Skib *      Jesse Barnes <jesse.barnes@intel.com>
28235783Skib *      Chris Wilson <chris@chris-wilson.co.uk>
29235783Skib */
30235783Skib
31235783Skib#include <sys/cdefs.h>
32235783Skib__FBSDID("$FreeBSD: head/sys/dev/drm2/i915/intel_panel.c 254797 2013-08-24 16:50:47Z dumbbell $");
33235783Skib
34235783Skib#include <dev/drm2/drmP.h>
35235783Skib#include <dev/drm2/drm.h>
36235783Skib#include <dev/drm2/i915/i915_drm.h>
37235783Skib#include <dev/drm2/i915/intel_drv.h>
38235783Skib
39235783Skib#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
40235783Skib
41235783Skibvoid
42235783Skibintel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
43235783Skib		       struct drm_display_mode *adjusted_mode)
44235783Skib{
45235783Skib	adjusted_mode->hdisplay = fixed_mode->hdisplay;
46235783Skib	adjusted_mode->hsync_start = fixed_mode->hsync_start;
47235783Skib	adjusted_mode->hsync_end = fixed_mode->hsync_end;
48235783Skib	adjusted_mode->htotal = fixed_mode->htotal;
49235783Skib
50235783Skib	adjusted_mode->vdisplay = fixed_mode->vdisplay;
51235783Skib	adjusted_mode->vsync_start = fixed_mode->vsync_start;
52235783Skib	adjusted_mode->vsync_end = fixed_mode->vsync_end;
53235783Skib	adjusted_mode->vtotal = fixed_mode->vtotal;
54235783Skib
55235783Skib	adjusted_mode->clock = fixed_mode->clock;
56235783Skib}
57235783Skib
58235783Skib/* adjusted_mode has been preset to be the panel's fixed mode */
59235783Skibvoid
60235783Skibintel_pch_panel_fitting(struct drm_device *dev,
61235783Skib			int fitting_mode,
62254797Sdumbbell			const struct drm_display_mode *mode,
63235783Skib			struct drm_display_mode *adjusted_mode)
64235783Skib{
65235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
66235783Skib	int x, y, width, height;
67235783Skib
68235783Skib	x = y = width = height = 0;
69235783Skib
70235783Skib	/* Native modes don't need fitting */
71235783Skib	if (adjusted_mode->hdisplay == mode->hdisplay &&
72235783Skib	    adjusted_mode->vdisplay == mode->vdisplay)
73235783Skib		goto done;
74235783Skib
75235783Skib	switch (fitting_mode) {
76235783Skib	case DRM_MODE_SCALE_CENTER:
77235783Skib		width = mode->hdisplay;
78235783Skib		height = mode->vdisplay;
79235783Skib		x = (adjusted_mode->hdisplay - width + 1)/2;
80235783Skib		y = (adjusted_mode->vdisplay - height + 1)/2;
81235783Skib		break;
82235783Skib
83235783Skib	case DRM_MODE_SCALE_ASPECT:
84235783Skib		/* Scale but preserve the aspect ratio */
85235783Skib		{
86235783Skib			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
87235783Skib			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
88235783Skib			if (scaled_width > scaled_height) { /* pillar */
89235783Skib				width = scaled_height / mode->vdisplay;
90235783Skib				if (width & 1)
91235783Skib					width++;
92235783Skib				x = (adjusted_mode->hdisplay - width + 1) / 2;
93235783Skib				y = 0;
94235783Skib				height = adjusted_mode->vdisplay;
95235783Skib			} else if (scaled_width < scaled_height) { /* letter */
96235783Skib				height = scaled_width / mode->hdisplay;
97235783Skib				if (height & 1)
98235783Skib					height++;
99235783Skib				y = (adjusted_mode->vdisplay - height + 1) / 2;
100235783Skib				x = 0;
101235783Skib				width = adjusted_mode->hdisplay;
102235783Skib			} else {
103235783Skib				x = y = 0;
104235783Skib				width = adjusted_mode->hdisplay;
105235783Skib				height = adjusted_mode->vdisplay;
106235783Skib			}
107235783Skib		}
108235783Skib		break;
109235783Skib
110235783Skib	default:
111235783Skib	case DRM_MODE_SCALE_FULLSCREEN:
112235783Skib		x = y = 0;
113235783Skib		width = adjusted_mode->hdisplay;
114235783Skib		height = adjusted_mode->vdisplay;
115235783Skib		break;
116235783Skib	}
117235783Skib
118235783Skibdone:
119235783Skib	dev_priv->pch_pf_pos = (x << 16) | y;
120235783Skib	dev_priv->pch_pf_size = (width << 16) | height;
121235783Skib}
122235783Skib
123235783Skibstatic int is_backlight_combination_mode(struct drm_device *dev)
124235783Skib{
125235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
126235783Skib
127235783Skib	if (INTEL_INFO(dev)->gen >= 4)
128235783Skib		return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
129235783Skib
130235783Skib	if (IS_GEN2(dev))
131235783Skib		return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
132235783Skib
133235783Skib	return 0;
134235783Skib}
135235783Skib
136235783Skibstatic u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
137235783Skib{
138235783Skib	u32 val;
139235783Skib
140235783Skib	/* Restore the CTL value if it lost, e.g. GPU reset */
141235783Skib
142235783Skib	if (HAS_PCH_SPLIT(dev_priv->dev)) {
143235783Skib		val = I915_READ(BLC_PWM_PCH_CTL2);
144235783Skib		if (dev_priv->saveBLC_PWM_CTL2 == 0) {
145235783Skib			dev_priv->saveBLC_PWM_CTL2 = val;
146235783Skib		} else if (val == 0) {
147235783Skib			I915_WRITE(BLC_PWM_PCH_CTL2,
148235783Skib				   dev_priv->saveBLC_PWM_CTL2);
149235783Skib			val = dev_priv->saveBLC_PWM_CTL2;
150235783Skib		}
151235783Skib	} else {
152235783Skib		val = I915_READ(BLC_PWM_CTL);
153235783Skib		if (dev_priv->saveBLC_PWM_CTL == 0) {
154235783Skib			dev_priv->saveBLC_PWM_CTL = val;
155235783Skib			dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
156235783Skib		} else if (val == 0) {
157235783Skib			I915_WRITE(BLC_PWM_CTL,
158235783Skib				   dev_priv->saveBLC_PWM_CTL);
159235783Skib			I915_WRITE(BLC_PWM_CTL2,
160235783Skib				   dev_priv->saveBLC_PWM_CTL2);
161235783Skib			val = dev_priv->saveBLC_PWM_CTL;
162235783Skib		}
163235783Skib	}
164235783Skib
165235783Skib	return val;
166235783Skib}
167235783Skib
168235783Skibu32 intel_panel_get_max_backlight(struct drm_device *dev)
169235783Skib{
170235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
171235783Skib	u32 max;
172235783Skib
173235783Skib	max = i915_read_blc_pwm_ctl(dev_priv);
174235783Skib	if (max == 0) {
175235783Skib		/* XXX add code here to query mode clock or hardware clock
176235783Skib		 * and program max PWM appropriately.
177235783Skib		 */
178235783Skib#if 0
179235783Skib		printf("fixme: max PWM is zero.\n");
180235783Skib#endif
181235783Skib		return 1;
182235783Skib	}
183235783Skib
184235783Skib	if (HAS_PCH_SPLIT(dev)) {
185235783Skib		max >>= 16;
186235783Skib	} else {
187235783Skib		if (INTEL_INFO(dev)->gen < 4)
188235783Skib			max >>= 17;
189235783Skib		else
190235783Skib			max >>= 16;
191235783Skib
192235783Skib		if (is_backlight_combination_mode(dev))
193235783Skib			max *= 0xff;
194235783Skib	}
195235783Skib
196235783Skib	DRM_DEBUG("max backlight PWM = %d\n", max);
197235783Skib	return max;
198235783Skib}
199235783Skib
200235783Skibu32 intel_panel_get_backlight(struct drm_device *dev)
201235783Skib{
202235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
203235783Skib	u32 val;
204235783Skib
205235783Skib	if (HAS_PCH_SPLIT(dev)) {
206235783Skib		val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
207235783Skib	} else {
208235783Skib		val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
209235783Skib		if (INTEL_INFO(dev)->gen < 4)
210235783Skib			val >>= 1;
211235783Skib
212235783Skib		if (is_backlight_combination_mode(dev)) {
213235783Skib			u8 lbpc;
214235783Skib
215235783Skib			lbpc = pci_read_config(dev->device, PCI_LBPC, 1);
216235783Skib			val *= lbpc;
217235783Skib		}
218235783Skib	}
219235783Skib
220235783Skib	DRM_DEBUG("get backlight PWM = %d\n", val);
221235783Skib	return val;
222235783Skib}
223235783Skib
224235783Skibstatic void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
225235783Skib{
226235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
227235783Skib	u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
228235783Skib	I915_WRITE(BLC_PWM_CPU_CTL, val | level);
229235783Skib}
230235783Skib
231235783Skibstatic void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
232235783Skib{
233235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
234235783Skib	u32 tmp;
235235783Skib
236235783Skib	DRM_DEBUG("set backlight PWM = %d\n", level);
237235783Skib
238235783Skib	if (HAS_PCH_SPLIT(dev))
239235783Skib		return intel_pch_panel_set_backlight(dev, level);
240235783Skib
241235783Skib	if (is_backlight_combination_mode(dev)) {
242235783Skib		u32 max = intel_panel_get_max_backlight(dev);
243235783Skib		u8 lbpc;
244235783Skib
245235783Skib		lbpc = level * 0xfe / max + 1;
246235783Skib		level /= lbpc;
247235783Skib		pci_write_config(dev->device, PCI_LBPC, lbpc, 4);
248235783Skib	}
249235783Skib
250235783Skib	tmp = I915_READ(BLC_PWM_CTL);
251235783Skib	if (INTEL_INFO(dev)->gen < 4)
252235783Skib		level <<= 1;
253235783Skib	tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
254235783Skib	I915_WRITE(BLC_PWM_CTL, tmp | level);
255235783Skib}
256235783Skib
257235783Skibvoid intel_panel_set_backlight(struct drm_device *dev, u32 level)
258235783Skib{
259235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
260235783Skib
261235783Skib	dev_priv->backlight_level = level;
262235783Skib	if (dev_priv->backlight_enabled)
263235783Skib		intel_panel_actually_set_backlight(dev, level);
264235783Skib}
265235783Skib
266235783Skibvoid intel_panel_disable_backlight(struct drm_device *dev)
267235783Skib{
268235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
269235783Skib
270235783Skib	dev_priv->backlight_enabled = false;
271235783Skib	intel_panel_actually_set_backlight(dev, 0);
272235783Skib}
273235783Skib
274235783Skibvoid intel_panel_enable_backlight(struct drm_device *dev)
275235783Skib{
276235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
277235783Skib
278235783Skib	if (dev_priv->backlight_level == 0)
279235783Skib		dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
280235783Skib
281235783Skib	dev_priv->backlight_enabled = true;
282235783Skib	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
283235783Skib}
284235783Skib
285235783Skibstatic void intel_panel_init_backlight(struct drm_device *dev)
286235783Skib{
287235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
288235783Skib
289235783Skib	dev_priv->backlight_level = intel_panel_get_backlight(dev);
290235783Skib	dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
291235783Skib}
292235783Skib
293235783Skibenum drm_connector_status
294235783Skibintel_panel_detect(struct drm_device *dev)
295235783Skib{
296235783Skib#if 0
297235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
298235783Skib#endif
299235783Skib
300235783Skib	if (i915_panel_ignore_lid)
301235783Skib		return i915_panel_ignore_lid > 0 ?
302235783Skib			connector_status_connected :
303235783Skib			connector_status_disconnected;
304235783Skib
305235783Skib	/* opregion lid state on HP 2540p is wrong at boot up,
306235783Skib	 * appears to be either the BIOS or Linux ACPI fault */
307235783Skib#if 0
308235783Skib	/* Assume that the BIOS does not lie through the OpRegion... */
309235783Skib	if (dev_priv->opregion.lid_state)
310235783Skib		return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
311235783Skib			connector_status_connected :
312235783Skib			connector_status_disconnected;
313235783Skib#endif
314235783Skib
315235783Skib	return connector_status_unknown;
316235783Skib}
317235783Skib
318235783Skibint intel_panel_setup_backlight(struct drm_device *dev)
319235783Skib{
320235783Skib	intel_panel_init_backlight(dev);
321235783Skib	return 0;
322235783Skib}
323235783Skib
324235783Skibvoid intel_panel_destroy_backlight(struct drm_device *dev)
325235783Skib{
326235783Skib	return;
327235783Skib}
328