blob: 4e5ff593413d62ea429c4f548c36a421ec2aefd3 [file] [log] [blame]
Chris Wilson1d8e1c72010-08-07 11:01:28 +01001/*
2 * Copyright © 2006-2010 Intel Corporation
3 * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4 *
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:
11 *
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
14 * Software.
15 *
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.
23 *
24 * Authors:
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>
29 */
30
31#include "intel_drv.h"
32
33void
34intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
35 struct drm_display_mode *adjusted_mode)
36{
37 adjusted_mode->hdisplay = fixed_mode->hdisplay;
38 adjusted_mode->hsync_start = fixed_mode->hsync_start;
39 adjusted_mode->hsync_end = fixed_mode->hsync_end;
40 adjusted_mode->htotal = fixed_mode->htotal;
41
42 adjusted_mode->vdisplay = fixed_mode->vdisplay;
43 adjusted_mode->vsync_start = fixed_mode->vsync_start;
44 adjusted_mode->vsync_end = fixed_mode->vsync_end;
45 adjusted_mode->vtotal = fixed_mode->vtotal;
46
47 adjusted_mode->clock = fixed_mode->clock;
48
49 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
50}
51
52/* adjusted_mode has been preset to be the panel's fixed mode */
53void
54intel_pch_panel_fitting(struct drm_device *dev,
55 int fitting_mode,
56 struct drm_display_mode *mode,
57 struct drm_display_mode *adjusted_mode)
58{
59 struct drm_i915_private *dev_priv = dev->dev_private;
60 int x, y, width, height;
61
62 x = y = width = height = 0;
63
64 /* Native modes don't need fitting */
65 if (adjusted_mode->hdisplay == mode->hdisplay &&
66 adjusted_mode->vdisplay == mode->vdisplay)
67 goto done;
68
69 switch (fitting_mode) {
70 case DRM_MODE_SCALE_CENTER:
71 width = mode->hdisplay;
72 height = mode->vdisplay;
73 x = (adjusted_mode->hdisplay - width + 1)/2;
74 y = (adjusted_mode->vdisplay - height + 1)/2;
75 break;
76
77 case DRM_MODE_SCALE_ASPECT:
78 /* Scale but preserve the aspect ratio */
79 {
80 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
81 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
82 if (scaled_width > scaled_height) { /* pillar */
83 width = scaled_height / mode->vdisplay;
84 x = (adjusted_mode->hdisplay - width + 1) / 2;
85 y = 0;
86 height = adjusted_mode->vdisplay;
87 } else if (scaled_width < scaled_height) { /* letter */
88 height = scaled_width / mode->hdisplay;
89 y = (adjusted_mode->vdisplay - height + 1) / 2;
90 x = 0;
91 width = adjusted_mode->hdisplay;
92 } else {
93 x = y = 0;
94 width = adjusted_mode->hdisplay;
95 height = adjusted_mode->vdisplay;
96 }
97 }
98 break;
99
100 default:
101 case DRM_MODE_SCALE_FULLSCREEN:
102 x = y = 0;
103 width = adjusted_mode->hdisplay;
104 height = adjusted_mode->vdisplay;
105 break;
106 }
107
108done:
109 dev_priv->pch_pf_pos = (x << 16) | y;
110 dev_priv->pch_pf_size = (width << 16) | height;
111}
Chris Wilsona9573552010-08-22 13:18:16 +0100112
Chris Wilson0b0b0532010-11-23 09:45:50 +0000113static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
114{
115 u32 val;
116
117 /* Restore the CTL value if it lost, e.g. GPU reset */
118
119 if (HAS_PCH_SPLIT(dev_priv->dev)) {
120 val = I915_READ(BLC_PWM_PCH_CTL2);
121 if (dev_priv->saveBLC_PWM_CTL2 == 0) {
122 dev_priv->saveBLC_PWM_CTL2 = val;
123 } else if (val == 0) {
124 I915_WRITE(BLC_PWM_PCH_CTL2,
125 dev_priv->saveBLC_PWM_CTL);
126 val = dev_priv->saveBLC_PWM_CTL;
127 }
128 } else {
129 val = I915_READ(BLC_PWM_CTL);
130 if (dev_priv->saveBLC_PWM_CTL == 0) {
131 dev_priv->saveBLC_PWM_CTL = val;
132 dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
133 } else if (val == 0) {
134 I915_WRITE(BLC_PWM_CTL,
135 dev_priv->saveBLC_PWM_CTL);
136 I915_WRITE(BLC_PWM_CTL2,
137 dev_priv->saveBLC_PWM_CTL2);
138 val = dev_priv->saveBLC_PWM_CTL;
139 }
140 }
141
142 return val;
143}
144
Chris Wilsona9573552010-08-22 13:18:16 +0100145u32 intel_panel_get_max_backlight(struct drm_device *dev)
146{
147 struct drm_i915_private *dev_priv = dev->dev_private;
148 u32 max;
149
Chris Wilson0b0b0532010-11-23 09:45:50 +0000150 max = i915_read_blc_pwm_ctl(dev_priv);
151 if (max == 0) {
152 /* XXX add code here to query mode clock or hardware clock
153 * and program max PWM appropriately.
154 */
155 printk_once(KERN_WARNING "fixme: max PWM is zero.\n");
156 return 1;
157 }
158
Chris Wilsona9573552010-08-22 13:18:16 +0100159 if (HAS_PCH_SPLIT(dev)) {
Chris Wilson0b0b0532010-11-23 09:45:50 +0000160 max >>= 16;
Chris Wilsona9573552010-08-22 13:18:16 +0100161 } else {
Chris Wilsona9573552010-08-22 13:18:16 +0100162 if (IS_PINEVIEW(dev)) {
163 max >>= 17;
164 } else {
165 max >>= 16;
Chris Wilsona6c45cf2010-09-17 00:32:17 +0100166 if (INTEL_INFO(dev)->gen < 4)
Chris Wilsona9573552010-08-22 13:18:16 +0100167 max &= ~1;
168 }
Chris Wilsona9573552010-08-22 13:18:16 +0100169 }
170
Chris Wilsona9573552010-08-22 13:18:16 +0100171 DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
172 return max;
173}
174
175u32 intel_panel_get_backlight(struct drm_device *dev)
176{
177 struct drm_i915_private *dev_priv = dev->dev_private;
178 u32 val;
179
180 if (HAS_PCH_SPLIT(dev)) {
181 val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
182 } else {
183 val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
184 if (IS_PINEVIEW(dev))
185 val >>= 1;
Chris Wilsona9573552010-08-22 13:18:16 +0100186 }
187
188 DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
189 return val;
190}
191
192static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
193{
194 struct drm_i915_private *dev_priv = dev->dev_private;
195 u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
196 I915_WRITE(BLC_PWM_CPU_CTL, val | level);
197}
198
199void intel_panel_set_backlight(struct drm_device *dev, u32 level)
200{
201 struct drm_i915_private *dev_priv = dev->dev_private;
202 u32 tmp;
203
204 DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
205
206 if (HAS_PCH_SPLIT(dev))
207 return intel_pch_panel_set_backlight(dev, level);
Chris Wilsona9573552010-08-22 13:18:16 +0100208 tmp = I915_READ(BLC_PWM_CTL);
209 if (IS_PINEVIEW(dev)) {
210 tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
211 level <<= 1;
212 } else
213 tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
214 I915_WRITE(BLC_PWM_CTL, tmp | level);
215}
Chris Wilson47356eb2011-01-11 17:06:04 +0000216
217void intel_panel_disable_backlight(struct drm_device *dev)
218{
219 struct drm_i915_private *dev_priv = dev->dev_private;
220
221 if (dev_priv->backlight_enabled) {
222 dev_priv->backlight_level = intel_panel_get_backlight(dev);
223 dev_priv->backlight_enabled = false;
224 }
225
226 intel_panel_set_backlight(dev, 0);
227}
228
229void intel_panel_enable_backlight(struct drm_device *dev)
230{
231 struct drm_i915_private *dev_priv = dev->dev_private;
232
233 if (dev_priv->backlight_level == 0)
234 dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
235
236 intel_panel_set_backlight(dev, dev_priv->backlight_level);
237 dev_priv->backlight_enabled = true;
238}
239
240void intel_panel_setup_backlight(struct drm_device *dev)
241{
242 struct drm_i915_private *dev_priv = dev->dev_private;
243
Indan Zupancicc8303e72011-01-12 11:59:19 +0000244 dev_priv->backlight_level = intel_panel_get_backlight(dev);
Chris Wilson47356eb2011-01-11 17:06:04 +0000245 dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
246}
Chris Wilsonfe16d942011-02-12 10:29:38 +0000247
248enum drm_connector_status
249intel_panel_detect(struct drm_device *dev)
250{
Dave Airliebcd50232011-03-14 14:17:55 +1000251#if 0
Chris Wilsonfe16d942011-02-12 10:29:38 +0000252 struct drm_i915_private *dev_priv = dev->dev_private;
Dave Airliebcd50232011-03-14 14:17:55 +1000253#endif
Chris Wilsonfe16d942011-02-12 10:29:38 +0000254
Chris Wilsonfca87402011-02-17 13:44:48 +0000255 if (i915_panel_ignore_lid)
256 return i915_panel_ignore_lid > 0 ?
257 connector_status_connected :
258 connector_status_disconnected;
259
Dave Airliebcd50232011-03-14 14:17:55 +1000260 /* opregion lid state on HP 2540p is wrong at boot up,
261 * appears to be either the BIOS or Linux ACPI fault */
262#if 0
Chris Wilsonfe16d942011-02-12 10:29:38 +0000263 /* Assume that the BIOS does not lie through the OpRegion... */
264 if (dev_priv->opregion.lid_state)
265 return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
266 connector_status_connected :
267 connector_status_disconnected;
Dave Airliebcd50232011-03-14 14:17:55 +1000268#endif
Chris Wilsonfe16d942011-02-12 10:29:38 +0000269
270 return connector_status_unknown;
271}