Rev 3746 | Rev 4293 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2330 | Serge | 1 | /* |
2 | * Copyright © 2006-2010 Intel Corporation |
||
3 | * Copyright (c) 2006 Dave Airlie |
||
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 |
||
26 | * Dave Airlie |
||
27 | * Jesse Barnes |
||
28 | * Chris Wilson |
||
29 | */ |
||
30 | |||
3031 | serge | 31 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
32 | |||
33 | #include |
||
2330 | Serge | 34 | #include "intel_drv.h" |
35 | |||
36 | #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ |
||
37 | |||
38 | void |
||
4104 | Serge | 39 | intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, |
2330 | Serge | 40 | struct drm_display_mode *adjusted_mode) |
41 | { |
||
4104 | Serge | 42 | drm_mode_copy(adjusted_mode, fixed_mode); |
2330 | Serge | 43 | |
4104 | Serge | 44 | drm_mode_set_crtcinfo(adjusted_mode, 0); |
2330 | Serge | 45 | } |
46 | |||
47 | /* adjusted_mode has been preset to be the panel's fixed mode */ |
||
48 | void |
||
4104 | Serge | 49 | intel_pch_panel_fitting(struct intel_crtc *intel_crtc, |
50 | struct intel_crtc_config *pipe_config, |
||
51 | int fitting_mode) |
||
2330 | Serge | 52 | { |
4104 | Serge | 53 | struct drm_display_mode *mode, *adjusted_mode; |
2330 | Serge | 54 | int x, y, width, height; |
55 | |||
4104 | Serge | 56 | mode = &pipe_config->requested_mode; |
57 | adjusted_mode = &pipe_config->adjusted_mode; |
||
58 | |||
2330 | Serge | 59 | x = y = width = height = 0; |
60 | |||
61 | /* Native modes don't need fitting */ |
||
62 | if (adjusted_mode->hdisplay == mode->hdisplay && |
||
63 | adjusted_mode->vdisplay == mode->vdisplay) |
||
64 | goto done; |
||
65 | |||
66 | switch (fitting_mode) { |
||
67 | case DRM_MODE_SCALE_CENTER: |
||
68 | width = mode->hdisplay; |
||
69 | height = mode->vdisplay; |
||
70 | x = (adjusted_mode->hdisplay - width + 1)/2; |
||
71 | y = (adjusted_mode->vdisplay - height + 1)/2; |
||
72 | break; |
||
73 | |||
74 | case DRM_MODE_SCALE_ASPECT: |
||
75 | /* Scale but preserve the aspect ratio */ |
||
76 | { |
||
77 | u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; |
||
78 | u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; |
||
79 | if (scaled_width > scaled_height) { /* pillar */ |
||
80 | width = scaled_height / mode->vdisplay; |
||
81 | if (width & 1) |
||
82 | width++; |
||
83 | x = (adjusted_mode->hdisplay - width + 1) / 2; |
||
84 | y = 0; |
||
85 | height = adjusted_mode->vdisplay; |
||
86 | } else if (scaled_width < scaled_height) { /* letter */ |
||
87 | height = scaled_width / mode->hdisplay; |
||
88 | if (height & 1) |
||
89 | height++; |
||
90 | y = (adjusted_mode->vdisplay - height + 1) / 2; |
||
91 | x = 0; |
||
92 | width = adjusted_mode->hdisplay; |
||
93 | } else { |
||
94 | x = y = 0; |
||
95 | width = adjusted_mode->hdisplay; |
||
96 | height = adjusted_mode->vdisplay; |
||
97 | } |
||
98 | } |
||
99 | break; |
||
100 | |||
101 | case DRM_MODE_SCALE_FULLSCREEN: |
||
102 | x = y = 0; |
||
103 | width = adjusted_mode->hdisplay; |
||
104 | height = adjusted_mode->vdisplay; |
||
105 | break; |
||
4104 | Serge | 106 | |
107 | default: |
||
108 | WARN(1, "bad panel fit mode: %d\n", fitting_mode); |
||
109 | return; |
||
2330 | Serge | 110 | } |
111 | |||
112 | done: |
||
4104 | Serge | 113 | pipe_config->pch_pfit.pos = (x << 16) | y; |
114 | pipe_config->pch_pfit.size = (width << 16) | height; |
||
115 | pipe_config->pch_pfit.enabled = pipe_config->pch_pfit.size != 0; |
||
2330 | Serge | 116 | } |
117 | |||
4104 | Serge | 118 | static void |
119 | centre_horizontally(struct drm_display_mode *mode, |
||
120 | int width) |
||
121 | { |
||
122 | u32 border, sync_pos, blank_width, sync_width; |
||
123 | |||
124 | /* keep the hsync and hblank widths constant */ |
||
125 | sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; |
||
126 | blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; |
||
127 | sync_pos = (blank_width - sync_width + 1) / 2; |
||
128 | |||
129 | border = (mode->hdisplay - width + 1) / 2; |
||
130 | border += border & 1; /* make the border even */ |
||
131 | |||
132 | mode->crtc_hdisplay = width; |
||
133 | mode->crtc_hblank_start = width + border; |
||
134 | mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; |
||
135 | |||
136 | mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; |
||
137 | mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; |
||
138 | } |
||
139 | |||
140 | static void |
||
141 | centre_vertically(struct drm_display_mode *mode, |
||
142 | int height) |
||
143 | { |
||
144 | u32 border, sync_pos, blank_width, sync_width; |
||
145 | |||
146 | /* keep the vsync and vblank widths constant */ |
||
147 | sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; |
||
148 | blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; |
||
149 | sync_pos = (blank_width - sync_width + 1) / 2; |
||
150 | |||
151 | border = (mode->vdisplay - height + 1) / 2; |
||
152 | |||
153 | mode->crtc_vdisplay = height; |
||
154 | mode->crtc_vblank_start = height + border; |
||
155 | mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; |
||
156 | |||
157 | mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; |
||
158 | mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; |
||
159 | } |
||
160 | |||
161 | static inline u32 panel_fitter_scaling(u32 source, u32 target) |
||
162 | { |
||
163 | /* |
||
164 | * Floating point operation is not supported. So the FACTOR |
||
165 | * is defined, which can avoid the floating point computation |
||
166 | * when calculating the panel ratio. |
||
167 | */ |
||
168 | #define ACCURACY 12 |
||
169 | #define FACTOR (1 << ACCURACY) |
||
170 | u32 ratio = source * FACTOR / target; |
||
171 | return (FACTOR * ratio + FACTOR/2) / FACTOR; |
||
172 | } |
||
173 | |||
174 | void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, |
||
175 | struct intel_crtc_config *pipe_config, |
||
176 | int fitting_mode) |
||
177 | { |
||
178 | struct drm_device *dev = intel_crtc->base.dev; |
||
179 | u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; |
||
180 | struct drm_display_mode *mode, *adjusted_mode; |
||
181 | |||
182 | mode = &pipe_config->requested_mode; |
||
183 | adjusted_mode = &pipe_config->adjusted_mode; |
||
184 | |||
185 | /* Native modes don't need fitting */ |
||
186 | if (adjusted_mode->hdisplay == mode->hdisplay && |
||
187 | adjusted_mode->vdisplay == mode->vdisplay) |
||
188 | goto out; |
||
189 | |||
190 | switch (fitting_mode) { |
||
191 | case DRM_MODE_SCALE_CENTER: |
||
192 | /* |
||
193 | * For centered modes, we have to calculate border widths & |
||
194 | * heights and modify the values programmed into the CRTC. |
||
195 | */ |
||
196 | centre_horizontally(adjusted_mode, mode->hdisplay); |
||
197 | centre_vertically(adjusted_mode, mode->vdisplay); |
||
198 | border = LVDS_BORDER_ENABLE; |
||
199 | break; |
||
200 | case DRM_MODE_SCALE_ASPECT: |
||
201 | /* Scale but preserve the aspect ratio */ |
||
202 | if (INTEL_INFO(dev)->gen >= 4) { |
||
203 | u32 scaled_width = adjusted_mode->hdisplay * |
||
204 | mode->vdisplay; |
||
205 | u32 scaled_height = mode->hdisplay * |
||
206 | adjusted_mode->vdisplay; |
||
207 | |||
208 | /* 965+ is easy, it does everything in hw */ |
||
209 | if (scaled_width > scaled_height) |
||
210 | pfit_control |= PFIT_ENABLE | |
||
211 | PFIT_SCALING_PILLAR; |
||
212 | else if (scaled_width < scaled_height) |
||
213 | pfit_control |= PFIT_ENABLE | |
||
214 | PFIT_SCALING_LETTER; |
||
215 | else if (adjusted_mode->hdisplay != mode->hdisplay) |
||
216 | pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; |
||
217 | } else { |
||
218 | u32 scaled_width = adjusted_mode->hdisplay * |
||
219 | mode->vdisplay; |
||
220 | u32 scaled_height = mode->hdisplay * |
||
221 | adjusted_mode->vdisplay; |
||
222 | /* |
||
223 | * For earlier chips we have to calculate the scaling |
||
224 | * ratio by hand and program it into the |
||
225 | * PFIT_PGM_RATIO register |
||
226 | */ |
||
227 | if (scaled_width > scaled_height) { /* pillar */ |
||
228 | centre_horizontally(adjusted_mode, |
||
229 | scaled_height / |
||
230 | mode->vdisplay); |
||
231 | |||
232 | border = LVDS_BORDER_ENABLE; |
||
233 | if (mode->vdisplay != adjusted_mode->vdisplay) { |
||
234 | u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); |
||
235 | pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | |
||
236 | bits << PFIT_VERT_SCALE_SHIFT); |
||
237 | pfit_control |= (PFIT_ENABLE | |
||
238 | VERT_INTERP_BILINEAR | |
||
239 | HORIZ_INTERP_BILINEAR); |
||
240 | } |
||
241 | } else if (scaled_width < scaled_height) { /* letter */ |
||
242 | centre_vertically(adjusted_mode, |
||
243 | scaled_width / |
||
244 | mode->hdisplay); |
||
245 | |||
246 | border = LVDS_BORDER_ENABLE; |
||
247 | if (mode->hdisplay != adjusted_mode->hdisplay) { |
||
248 | u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); |
||
249 | pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | |
||
250 | bits << PFIT_VERT_SCALE_SHIFT); |
||
251 | pfit_control |= (PFIT_ENABLE | |
||
252 | VERT_INTERP_BILINEAR | |
||
253 | HORIZ_INTERP_BILINEAR); |
||
254 | } |
||
255 | } else { |
||
256 | /* Aspects match, Let hw scale both directions */ |
||
257 | pfit_control |= (PFIT_ENABLE | |
||
258 | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | |
||
259 | VERT_INTERP_BILINEAR | |
||
260 | HORIZ_INTERP_BILINEAR); |
||
261 | } |
||
262 | } |
||
263 | break; |
||
264 | case DRM_MODE_SCALE_FULLSCREEN: |
||
265 | /* |
||
266 | * Full scaling, even if it changes the aspect ratio. |
||
267 | * Fortunately this is all done for us in hw. |
||
268 | */ |
||
269 | if (mode->vdisplay != adjusted_mode->vdisplay || |
||
270 | mode->hdisplay != adjusted_mode->hdisplay) { |
||
271 | pfit_control |= PFIT_ENABLE; |
||
272 | if (INTEL_INFO(dev)->gen >= 4) |
||
273 | pfit_control |= PFIT_SCALING_AUTO; |
||
274 | else |
||
275 | pfit_control |= (VERT_AUTO_SCALE | |
||
276 | VERT_INTERP_BILINEAR | |
||
277 | HORIZ_AUTO_SCALE | |
||
278 | HORIZ_INTERP_BILINEAR); |
||
279 | } |
||
280 | break; |
||
281 | default: |
||
282 | WARN(1, "bad panel fit mode: %d\n", fitting_mode); |
||
283 | return; |
||
284 | } |
||
285 | |||
286 | /* 965+ wants fuzzy fitting */ |
||
287 | /* FIXME: handle multiple panels by failing gracefully */ |
||
288 | if (INTEL_INFO(dev)->gen >= 4) |
||
289 | pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | |
||
290 | PFIT_FILTER_FUZZY); |
||
291 | |||
292 | out: |
||
293 | if ((pfit_control & PFIT_ENABLE) == 0) { |
||
294 | pfit_control = 0; |
||
295 | pfit_pgm_ratios = 0; |
||
296 | } |
||
297 | |||
298 | /* Make sure pre-965 set dither correctly for 18bpp panels. */ |
||
299 | if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) |
||
300 | pfit_control |= PANEL_8TO6_DITHER_ENABLE; |
||
301 | |||
302 | pipe_config->gmch_pfit.control = pfit_control; |
||
303 | pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; |
||
304 | pipe_config->gmch_pfit.lvds_border_bits = border; |
||
305 | } |
||
306 | |||
2330 | Serge | 307 | static int is_backlight_combination_mode(struct drm_device *dev) |
308 | { |
||
309 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
310 | |||
311 | if (INTEL_INFO(dev)->gen >= 4) |
||
312 | return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; |
||
313 | |||
314 | if (IS_GEN2(dev)) |
||
315 | return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; |
||
316 | |||
317 | return 0; |
||
318 | } |
||
319 | |||
4104 | Serge | 320 | /* XXX: query mode clock or hardware clock and program max PWM appropriately |
321 | * when it's 0. |
||
322 | */ |
||
3243 | Serge | 323 | static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) |
2330 | Serge | 324 | { |
3243 | Serge | 325 | struct drm_i915_private *dev_priv = dev->dev_private; |
2330 | Serge | 326 | u32 val; |
327 | |||
4104 | Serge | 328 | // WARN_ON_SMP(!spin_is_locked(&dev_priv->backlight.lock)); |
329 | |||
2330 | Serge | 330 | /* Restore the CTL value if it lost, e.g. GPU reset */ |
331 | |||
332 | if (HAS_PCH_SPLIT(dev_priv->dev)) { |
||
333 | val = I915_READ(BLC_PWM_PCH_CTL2); |
||
3243 | Serge | 334 | if (dev_priv->regfile.saveBLC_PWM_CTL2 == 0) { |
335 | dev_priv->regfile.saveBLC_PWM_CTL2 = val; |
||
2330 | Serge | 336 | } else if (val == 0) { |
3243 | Serge | 337 | val = dev_priv->regfile.saveBLC_PWM_CTL2; |
338 | I915_WRITE(BLC_PWM_PCH_CTL2, val); |
||
2330 | Serge | 339 | } |
340 | } else { |
||
341 | val = I915_READ(BLC_PWM_CTL); |
||
3243 | Serge | 342 | if (dev_priv->regfile.saveBLC_PWM_CTL == 0) { |
343 | dev_priv->regfile.saveBLC_PWM_CTL = val; |
||
344 | if (INTEL_INFO(dev)->gen >= 4) |
||
345 | dev_priv->regfile.saveBLC_PWM_CTL2 = |
||
346 | I915_READ(BLC_PWM_CTL2); |
||
2330 | Serge | 347 | } else if (val == 0) { |
3243 | Serge | 348 | val = dev_priv->regfile.saveBLC_PWM_CTL; |
349 | I915_WRITE(BLC_PWM_CTL, val); |
||
350 | if (INTEL_INFO(dev)->gen >= 4) |
||
2330 | Serge | 351 | I915_WRITE(BLC_PWM_CTL2, |
3243 | Serge | 352 | dev_priv->regfile.saveBLC_PWM_CTL2); |
2330 | Serge | 353 | } |
354 | } |
||
355 | |||
356 | return val; |
||
357 | } |
||
358 | |||
4104 | Serge | 359 | static u32 intel_panel_get_max_backlight(struct drm_device *dev) |
2330 | Serge | 360 | { |
361 | u32 max; |
||
362 | |||
3243 | Serge | 363 | max = i915_read_blc_pwm_ctl(dev); |
2330 | Serge | 364 | |
365 | if (HAS_PCH_SPLIT(dev)) { |
||
366 | max >>= 16; |
||
367 | } else { |
||
2342 | Serge | 368 | if (INTEL_INFO(dev)->gen < 4) |
2330 | Serge | 369 | max >>= 17; |
2342 | Serge | 370 | else |
2330 | Serge | 371 | max >>= 16; |
372 | |||
373 | if (is_backlight_combination_mode(dev)) |
||
374 | max *= 0xff; |
||
375 | } |
||
376 | |||
4104 | Serge | 377 | DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); |
3031 | serge | 378 | |
2330 | Serge | 379 | return max; |
380 | } |
||
381 | |||
3031 | serge | 382 | static int i915_panel_invert_brightness; |
383 | MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness " |
||
384 | "(-1 force normal, 0 machine defaults, 1 force inversion), please " |
||
385 | "report PCI device ID, subsystem vendor and subsystem device ID " |
||
386 | "to dri-devel@lists.freedesktop.org, if your machine needs it. " |
||
387 | "It will then be included in an upcoming module version."); |
||
388 | module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600); |
||
389 | static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val) |
||
2330 | Serge | 390 | { |
391 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
3031 | serge | 392 | |
393 | if (i915_panel_invert_brightness < 0) |
||
394 | return val; |
||
395 | |||
396 | if (i915_panel_invert_brightness > 0 || |
||
4104 | Serge | 397 | dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { |
398 | u32 max = intel_panel_get_max_backlight(dev); |
||
399 | if (max) |
||
400 | return max - val; |
||
401 | } |
||
3031 | serge | 402 | |
403 | return val; |
||
404 | } |
||
405 | |||
406 | static u32 intel_panel_get_backlight(struct drm_device *dev) |
||
407 | { |
||
408 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
2330 | Serge | 409 | u32 val; |
4104 | Serge | 410 | unsigned long flags; |
2330 | Serge | 411 | |
4104 | Serge | 412 | spin_lock_irqsave(&dev_priv->backlight.lock, flags); |
413 | |||
2330 | Serge | 414 | if (HAS_PCH_SPLIT(dev)) { |
415 | val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; |
||
416 | } else { |
||
417 | val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; |
||
2342 | Serge | 418 | if (INTEL_INFO(dev)->gen < 4) |
2330 | Serge | 419 | val >>= 1; |
420 | |||
2342 | Serge | 421 | if (is_backlight_combination_mode(dev)) { |
2330 | Serge | 422 | u8 lbpc; |
423 | |||
424 | pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); |
||
425 | val *= lbpc; |
||
426 | } |
||
427 | } |
||
428 | |||
3031 | serge | 429 | val = intel_panel_compute_brightness(dev, val); |
4104 | Serge | 430 | |
431 | spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); |
||
432 | |||
2330 | Serge | 433 | DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); |
434 | return val; |
||
435 | } |
||
436 | |||
437 | static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) |
||
438 | { |
||
439 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
440 | u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; |
||
441 | I915_WRITE(BLC_PWM_CPU_CTL, val | level); |
||
442 | } |
||
443 | |||
2342 | Serge | 444 | static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level) |
2330 | Serge | 445 | { |
446 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
447 | u32 tmp; |
||
448 | |||
449 | DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); |
||
3031 | serge | 450 | level = intel_panel_compute_brightness(dev, level); |
2330 | Serge | 451 | |
452 | if (HAS_PCH_SPLIT(dev)) |
||
453 | return intel_pch_panel_set_backlight(dev, level); |
||
454 | |||
2342 | Serge | 455 | if (is_backlight_combination_mode(dev)) { |
2330 | Serge | 456 | u32 max = intel_panel_get_max_backlight(dev); |
457 | u8 lbpc; |
||
458 | |||
4104 | Serge | 459 | /* we're screwed, but keep behaviour backwards compatible */ |
460 | if (!max) |
||
461 | max = 1; |
||
462 | |||
2330 | Serge | 463 | lbpc = level * 0xfe / max + 1; |
464 | level /= lbpc; |
||
465 | pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc); |
||
466 | } |
||
467 | |||
468 | tmp = I915_READ(BLC_PWM_CTL); |
||
3031 | serge | 469 | if (INTEL_INFO(dev)->gen < 4) |
2330 | Serge | 470 | level <<= 1; |
471 | tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; |
||
472 | I915_WRITE(BLC_PWM_CTL, tmp | level); |
||
473 | } |
||
474 | |||
4104 | Serge | 475 | /* set backlight brightness to level in range [0..max] */ |
476 | void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max) |
||
2342 | Serge | 477 | { |
478 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
479 | |||
3746 | Serge | 480 | dev_priv->backlight.level = level; |
481 | // if (dev_priv->backlight.device) |
||
482 | // dev_priv->backlight.device->props.brightness = level; |
||
483 | |||
484 | // if (dev_priv->backlight.enabled) |
||
485 | // intel_panel_actually_set_backlight(dev, level); |
||
2342 | Serge | 486 | } |
487 | |||
2330 | Serge | 488 | void intel_panel_disable_backlight(struct drm_device *dev) |
489 | { |
||
490 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
4104 | Serge | 491 | unsigned long flags; |
2330 | Serge | 492 | |
4104 | Serge | 493 | spin_lock_irqsave(&dev_priv->backlight.lock, flags); |
494 | |||
3746 | Serge | 495 | dev_priv->backlight.enabled = false; |
2342 | Serge | 496 | intel_panel_actually_set_backlight(dev, 0); |
3031 | serge | 497 | |
498 | if (INTEL_INFO(dev)->gen >= 4) { |
||
499 | uint32_t reg, tmp; |
||
500 | |||
501 | reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2; |
||
502 | |||
503 | I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE); |
||
504 | |||
505 | if (HAS_PCH_SPLIT(dev)) { |
||
506 | tmp = I915_READ(BLC_PWM_PCH_CTL1); |
||
507 | tmp &= ~BLM_PCH_PWM_ENABLE; |
||
508 | I915_WRITE(BLC_PWM_PCH_CTL1, tmp); |
||
509 | } |
||
510 | } |
||
4104 | Serge | 511 | |
512 | spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); |
||
2330 | Serge | 513 | } |
514 | |||
3031 | serge | 515 | void intel_panel_enable_backlight(struct drm_device *dev, |
516 | enum pipe pipe) |
||
2330 | Serge | 517 | { |
518 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
4104 | Serge | 519 | enum transcoder cpu_transcoder = |
520 | intel_pipe_to_cpu_transcoder(dev_priv, pipe); |
||
521 | unsigned long flags; |
||
2330 | Serge | 522 | |
4104 | Serge | 523 | spin_lock_irqsave(&dev_priv->backlight.lock, flags); |
524 | |||
3746 | Serge | 525 | if (dev_priv->backlight.level == 0) { |
526 | dev_priv->backlight.level = intel_panel_get_max_backlight(dev); |
||
527 | // if (dev_priv->backlight.device) |
||
528 | // dev_priv->backlight.device->props.brightness = |
||
529 | // dev_priv->backlight.level; |
||
530 | } |
||
2330 | Serge | 531 | |
3031 | serge | 532 | if (INTEL_INFO(dev)->gen >= 4) { |
533 | uint32_t reg, tmp; |
||
534 | |||
535 | reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2; |
||
536 | |||
537 | |||
538 | tmp = I915_READ(reg); |
||
539 | |||
540 | /* Note that this can also get called through dpms changes. And |
||
541 | * we don't track the backlight dpms state, hence check whether |
||
542 | * we have to do anything first. */ |
||
543 | if (tmp & BLM_PWM_ENABLE) |
||
544 | goto set_level; |
||
545 | |||
3746 | Serge | 546 | if (INTEL_INFO(dev)->num_pipes == 3) |
3031 | serge | 547 | tmp &= ~BLM_PIPE_SELECT_IVB; |
548 | else |
||
549 | tmp &= ~BLM_PIPE_SELECT; |
||
550 | |||
4104 | Serge | 551 | if (cpu_transcoder == TRANSCODER_EDP) |
552 | tmp |= BLM_TRANSCODER_EDP; |
||
553 | else |
||
554 | tmp |= BLM_PIPE(cpu_transcoder); |
||
3031 | serge | 555 | tmp &= ~BLM_PWM_ENABLE; |
556 | |||
557 | I915_WRITE(reg, tmp); |
||
558 | POSTING_READ(reg); |
||
559 | I915_WRITE(reg, tmp | BLM_PWM_ENABLE); |
||
560 | |||
4104 | Serge | 561 | if (HAS_PCH_SPLIT(dev) && |
562 | !(dev_priv->quirks & QUIRK_NO_PCH_PWM_ENABLE)) { |
||
3031 | serge | 563 | tmp = I915_READ(BLC_PWM_PCH_CTL1); |
564 | tmp |= BLM_PCH_PWM_ENABLE; |
||
565 | tmp &= ~BLM_PCH_OVERRIDE_ENABLE; |
||
566 | I915_WRITE(BLC_PWM_PCH_CTL1, tmp); |
||
567 | } |
||
568 | } |
||
569 | |||
570 | set_level: |
||
571 | /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. |
||
572 | * BLC_PWM_CPU_CTL may be cleared to zero automatically when these |
||
573 | * registers are set. |
||
574 | */ |
||
3746 | Serge | 575 | dev_priv->backlight.enabled = true; |
576 | intel_panel_actually_set_backlight(dev, dev_priv->backlight.level); |
||
4104 | Serge | 577 | |
578 | spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); |
||
2330 | Serge | 579 | } |
580 | |||
581 | static void intel_panel_init_backlight(struct drm_device *dev) |
||
582 | { |
||
583 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
584 | |||
3746 | Serge | 585 | dev_priv->backlight.level = intel_panel_get_backlight(dev); |
586 | dev_priv->backlight.enabled = dev_priv->backlight.level != 0; |
||
2330 | Serge | 587 | } |
588 | |||
589 | enum drm_connector_status |
||
590 | intel_panel_detect(struct drm_device *dev) |
||
591 | { |
||
592 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
593 | |||
594 | /* Assume that the BIOS does not lie through the OpRegion... */ |
||
3243 | Serge | 595 | if (!i915_panel_ignore_lid && dev_priv->opregion.lid_state) { |
2330 | Serge | 596 | return ioread32(dev_priv->opregion.lid_state) & 0x1 ? |
597 | connector_status_connected : |
||
598 | connector_status_disconnected; |
||
3243 | Serge | 599 | } |
2330 | Serge | 600 | |
3243 | Serge | 601 | switch (i915_panel_ignore_lid) { |
602 | case -2: |
||
603 | return connector_status_connected; |
||
604 | case -1: |
||
605 | return connector_status_disconnected; |
||
606 | default: |
||
2330 | Serge | 607 | return connector_status_unknown; |
3243 | Serge | 608 | } |
2330 | Serge | 609 | } |
610 | |||
611 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE |
||
612 | static int intel_panel_update_status(struct backlight_device *bd) |
||
613 | { |
||
614 | struct drm_device *dev = bl_get_data(bd); |
||
4104 | Serge | 615 | intel_panel_set_backlight(dev, bd->props.brightness, |
616 | bd->props.max_brightness); |
||
2330 | Serge | 617 | return 0; |
618 | } |
||
619 | |||
620 | static int intel_panel_get_brightness(struct backlight_device *bd) |
||
621 | { |
||
622 | struct drm_device *dev = bl_get_data(bd); |
||
3746 | Serge | 623 | return intel_panel_get_backlight(dev); |
2330 | Serge | 624 | } |
625 | |||
626 | static const struct backlight_ops intel_panel_bl_ops = { |
||
627 | .update_status = intel_panel_update_status, |
||
628 | .get_brightness = intel_panel_get_brightness, |
||
629 | }; |
||
630 | |||
3243 | Serge | 631 | int intel_panel_setup_backlight(struct drm_connector *connector) |
2330 | Serge | 632 | { |
3243 | Serge | 633 | struct drm_device *dev = connector->dev; |
2330 | Serge | 634 | struct drm_i915_private *dev_priv = dev->dev_private; |
635 | struct backlight_properties props; |
||
4104 | Serge | 636 | unsigned long flags; |
2330 | Serge | 637 | |
638 | intel_panel_init_backlight(dev); |
||
639 | |||
3746 | Serge | 640 | if (WARN_ON(dev_priv->backlight.device)) |
641 | return -ENODEV; |
||
642 | |||
3031 | serge | 643 | memset(&props, 0, sizeof(props)); |
2330 | Serge | 644 | props.type = BACKLIGHT_RAW; |
3746 | Serge | 645 | props.brightness = dev_priv->backlight.level; |
4104 | Serge | 646 | |
647 | spin_lock_irqsave(&dev_priv->backlight.lock, flags); |
||
648 | props.max_brightness = intel_panel_get_max_backlight(dev); |
||
649 | spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); |
||
650 | |||
3031 | serge | 651 | if (props.max_brightness == 0) { |
652 | DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n"); |
||
653 | return -ENODEV; |
||
654 | } |
||
3746 | Serge | 655 | dev_priv->backlight.device = |
2330 | Serge | 656 | backlight_device_register("intel_backlight", |
657 | &connector->kdev, dev, |
||
658 | &intel_panel_bl_ops, &props); |
||
659 | |||
3746 | Serge | 660 | if (IS_ERR(dev_priv->backlight.device)) { |
2330 | Serge | 661 | DRM_ERROR("Failed to register backlight: %ld\n", |
3746 | Serge | 662 | PTR_ERR(dev_priv->backlight.device)); |
663 | dev_priv->backlight.device = NULL; |
||
2330 | Serge | 664 | return -ENODEV; |
665 | } |
||
666 | return 0; |
||
667 | } |
||
668 | |||
669 | void intel_panel_destroy_backlight(struct drm_device *dev) |
||
670 | { |
||
671 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
3746 | Serge | 672 | if (dev_priv->backlight.device) { |
673 | backlight_device_unregister(dev_priv->backlight.device); |
||
674 | dev_priv->backlight.device = NULL; |
||
675 | } |
||
2330 | Serge | 676 | } |
677 | #else |
||
3243 | Serge | 678 | int intel_panel_setup_backlight(struct drm_connector *connector) |
2330 | Serge | 679 | { |
3243 | Serge | 680 | intel_panel_init_backlight(connector->dev); |
2330 | Serge | 681 | return 0; |
682 | } |
||
683 | |||
684 | void intel_panel_destroy_backlight(struct drm_device *dev) |
||
685 | { |
||
686 | return; |
||
687 | } |
||
688 | #endif |
||
3243 | Serge | 689 | |
690 | int intel_panel_init(struct intel_panel *panel, |
||
691 | struct drm_display_mode *fixed_mode) |
||
692 | { |
||
693 | panel->fixed_mode = fixed_mode; |
||
694 | |||
695 | return 0; |
||
696 | } |
||
697 | |||
698 | void intel_panel_fini(struct intel_panel *panel) |
||
699 | { |
||
700 | struct intel_connector *intel_connector = |
||
701 | container_of(panel, struct intel_connector, panel); |
||
702 | |||
703 | if (panel->fixed_mode) |
||
704 | drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode); |
||
705 | }=><=>>>>>>><>><>><>>><>><>>><>><>><>> |