aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_panel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_panel.c')
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index e7f5299d9d57..a06ff07a4d3b 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -30,6 +30,8 @@
30 30
31#include "intel_drv.h" 31#include "intel_drv.h"
32 32
33#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
34
33void 35void
34intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, 36intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
35 struct drm_display_mode *adjusted_mode) 37 struct drm_display_mode *adjusted_mode)
@@ -109,3 +111,197 @@ done:
109 dev_priv->pch_pf_pos = (x << 16) | y; 111 dev_priv->pch_pf_pos = (x << 16) | y;
110 dev_priv->pch_pf_size = (width << 16) | height; 112 dev_priv->pch_pf_size = (width << 16) | height;
111} 113}
114
115static int is_backlight_combination_mode(struct drm_device *dev)
116{
117 struct drm_i915_private *dev_priv = dev->dev_private;
118
119 if (INTEL_INFO(dev)->gen >= 4)
120 return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
121
122 if (IS_GEN2(dev))
123 return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
124
125 return 0;
126}
127
128static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
129{
130 u32 val;
131
132 /* Restore the CTL value if it lost, e.g. GPU reset */
133
134 if (HAS_PCH_SPLIT(dev_priv->dev)) {
135 val = I915_READ(BLC_PWM_PCH_CTL2);
136 if (dev_priv->saveBLC_PWM_CTL2 == 0) {
137 dev_priv->saveBLC_PWM_CTL2 = val;
138 } else if (val == 0) {
139 I915_WRITE(BLC_PWM_PCH_CTL2,
140 dev_priv->saveBLC_PWM_CTL);
141 val = dev_priv->saveBLC_PWM_CTL;
142 }
143 } else {
144 val = I915_READ(BLC_PWM_CTL);
145 if (dev_priv->saveBLC_PWM_CTL == 0) {
146 dev_priv->saveBLC_PWM_CTL = val;
147 dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
148 } else if (val == 0) {
149 I915_WRITE(BLC_PWM_CTL,
150 dev_priv->saveBLC_PWM_CTL);
151 I915_WRITE(BLC_PWM_CTL2,
152 dev_priv->saveBLC_PWM_CTL2);
153 val = dev_priv->saveBLC_PWM_CTL;
154 }
155 }
156
157 return val;
158}
159
160u32 intel_panel_get_max_backlight(struct drm_device *dev)
161{
162 struct drm_i915_private *dev_priv = dev->dev_private;
163 u32 max;
164
165 max = i915_read_blc_pwm_ctl(dev_priv);
166 if (max == 0) {
167 /* XXX add code here to query mode clock or hardware clock
168 * and program max PWM appropriately.
169 */
170 printk_once(KERN_WARNING "fixme: max PWM is zero.\n");
171 return 1;
172 }
173
174 if (HAS_PCH_SPLIT(dev)) {
175 max >>= 16;
176 } else {
177 if (IS_PINEVIEW(dev)) {
178 max >>= 17;
179 } else {
180 max >>= 16;
181 if (INTEL_INFO(dev)->gen < 4)
182 max &= ~1;
183 }
184
185 if (is_backlight_combination_mode(dev))
186 max *= 0xff;
187 }
188
189 DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
190 return max;
191}
192
193u32 intel_panel_get_backlight(struct drm_device *dev)
194{
195 struct drm_i915_private *dev_priv = dev->dev_private;
196 u32 val;
197
198 if (HAS_PCH_SPLIT(dev)) {
199 val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
200 } else {
201 val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
202 if (IS_PINEVIEW(dev))
203 val >>= 1;
204
205 if (is_backlight_combination_mode(dev)){
206 u8 lbpc;
207
208 val &= ~1;
209 pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
210 val *= lbpc;
211 }
212 }
213
214 DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
215 return val;
216}
217
218static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
219{
220 struct drm_i915_private *dev_priv = dev->dev_private;
221 u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
222 I915_WRITE(BLC_PWM_CPU_CTL, val | level);
223}
224
225void intel_panel_set_backlight(struct drm_device *dev, u32 level)
226{
227 struct drm_i915_private *dev_priv = dev->dev_private;
228 u32 tmp;
229
230 DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
231
232 if (HAS_PCH_SPLIT(dev))
233 return intel_pch_panel_set_backlight(dev, level);
234
235 if (is_backlight_combination_mode(dev)){
236 u32 max = intel_panel_get_max_backlight(dev);
237 u8 lbpc;
238
239 lbpc = level * 0xfe / max + 1;
240 level /= lbpc;
241 pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
242 }
243
244 tmp = I915_READ(BLC_PWM_CTL);
245 if (IS_PINEVIEW(dev)) {
246 tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
247 level <<= 1;
248 } else
249 tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
250 I915_WRITE(BLC_PWM_CTL, tmp | level);
251}
252
253void intel_panel_disable_backlight(struct drm_device *dev)
254{
255 struct drm_i915_private *dev_priv = dev->dev_private;
256
257 if (dev_priv->backlight_enabled) {
258 dev_priv->backlight_level = intel_panel_get_backlight(dev);
259 dev_priv->backlight_enabled = false;
260 }
261
262 intel_panel_set_backlight(dev, 0);
263}
264
265void intel_panel_enable_backlight(struct drm_device *dev)
266{
267 struct drm_i915_private *dev_priv = dev->dev_private;
268
269 if (dev_priv->backlight_level == 0)
270 dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
271
272 intel_panel_set_backlight(dev, dev_priv->backlight_level);
273 dev_priv->backlight_enabled = true;
274}
275
276void intel_panel_setup_backlight(struct drm_device *dev)
277{
278 struct drm_i915_private *dev_priv = dev->dev_private;
279
280 dev_priv->backlight_level = intel_panel_get_backlight(dev);
281 dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
282}
283
284enum drm_connector_status
285intel_panel_detect(struct drm_device *dev)
286{
287#if 0
288 struct drm_i915_private *dev_priv = dev->dev_private;
289#endif
290
291 if (i915_panel_ignore_lid)
292 return i915_panel_ignore_lid > 0 ?
293 connector_status_connected :
294 connector_status_disconnected;
295
296 /* opregion lid state on HP 2540p is wrong at boot up,
297 * appears to be either the BIOS or Linux ACPI fault */
298#if 0
299 /* Assume that the BIOS does not lie through the OpRegion... */
300 if (dev_priv->opregion.lid_state)
301 return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
302 connector_status_connected :
303 connector_status_disconnected;
304#endif
305
306 return connector_status_unknown;
307}