Rev 5060 | Rev 5354 | 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 | void |
||
4104 | Serge | 37 | intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, |
2330 | Serge | 38 | struct drm_display_mode *adjusted_mode) |
39 | { |
||
4104 | Serge | 40 | drm_mode_copy(adjusted_mode, fixed_mode); |
2330 | Serge | 41 | |
4104 | Serge | 42 | drm_mode_set_crtcinfo(adjusted_mode, 0); |
2330 | Serge | 43 | } |
44 | |||
5060 | serge | 45 | /** |
46 | * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID |
||
47 | * @dev: drm device |
||
48 | * @fixed_mode : panel native mode |
||
49 | * @connector: LVDS/eDP connector |
||
50 | * |
||
51 | * Return downclock_avail |
||
52 | * Find the reduced downclock for LVDS/eDP in EDID. |
||
53 | */ |
||
54 | struct drm_display_mode * |
||
55 | intel_find_panel_downclock(struct drm_device *dev, |
||
56 | struct drm_display_mode *fixed_mode, |
||
57 | struct drm_connector *connector) |
||
58 | { |
||
59 | struct drm_display_mode *scan, *tmp_mode; |
||
60 | int temp_downclock; |
||
61 | |||
62 | temp_downclock = fixed_mode->clock; |
||
63 | tmp_mode = NULL; |
||
64 | |||
65 | list_for_each_entry(scan, &connector->probed_modes, head) { |
||
66 | /* |
||
67 | * If one mode has the same resolution with the fixed_panel |
||
68 | * mode while they have the different refresh rate, it means |
||
69 | * that the reduced downclock is found. In such |
||
70 | * case we can set the different FPx0/1 to dynamically select |
||
71 | * between low and high frequency. |
||
72 | */ |
||
73 | if (scan->hdisplay == fixed_mode->hdisplay && |
||
74 | scan->hsync_start == fixed_mode->hsync_start && |
||
75 | scan->hsync_end == fixed_mode->hsync_end && |
||
76 | scan->htotal == fixed_mode->htotal && |
||
77 | scan->vdisplay == fixed_mode->vdisplay && |
||
78 | scan->vsync_start == fixed_mode->vsync_start && |
||
79 | scan->vsync_end == fixed_mode->vsync_end && |
||
80 | scan->vtotal == fixed_mode->vtotal) { |
||
81 | if (scan->clock < temp_downclock) { |
||
82 | /* |
||
83 | * The downclock is already found. But we |
||
84 | * expect to find the lower downclock. |
||
85 | */ |
||
86 | temp_downclock = scan->clock; |
||
87 | tmp_mode = scan; |
||
88 | } |
||
89 | } |
||
90 | } |
||
91 | |||
92 | if (temp_downclock < fixed_mode->clock) |
||
93 | return drm_mode_duplicate(dev, tmp_mode); |
||
94 | else |
||
95 | return NULL; |
||
96 | } |
||
97 | |||
2330 | Serge | 98 | /* adjusted_mode has been preset to be the panel's fixed mode */ |
99 | void |
||
4104 | Serge | 100 | intel_pch_panel_fitting(struct intel_crtc *intel_crtc, |
101 | struct intel_crtc_config *pipe_config, |
||
102 | int fitting_mode) |
||
2330 | Serge | 103 | { |
4560 | Serge | 104 | struct drm_display_mode *adjusted_mode; |
2330 | Serge | 105 | int x, y, width, height; |
106 | |||
4104 | Serge | 107 | adjusted_mode = &pipe_config->adjusted_mode; |
108 | |||
2330 | Serge | 109 | x = y = width = height = 0; |
110 | |||
111 | /* Native modes don't need fitting */ |
||
4560 | Serge | 112 | if (adjusted_mode->hdisplay == pipe_config->pipe_src_w && |
113 | adjusted_mode->vdisplay == pipe_config->pipe_src_h) |
||
2330 | Serge | 114 | goto done; |
115 | |||
116 | switch (fitting_mode) { |
||
117 | case DRM_MODE_SCALE_CENTER: |
||
4560 | Serge | 118 | width = pipe_config->pipe_src_w; |
119 | height = pipe_config->pipe_src_h; |
||
2330 | Serge | 120 | x = (adjusted_mode->hdisplay - width + 1)/2; |
121 | y = (adjusted_mode->vdisplay - height + 1)/2; |
||
122 | break; |
||
123 | |||
124 | case DRM_MODE_SCALE_ASPECT: |
||
125 | /* Scale but preserve the aspect ratio */ |
||
126 | { |
||
4560 | Serge | 127 | u32 scaled_width = adjusted_mode->hdisplay |
128 | * pipe_config->pipe_src_h; |
||
129 | u32 scaled_height = pipe_config->pipe_src_w |
||
130 | * adjusted_mode->vdisplay; |
||
2330 | Serge | 131 | if (scaled_width > scaled_height) { /* pillar */ |
4560 | Serge | 132 | width = scaled_height / pipe_config->pipe_src_h; |
2330 | Serge | 133 | if (width & 1) |
134 | width++; |
||
135 | x = (adjusted_mode->hdisplay - width + 1) / 2; |
||
136 | y = 0; |
||
137 | height = adjusted_mode->vdisplay; |
||
138 | } else if (scaled_width < scaled_height) { /* letter */ |
||
4560 | Serge | 139 | height = scaled_width / pipe_config->pipe_src_w; |
2330 | Serge | 140 | if (height & 1) |
141 | height++; |
||
142 | y = (adjusted_mode->vdisplay - height + 1) / 2; |
||
143 | x = 0; |
||
144 | width = adjusted_mode->hdisplay; |
||
145 | } else { |
||
146 | x = y = 0; |
||
147 | width = adjusted_mode->hdisplay; |
||
148 | height = adjusted_mode->vdisplay; |
||
149 | } |
||
150 | } |
||
151 | break; |
||
152 | |||
153 | case DRM_MODE_SCALE_FULLSCREEN: |
||
154 | x = y = 0; |
||
155 | width = adjusted_mode->hdisplay; |
||
156 | height = adjusted_mode->vdisplay; |
||
157 | break; |
||
4104 | Serge | 158 | |
159 | default: |
||
160 | WARN(1, "bad panel fit mode: %d\n", fitting_mode); |
||
161 | return; |
||
2330 | Serge | 162 | } |
163 | |||
164 | done: |
||
4104 | Serge | 165 | pipe_config->pch_pfit.pos = (x << 16) | y; |
166 | pipe_config->pch_pfit.size = (width << 16) | height; |
||
167 | pipe_config->pch_pfit.enabled = pipe_config->pch_pfit.size != 0; |
||
2330 | Serge | 168 | } |
169 | |||
4104 | Serge | 170 | static void |
171 | centre_horizontally(struct drm_display_mode *mode, |
||
172 | int width) |
||
173 | { |
||
174 | u32 border, sync_pos, blank_width, sync_width; |
||
175 | |||
176 | /* keep the hsync and hblank widths constant */ |
||
177 | sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; |
||
178 | blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; |
||
179 | sync_pos = (blank_width - sync_width + 1) / 2; |
||
180 | |||
181 | border = (mode->hdisplay - width + 1) / 2; |
||
182 | border += border & 1; /* make the border even */ |
||
183 | |||
184 | mode->crtc_hdisplay = width; |
||
185 | mode->crtc_hblank_start = width + border; |
||
186 | mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; |
||
187 | |||
188 | mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; |
||
189 | mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; |
||
190 | } |
||
191 | |||
192 | static void |
||
193 | centre_vertically(struct drm_display_mode *mode, |
||
194 | int height) |
||
195 | { |
||
196 | u32 border, sync_pos, blank_width, sync_width; |
||
197 | |||
198 | /* keep the vsync and vblank widths constant */ |
||
199 | sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; |
||
200 | blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; |
||
201 | sync_pos = (blank_width - sync_width + 1) / 2; |
||
202 | |||
203 | border = (mode->vdisplay - height + 1) / 2; |
||
204 | |||
205 | mode->crtc_vdisplay = height; |
||
206 | mode->crtc_vblank_start = height + border; |
||
207 | mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; |
||
208 | |||
209 | mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; |
||
210 | mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; |
||
211 | } |
||
212 | |||
213 | static inline u32 panel_fitter_scaling(u32 source, u32 target) |
||
214 | { |
||
215 | /* |
||
216 | * Floating point operation is not supported. So the FACTOR |
||
217 | * is defined, which can avoid the floating point computation |
||
218 | * when calculating the panel ratio. |
||
219 | */ |
||
220 | #define ACCURACY 12 |
||
221 | #define FACTOR (1 << ACCURACY) |
||
222 | u32 ratio = source * FACTOR / target; |
||
223 | return (FACTOR * ratio + FACTOR/2) / FACTOR; |
||
224 | } |
||
225 | |||
4560 | Serge | 226 | static void i965_scale_aspect(struct intel_crtc_config *pipe_config, |
227 | u32 *pfit_control) |
||
4104 | Serge | 228 | { |
4560 | Serge | 229 | struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; |
4104 | Serge | 230 | u32 scaled_width = adjusted_mode->hdisplay * |
4560 | Serge | 231 | pipe_config->pipe_src_h; |
232 | u32 scaled_height = pipe_config->pipe_src_w * |
||
4104 | Serge | 233 | adjusted_mode->vdisplay; |
234 | |||
235 | /* 965+ is easy, it does everything in hw */ |
||
236 | if (scaled_width > scaled_height) |
||
4560 | Serge | 237 | *pfit_control |= PFIT_ENABLE | |
4104 | Serge | 238 | PFIT_SCALING_PILLAR; |
239 | else if (scaled_width < scaled_height) |
||
4560 | Serge | 240 | *pfit_control |= PFIT_ENABLE | |
4104 | Serge | 241 | PFIT_SCALING_LETTER; |
4560 | Serge | 242 | else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w) |
243 | *pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; |
||
244 | } |
||
245 | |||
246 | static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config, |
||
247 | u32 *pfit_control, u32 *pfit_pgm_ratios, |
||
248 | u32 *border) |
||
249 | { |
||
250 | struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; |
||
4104 | Serge | 251 | u32 scaled_width = adjusted_mode->hdisplay * |
4560 | Serge | 252 | pipe_config->pipe_src_h; |
253 | u32 scaled_height = pipe_config->pipe_src_w * |
||
4104 | Serge | 254 | adjusted_mode->vdisplay; |
4560 | Serge | 255 | u32 bits; |
256 | |||
4104 | Serge | 257 | /* |
258 | * For earlier chips we have to calculate the scaling |
||
259 | * ratio by hand and program it into the |
||
260 | * PFIT_PGM_RATIO register |
||
261 | */ |
||
262 | if (scaled_width > scaled_height) { /* pillar */ |
||
263 | centre_horizontally(adjusted_mode, |
||
264 | scaled_height / |
||
4560 | Serge | 265 | pipe_config->pipe_src_h); |
4104 | Serge | 266 | |
4560 | Serge | 267 | *border = LVDS_BORDER_ENABLE; |
268 | if (pipe_config->pipe_src_h != adjusted_mode->vdisplay) { |
||
269 | bits = panel_fitter_scaling(pipe_config->pipe_src_h, |
||
270 | adjusted_mode->vdisplay); |
||
271 | |||
272 | *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | |
||
4104 | Serge | 273 | bits << PFIT_VERT_SCALE_SHIFT); |
4560 | Serge | 274 | *pfit_control |= (PFIT_ENABLE | |
4104 | Serge | 275 | VERT_INTERP_BILINEAR | |
276 | HORIZ_INTERP_BILINEAR); |
||
277 | } |
||
278 | } else if (scaled_width < scaled_height) { /* letter */ |
||
279 | centre_vertically(adjusted_mode, |
||
280 | scaled_width / |
||
4560 | Serge | 281 | pipe_config->pipe_src_w); |
4104 | Serge | 282 | |
4560 | Serge | 283 | *border = LVDS_BORDER_ENABLE; |
284 | if (pipe_config->pipe_src_w != adjusted_mode->hdisplay) { |
||
285 | bits = panel_fitter_scaling(pipe_config->pipe_src_w, |
||
286 | adjusted_mode->hdisplay); |
||
287 | |||
288 | *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | |
||
4104 | Serge | 289 | bits << PFIT_VERT_SCALE_SHIFT); |
4560 | Serge | 290 | *pfit_control |= (PFIT_ENABLE | |
4104 | Serge | 291 | VERT_INTERP_BILINEAR | |
292 | HORIZ_INTERP_BILINEAR); |
||
293 | } |
||
294 | } else { |
||
295 | /* Aspects match, Let hw scale both directions */ |
||
4560 | Serge | 296 | *pfit_control |= (PFIT_ENABLE | |
4104 | Serge | 297 | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | |
298 | VERT_INTERP_BILINEAR | |
||
299 | HORIZ_INTERP_BILINEAR); |
||
300 | } |
||
4560 | Serge | 301 | } |
302 | |||
303 | void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, |
||
304 | struct intel_crtc_config *pipe_config, |
||
305 | int fitting_mode) |
||
306 | { |
||
307 | struct drm_device *dev = intel_crtc->base.dev; |
||
308 | u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; |
||
309 | struct drm_display_mode *adjusted_mode; |
||
310 | |||
311 | adjusted_mode = &pipe_config->adjusted_mode; |
||
312 | |||
313 | /* Native modes don't need fitting */ |
||
314 | if (adjusted_mode->hdisplay == pipe_config->pipe_src_w && |
||
315 | adjusted_mode->vdisplay == pipe_config->pipe_src_h) |
||
316 | goto out; |
||
317 | |||
318 | switch (fitting_mode) { |
||
319 | case DRM_MODE_SCALE_CENTER: |
||
320 | /* |
||
321 | * For centered modes, we have to calculate border widths & |
||
322 | * heights and modify the values programmed into the CRTC. |
||
323 | */ |
||
324 | centre_horizontally(adjusted_mode, pipe_config->pipe_src_w); |
||
325 | centre_vertically(adjusted_mode, pipe_config->pipe_src_h); |
||
326 | border = LVDS_BORDER_ENABLE; |
||
4104 | Serge | 327 | break; |
4560 | Serge | 328 | case DRM_MODE_SCALE_ASPECT: |
329 | /* Scale but preserve the aspect ratio */ |
||
330 | if (INTEL_INFO(dev)->gen >= 4) |
||
331 | i965_scale_aspect(pipe_config, &pfit_control); |
||
332 | else |
||
333 | i9xx_scale_aspect(pipe_config, &pfit_control, |
||
334 | &pfit_pgm_ratios, &border); |
||
335 | break; |
||
4104 | Serge | 336 | case DRM_MODE_SCALE_FULLSCREEN: |
337 | /* |
||
338 | * Full scaling, even if it changes the aspect ratio. |
||
339 | * Fortunately this is all done for us in hw. |
||
340 | */ |
||
4560 | Serge | 341 | if (pipe_config->pipe_src_h != adjusted_mode->vdisplay || |
342 | pipe_config->pipe_src_w != adjusted_mode->hdisplay) { |
||
4104 | Serge | 343 | pfit_control |= PFIT_ENABLE; |
344 | if (INTEL_INFO(dev)->gen >= 4) |
||
345 | pfit_control |= PFIT_SCALING_AUTO; |
||
346 | else |
||
347 | pfit_control |= (VERT_AUTO_SCALE | |
||
348 | VERT_INTERP_BILINEAR | |
||
349 | HORIZ_AUTO_SCALE | |
||
350 | HORIZ_INTERP_BILINEAR); |
||
351 | } |
||
352 | break; |
||
353 | default: |
||
354 | WARN(1, "bad panel fit mode: %d\n", fitting_mode); |
||
355 | return; |
||
356 | } |
||
357 | |||
358 | /* 965+ wants fuzzy fitting */ |
||
359 | /* FIXME: handle multiple panels by failing gracefully */ |
||
360 | if (INTEL_INFO(dev)->gen >= 4) |
||
361 | pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | |
||
362 | PFIT_FILTER_FUZZY); |
||
363 | |||
364 | out: |
||
365 | if ((pfit_control & PFIT_ENABLE) == 0) { |
||
366 | pfit_control = 0; |
||
367 | pfit_pgm_ratios = 0; |
||
368 | } |
||
369 | |||
370 | /* Make sure pre-965 set dither correctly for 18bpp panels. */ |
||
371 | if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) |
||
372 | pfit_control |= PANEL_8TO6_DITHER_ENABLE; |
||
373 | |||
374 | pipe_config->gmch_pfit.control = pfit_control; |
||
375 | pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; |
||
376 | pipe_config->gmch_pfit.lvds_border_bits = border; |
||
377 | } |
||
378 | |||
5060 | serge | 379 | enum drm_connector_status |
380 | intel_panel_detect(struct drm_device *dev) |
||
381 | { |
||
382 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
383 | |||
384 | /* Assume that the BIOS does not lie through the OpRegion... */ |
||
385 | if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) { |
||
386 | return ioread32(dev_priv->opregion.lid_state) & 0x1 ? |
||
387 | connector_status_connected : |
||
388 | connector_status_disconnected; |
||
389 | } |
||
390 | |||
391 | switch (i915.panel_ignore_lid) { |
||
392 | case -2: |
||
393 | return connector_status_connected; |
||
394 | case -1: |
||
395 | return connector_status_disconnected; |
||
396 | default: |
||
397 | return connector_status_unknown; |
||
398 | } |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * scale - scale values from one range to another |
||
403 | * |
||
404 | * @source_val: value in range [@source_min..@source_max] |
||
405 | * |
||
406 | * Return @source_val in range [@source_min..@source_max] scaled to range |
||
407 | * [@target_min..@target_max]. |
||
408 | */ |
||
409 | static uint32_t scale(uint32_t source_val, |
||
410 | uint32_t source_min, uint32_t source_max, |
||
411 | uint32_t target_min, uint32_t target_max) |
||
412 | { |
||
413 | uint64_t target_val; |
||
414 | |||
415 | WARN_ON(source_min > source_max); |
||
416 | WARN_ON(target_min > target_max); |
||
417 | |||
418 | /* defensive */ |
||
419 | source_val = clamp(source_val, source_min, source_max); |
||
420 | |||
421 | /* avoid overflows */ |
||
422 | target_val = (uint64_t)(source_val - source_min) * |
||
423 | (target_max - target_min); |
||
424 | do_div(target_val, source_max - source_min); |
||
425 | target_val += target_min; |
||
426 | |||
427 | return target_val; |
||
428 | } |
||
429 | |||
430 | /* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */ |
||
431 | static inline u32 scale_user_to_hw(struct intel_connector *connector, |
||
432 | u32 user_level, u32 user_max) |
||
433 | { |
||
434 | struct intel_panel *panel = &connector->panel; |
||
435 | |||
436 | return scale(user_level, 0, user_max, |
||
437 | panel->backlight.min, panel->backlight.max); |
||
438 | } |
||
439 | |||
440 | /* Scale user_level in range [0..user_max] to [0..hw_max], clamping the result |
||
441 | * to [hw_min..hw_max]. */ |
||
442 | static inline u32 clamp_user_to_hw(struct intel_connector *connector, |
||
443 | u32 user_level, u32 user_max) |
||
444 | { |
||
445 | struct intel_panel *panel = &connector->panel; |
||
446 | u32 hw_level; |
||
447 | |||
448 | hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max); |
||
449 | hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max); |
||
450 | |||
451 | return hw_level; |
||
452 | } |
||
453 | |||
454 | /* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */ |
||
455 | static inline u32 scale_hw_to_user(struct intel_connector *connector, |
||
456 | u32 hw_level, u32 user_max) |
||
457 | { |
||
458 | struct intel_panel *panel = &connector->panel; |
||
459 | |||
460 | return scale(hw_level, panel->backlight.min, panel->backlight.max, |
||
461 | 0, user_max); |
||
462 | } |
||
463 | |||
4560 | Serge | 464 | static u32 intel_panel_compute_brightness(struct intel_connector *connector, |
465 | u32 val) |
||
2330 | Serge | 466 | { |
4560 | Serge | 467 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 468 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 469 | struct intel_panel *panel = &connector->panel; |
2330 | Serge | 470 | |
4560 | Serge | 471 | WARN_ON(panel->backlight.max == 0); |
2330 | Serge | 472 | |
5060 | serge | 473 | if (i915.invert_brightness < 0) |
4560 | Serge | 474 | return val; |
2330 | Serge | 475 | |
5060 | serge | 476 | if (i915.invert_brightness > 0 || |
4560 | Serge | 477 | dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { |
478 | return panel->backlight.max - val; |
||
479 | } |
||
480 | |||
481 | return val; |
||
2330 | Serge | 482 | } |
483 | |||
4560 | Serge | 484 | static u32 bdw_get_backlight(struct intel_connector *connector) |
2330 | Serge | 485 | { |
4560 | Serge | 486 | struct drm_device *dev = connector->base.dev; |
3243 | Serge | 487 | struct drm_i915_private *dev_priv = dev->dev_private; |
2330 | Serge | 488 | |
4560 | Serge | 489 | return I915_READ(BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK; |
490 | } |
||
4104 | Serge | 491 | |
4560 | Serge | 492 | static u32 pch_get_backlight(struct intel_connector *connector) |
493 | { |
||
494 | struct drm_device *dev = connector->base.dev; |
||
495 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
2330 | Serge | 496 | |
4560 | Serge | 497 | return I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; |
2330 | Serge | 498 | } |
499 | |||
4560 | Serge | 500 | static u32 i9xx_get_backlight(struct intel_connector *connector) |
2330 | Serge | 501 | { |
4560 | Serge | 502 | struct drm_device *dev = connector->base.dev; |
503 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
504 | struct intel_panel *panel = &connector->panel; |
||
505 | u32 val; |
||
2330 | Serge | 506 | |
4560 | Serge | 507 | val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; |
508 | if (INTEL_INFO(dev)->gen < 4) |
||
509 | val >>= 1; |
||
2330 | Serge | 510 | |
4560 | Serge | 511 | if (panel->backlight.combination_mode) { |
512 | u8 lbpc; |
||
2330 | Serge | 513 | |
4560 | Serge | 514 | pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); |
515 | val *= lbpc; |
||
2330 | Serge | 516 | } |
517 | |||
4560 | Serge | 518 | return val; |
2330 | Serge | 519 | } |
520 | |||
4560 | Serge | 521 | static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe) |
2330 | Serge | 522 | { |
523 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
3031 | serge | 524 | |
4560 | Serge | 525 | return I915_READ(VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK; |
526 | } |
||
3031 | serge | 527 | |
4560 | Serge | 528 | static u32 vlv_get_backlight(struct intel_connector *connector) |
529 | { |
||
530 | struct drm_device *dev = connector->base.dev; |
||
531 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
3031 | serge | 532 | |
4560 | Serge | 533 | return _vlv_get_backlight(dev, pipe); |
3031 | serge | 534 | } |
535 | |||
4560 | Serge | 536 | static u32 intel_panel_get_backlight(struct intel_connector *connector) |
3031 | serge | 537 | { |
4560 | Serge | 538 | struct drm_device *dev = connector->base.dev; |
3031 | serge | 539 | struct drm_i915_private *dev_priv = dev->dev_private; |
2330 | Serge | 540 | u32 val; |
4104 | Serge | 541 | unsigned long flags; |
2330 | Serge | 542 | |
4560 | Serge | 543 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
4104 | Serge | 544 | |
4560 | Serge | 545 | val = dev_priv->display.get_backlight(connector); |
546 | val = intel_panel_compute_brightness(connector, val); |
||
2330 | Serge | 547 | |
4560 | Serge | 548 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
2330 | Serge | 549 | |
550 | DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); |
||
551 | return val; |
||
552 | } |
||
553 | |||
4560 | Serge | 554 | static void bdw_set_backlight(struct intel_connector *connector, u32 level) |
2330 | Serge | 555 | { |
4560 | Serge | 556 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 557 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 558 | u32 val = I915_READ(BLC_PWM_PCH_CTL2) & ~BACKLIGHT_DUTY_CYCLE_MASK; |
559 | I915_WRITE(BLC_PWM_PCH_CTL2, val | level); |
||
2330 | Serge | 560 | } |
561 | |||
4560 | Serge | 562 | static void pch_set_backlight(struct intel_connector *connector, u32 level) |
2330 | Serge | 563 | { |
4560 | Serge | 564 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 565 | struct drm_i915_private *dev_priv = dev->dev_private; |
566 | u32 tmp; |
||
567 | |||
4560 | Serge | 568 | tmp = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; |
569 | I915_WRITE(BLC_PWM_CPU_CTL, tmp | level); |
||
570 | } |
||
2330 | Serge | 571 | |
4560 | Serge | 572 | static void i9xx_set_backlight(struct intel_connector *connector, u32 level) |
573 | { |
||
574 | struct drm_device *dev = connector->base.dev; |
||
575 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
576 | struct intel_panel *panel = &connector->panel; |
||
577 | u32 tmp, mask; |
||
2330 | Serge | 578 | |
4560 | Serge | 579 | WARN_ON(panel->backlight.max == 0); |
580 | |||
581 | if (panel->backlight.combination_mode) { |
||
2330 | Serge | 582 | u8 lbpc; |
583 | |||
4560 | Serge | 584 | lbpc = level * 0xfe / panel->backlight.max + 1; |
2330 | Serge | 585 | level /= lbpc; |
586 | pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc); |
||
587 | } |
||
588 | |||
4560 | Serge | 589 | if (IS_GEN4(dev)) { |
590 | mask = BACKLIGHT_DUTY_CYCLE_MASK; |
||
591 | } else { |
||
2330 | Serge | 592 | level <<= 1; |
4560 | Serge | 593 | mask = BACKLIGHT_DUTY_CYCLE_MASK_PNV; |
594 | } |
||
595 | |||
596 | tmp = I915_READ(BLC_PWM_CTL) & ~mask; |
||
2330 | Serge | 597 | I915_WRITE(BLC_PWM_CTL, tmp | level); |
598 | } |
||
599 | |||
4560 | Serge | 600 | static void vlv_set_backlight(struct intel_connector *connector, u32 level) |
601 | { |
||
602 | struct drm_device *dev = connector->base.dev; |
||
603 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
604 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
605 | u32 tmp; |
||
606 | |||
607 | tmp = I915_READ(VLV_BLC_PWM_CTL(pipe)) & ~BACKLIGHT_DUTY_CYCLE_MASK; |
||
608 | I915_WRITE(VLV_BLC_PWM_CTL(pipe), tmp | level); |
||
609 | } |
||
610 | |||
611 | static void |
||
612 | intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) |
||
613 | { |
||
614 | struct drm_device *dev = connector->base.dev; |
||
615 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
616 | |||
617 | DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); |
||
618 | |||
619 | level = intel_panel_compute_brightness(connector, level); |
||
620 | dev_priv->display.set_backlight(connector, level); |
||
621 | } |
||
622 | |||
5060 | serge | 623 | /* set backlight brightness to level in range [0..max], scaling wrt hw min */ |
624 | static void intel_panel_set_backlight(struct intel_connector *connector, |
||
625 | u32 user_level, u32 user_max) |
||
2342 | Serge | 626 | { |
4560 | Serge | 627 | struct drm_device *dev = connector->base.dev; |
2342 | Serge | 628 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 629 | struct intel_panel *panel = &connector->panel; |
630 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
5060 | serge | 631 | u32 hw_level; |
4560 | Serge | 632 | unsigned long flags; |
2342 | Serge | 633 | |
4560 | Serge | 634 | if (!panel->backlight.present || pipe == INVALID_PIPE) |
635 | return; |
||
3746 | Serge | 636 | |
4560 | Serge | 637 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
638 | |||
639 | WARN_ON(panel->backlight.max == 0); |
||
640 | |||
5060 | serge | 641 | hw_level = scale_user_to_hw(connector, user_level, user_max); |
642 | panel->backlight.level = hw_level; |
||
4560 | Serge | 643 | |
5060 | serge | 644 | if (panel->backlight.enabled) |
645 | intel_panel_actually_set_backlight(connector, hw_level); |
||
4560 | Serge | 646 | |
5060 | serge | 647 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
648 | } |
||
649 | |||
650 | /* set backlight brightness to level in range [0..max], assuming hw min is |
||
651 | * respected. |
||
652 | */ |
||
653 | void intel_panel_set_backlight_acpi(struct intel_connector *connector, |
||
654 | u32 user_level, u32 user_max) |
||
655 | { |
||
656 | struct drm_device *dev = connector->base.dev; |
||
657 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
658 | struct intel_panel *panel = &connector->panel; |
||
659 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
660 | u32 hw_level; |
||
661 | unsigned long flags; |
||
662 | |||
663 | if (!panel->backlight.present || pipe == INVALID_PIPE) |
||
664 | return; |
||
665 | |||
666 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
||
667 | |||
668 | WARN_ON(panel->backlight.max == 0); |
||
669 | |||
670 | hw_level = clamp_user_to_hw(connector, user_level, user_max); |
||
671 | panel->backlight.level = hw_level; |
||
672 | |||
673 | |||
4560 | Serge | 674 | if (panel->backlight.enabled) |
5060 | serge | 675 | intel_panel_actually_set_backlight(connector, hw_level); |
4560 | Serge | 676 | |
677 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
||
2342 | Serge | 678 | } |
679 | |||
4560 | Serge | 680 | static void pch_disable_backlight(struct intel_connector *connector) |
2330 | Serge | 681 | { |
4560 | Serge | 682 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 683 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 684 | u32 tmp; |
685 | |||
686 | intel_panel_actually_set_backlight(connector, 0); |
||
687 | |||
688 | tmp = I915_READ(BLC_PWM_CPU_CTL2); |
||
689 | I915_WRITE(BLC_PWM_CPU_CTL2, tmp & ~BLM_PWM_ENABLE); |
||
690 | |||
691 | tmp = I915_READ(BLC_PWM_PCH_CTL1); |
||
692 | I915_WRITE(BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE); |
||
693 | } |
||
694 | |||
695 | static void i9xx_disable_backlight(struct intel_connector *connector) |
||
696 | { |
||
697 | intel_panel_actually_set_backlight(connector, 0); |
||
698 | } |
||
699 | |||
700 | static void i965_disable_backlight(struct intel_connector *connector) |
||
701 | { |
||
702 | struct drm_device *dev = connector->base.dev; |
||
703 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
704 | u32 tmp; |
||
705 | |||
706 | intel_panel_actually_set_backlight(connector, 0); |
||
707 | |||
708 | tmp = I915_READ(BLC_PWM_CTL2); |
||
709 | I915_WRITE(BLC_PWM_CTL2, tmp & ~BLM_PWM_ENABLE); |
||
710 | } |
||
711 | |||
712 | static void vlv_disable_backlight(struct intel_connector *connector) |
||
713 | { |
||
714 | struct drm_device *dev = connector->base.dev; |
||
715 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
716 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
717 | u32 tmp; |
||
718 | |||
719 | intel_panel_actually_set_backlight(connector, 0); |
||
720 | |||
721 | tmp = I915_READ(VLV_BLC_PWM_CTL2(pipe)); |
||
722 | I915_WRITE(VLV_BLC_PWM_CTL2(pipe), tmp & ~BLM_PWM_ENABLE); |
||
723 | } |
||
724 | |||
725 | void intel_panel_disable_backlight(struct intel_connector *connector) |
||
726 | { |
||
727 | struct drm_device *dev = connector->base.dev; |
||
728 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
729 | struct intel_panel *panel = &connector->panel; |
||
730 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
4104 | Serge | 731 | unsigned long flags; |
2330 | Serge | 732 | |
4560 | Serge | 733 | if (!panel->backlight.present || pipe == INVALID_PIPE) |
734 | return; |
||
735 | |||
4293 | Serge | 736 | /* |
737 | * Do not disable backlight on the vgaswitcheroo path. When switching |
||
738 | * away from i915, the other client may depend on i915 to handle the |
||
739 | * backlight. This will leave the backlight on unnecessarily when |
||
740 | * another client is not activated. |
||
741 | */ |
||
742 | if (dev->switch_power_state == DRM_SWITCH_POWER_CHANGING) { |
||
743 | DRM_DEBUG_DRIVER("Skipping backlight disable on vga switch\n"); |
||
744 | return; |
||
745 | } |
||
746 | |||
4560 | Serge | 747 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
4104 | Serge | 748 | |
4560 | Serge | 749 | panel->backlight.enabled = false; |
750 | dev_priv->display.disable_backlight(connector); |
||
3031 | serge | 751 | |
4560 | Serge | 752 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
753 | } |
||
3031 | serge | 754 | |
4560 | Serge | 755 | static void bdw_enable_backlight(struct intel_connector *connector) |
756 | { |
||
757 | struct drm_device *dev = connector->base.dev; |
||
758 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
759 | struct intel_panel *panel = &connector->panel; |
||
760 | u32 pch_ctl1, pch_ctl2; |
||
3031 | serge | 761 | |
4560 | Serge | 762 | pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1); |
763 | if (pch_ctl1 & BLM_PCH_PWM_ENABLE) { |
||
764 | DRM_DEBUG_KMS("pch backlight already enabled\n"); |
||
765 | pch_ctl1 &= ~BLM_PCH_PWM_ENABLE; |
||
766 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1); |
||
3031 | serge | 767 | } |
4104 | Serge | 768 | |
4560 | Serge | 769 | pch_ctl2 = panel->backlight.max << 16; |
770 | I915_WRITE(BLC_PWM_PCH_CTL2, pch_ctl2); |
||
771 | |||
772 | pch_ctl1 = 0; |
||
773 | if (panel->backlight.active_low_pwm) |
||
774 | pch_ctl1 |= BLM_PCH_POLARITY; |
||
775 | |||
776 | /* BDW always uses the pch pwm controls. */ |
||
777 | pch_ctl1 |= BLM_PCH_OVERRIDE_ENABLE; |
||
778 | |||
779 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1); |
||
780 | POSTING_READ(BLC_PWM_PCH_CTL1); |
||
781 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1 | BLM_PCH_PWM_ENABLE); |
||
782 | |||
783 | /* This won't stick until the above enable. */ |
||
784 | intel_panel_actually_set_backlight(connector, panel->backlight.level); |
||
2330 | Serge | 785 | } |
786 | |||
4560 | Serge | 787 | static void pch_enable_backlight(struct intel_connector *connector) |
2330 | Serge | 788 | { |
4560 | Serge | 789 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 790 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 791 | struct intel_panel *panel = &connector->panel; |
792 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
4104 | Serge | 793 | enum transcoder cpu_transcoder = |
794 | intel_pipe_to_cpu_transcoder(dev_priv, pipe); |
||
4560 | Serge | 795 | u32 cpu_ctl2, pch_ctl1, pch_ctl2; |
2330 | Serge | 796 | |
4560 | Serge | 797 | cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2); |
798 | if (cpu_ctl2 & BLM_PWM_ENABLE) { |
||
5097 | serge | 799 | DRM_DEBUG_KMS("cpu backlight already enabled\n"); |
4560 | Serge | 800 | cpu_ctl2 &= ~BLM_PWM_ENABLE; |
801 | I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2); |
||
802 | } |
||
4104 | Serge | 803 | |
4560 | Serge | 804 | pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1); |
805 | if (pch_ctl1 & BLM_PCH_PWM_ENABLE) { |
||
806 | DRM_DEBUG_KMS("pch backlight already enabled\n"); |
||
807 | pch_ctl1 &= ~BLM_PCH_PWM_ENABLE; |
||
808 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1); |
||
3746 | Serge | 809 | } |
2330 | Serge | 810 | |
4560 | Serge | 811 | if (cpu_transcoder == TRANSCODER_EDP) |
812 | cpu_ctl2 = BLM_TRANSCODER_EDP; |
||
813 | else |
||
814 | cpu_ctl2 = BLM_PIPE(cpu_transcoder); |
||
815 | I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2); |
||
816 | POSTING_READ(BLC_PWM_CPU_CTL2); |
||
817 | I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2 | BLM_PWM_ENABLE); |
||
3031 | serge | 818 | |
4560 | Serge | 819 | /* This won't stick until the above enable. */ |
820 | intel_panel_actually_set_backlight(connector, panel->backlight.level); |
||
3031 | serge | 821 | |
4560 | Serge | 822 | pch_ctl2 = panel->backlight.max << 16; |
823 | I915_WRITE(BLC_PWM_PCH_CTL2, pch_ctl2); |
||
3031 | serge | 824 | |
4560 | Serge | 825 | pch_ctl1 = 0; |
826 | if (panel->backlight.active_low_pwm) |
||
827 | pch_ctl1 |= BLM_PCH_POLARITY; |
||
3031 | serge | 828 | |
4560 | Serge | 829 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1); |
830 | POSTING_READ(BLC_PWM_PCH_CTL1); |
||
831 | I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1 | BLM_PCH_PWM_ENABLE); |
||
832 | } |
||
3031 | serge | 833 | |
4560 | Serge | 834 | static void i9xx_enable_backlight(struct intel_connector *connector) |
835 | { |
||
836 | struct drm_device *dev = connector->base.dev; |
||
837 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
838 | struct intel_panel *panel = &connector->panel; |
||
839 | u32 ctl, freq; |
||
3031 | serge | 840 | |
4560 | Serge | 841 | ctl = I915_READ(BLC_PWM_CTL); |
842 | if (ctl & BACKLIGHT_DUTY_CYCLE_MASK_PNV) { |
||
5097 | serge | 843 | DRM_DEBUG_KMS("backlight already enabled\n"); |
4560 | Serge | 844 | I915_WRITE(BLC_PWM_CTL, 0); |
845 | } |
||
3031 | serge | 846 | |
4560 | Serge | 847 | freq = panel->backlight.max; |
848 | if (panel->backlight.combination_mode) |
||
849 | freq /= 0xff; |
||
3031 | serge | 850 | |
4560 | Serge | 851 | ctl = freq << 17; |
5060 | serge | 852 | if (panel->backlight.combination_mode) |
4560 | Serge | 853 | ctl |= BLM_LEGACY_MODE; |
854 | if (IS_PINEVIEW(dev) && panel->backlight.active_low_pwm) |
||
855 | ctl |= BLM_POLARITY_PNV; |
||
856 | |||
857 | I915_WRITE(BLC_PWM_CTL, ctl); |
||
858 | POSTING_READ(BLC_PWM_CTL); |
||
859 | |||
860 | /* XXX: combine this into above write? */ |
||
861 | intel_panel_actually_set_backlight(connector, panel->backlight.level); |
||
862 | } |
||
863 | |||
864 | static void i965_enable_backlight(struct intel_connector *connector) |
||
865 | { |
||
866 | struct drm_device *dev = connector->base.dev; |
||
867 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
868 | struct intel_panel *panel = &connector->panel; |
||
869 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
870 | u32 ctl, ctl2, freq; |
||
871 | |||
872 | ctl2 = I915_READ(BLC_PWM_CTL2); |
||
873 | if (ctl2 & BLM_PWM_ENABLE) { |
||
5097 | serge | 874 | DRM_DEBUG_KMS("backlight already enabled\n"); |
4560 | Serge | 875 | ctl2 &= ~BLM_PWM_ENABLE; |
876 | I915_WRITE(BLC_PWM_CTL2, ctl2); |
||
3031 | serge | 877 | } |
4560 | Serge | 878 | |
879 | freq = panel->backlight.max; |
||
880 | if (panel->backlight.combination_mode) |
||
881 | freq /= 0xff; |
||
882 | |||
883 | ctl = freq << 16; |
||
884 | I915_WRITE(BLC_PWM_CTL, ctl); |
||
885 | |||
886 | ctl2 = BLM_PIPE(pipe); |
||
887 | if (panel->backlight.combination_mode) |
||
888 | ctl2 |= BLM_COMBINATION_MODE; |
||
889 | if (panel->backlight.active_low_pwm) |
||
890 | ctl2 |= BLM_POLARITY_I965; |
||
891 | I915_WRITE(BLC_PWM_CTL2, ctl2); |
||
892 | POSTING_READ(BLC_PWM_CTL2); |
||
893 | I915_WRITE(BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE); |
||
5060 | serge | 894 | |
895 | intel_panel_actually_set_backlight(connector, panel->backlight.level); |
||
4560 | Serge | 896 | } |
897 | |||
898 | static void vlv_enable_backlight(struct intel_connector *connector) |
||
899 | { |
||
900 | struct drm_device *dev = connector->base.dev; |
||
901 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
902 | struct intel_panel *panel = &connector->panel; |
||
903 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
904 | u32 ctl, ctl2; |
||
905 | |||
906 | ctl2 = I915_READ(VLV_BLC_PWM_CTL2(pipe)); |
||
907 | if (ctl2 & BLM_PWM_ENABLE) { |
||
5097 | serge | 908 | DRM_DEBUG_KMS("backlight already enabled\n"); |
4560 | Serge | 909 | ctl2 &= ~BLM_PWM_ENABLE; |
910 | I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2); |
||
3031 | serge | 911 | } |
912 | |||
4560 | Serge | 913 | ctl = panel->backlight.max << 16; |
914 | I915_WRITE(VLV_BLC_PWM_CTL(pipe), ctl); |
||
4104 | Serge | 915 | |
4560 | Serge | 916 | /* XXX: combine this into above write? */ |
917 | intel_panel_actually_set_backlight(connector, panel->backlight.level); |
||
918 | |||
919 | ctl2 = 0; |
||
920 | if (panel->backlight.active_low_pwm) |
||
921 | ctl2 |= BLM_POLARITY_I965; |
||
922 | I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2); |
||
923 | POSTING_READ(VLV_BLC_PWM_CTL2(pipe)); |
||
924 | I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2 | BLM_PWM_ENABLE); |
||
2330 | Serge | 925 | } |
926 | |||
4560 | Serge | 927 | void intel_panel_enable_backlight(struct intel_connector *connector) |
2330 | Serge | 928 | { |
4560 | Serge | 929 | struct drm_device *dev = connector->base.dev; |
2330 | Serge | 930 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 931 | struct intel_panel *panel = &connector->panel; |
932 | enum pipe pipe = intel_get_pipe_from_connector(connector); |
||
933 | unsigned long flags; |
||
2330 | Serge | 934 | |
4560 | Serge | 935 | if (!panel->backlight.present || pipe == INVALID_PIPE) |
936 | return; |
||
937 | |||
938 | DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe)); |
||
939 | |||
940 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
||
941 | |||
942 | WARN_ON(panel->backlight.max == 0); |
||
943 | |||
944 | if (panel->backlight.level == 0) { |
||
945 | panel->backlight.level = panel->backlight.max; |
||
946 | } |
||
947 | |||
948 | dev_priv->display.enable_backlight(connector); |
||
949 | panel->backlight.enabled = true; |
||
950 | |||
951 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
||
2330 | Serge | 952 | } |
953 | |||
4560 | Serge | 954 | #if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE) |
955 | static int intel_backlight_device_update_status(struct backlight_device *bd) |
||
2330 | Serge | 956 | { |
4560 | Serge | 957 | struct intel_connector *connector = bl_get_data(bd); |
958 | struct drm_device *dev = connector->base.dev; |
||
959 | |||
5060 | serge | 960 | drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); |
4560 | Serge | 961 | DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n", |
962 | bd->props.brightness, bd->props.max_brightness); |
||
963 | intel_panel_set_backlight(connector, bd->props.brightness, |
||
4104 | Serge | 964 | bd->props.max_brightness); |
5060 | serge | 965 | drm_modeset_unlock(&dev->mode_config.connection_mutex); |
2330 | Serge | 966 | return 0; |
967 | } |
||
968 | |||
4560 | Serge | 969 | static int intel_backlight_device_get_brightness(struct backlight_device *bd) |
2330 | Serge | 970 | { |
4560 | Serge | 971 | struct intel_connector *connector = bl_get_data(bd); |
972 | struct drm_device *dev = connector->base.dev; |
||
973 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
5060 | serge | 974 | u32 hw_level; |
4560 | Serge | 975 | int ret; |
976 | |||
977 | intel_runtime_pm_get(dev_priv); |
||
5060 | serge | 978 | drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); |
979 | |||
980 | hw_level = intel_panel_get_backlight(connector); |
||
981 | ret = scale_hw_to_user(connector, hw_level, bd->props.max_brightness); |
||
982 | |||
983 | drm_modeset_unlock(&dev->mode_config.connection_mutex); |
||
4560 | Serge | 984 | intel_runtime_pm_put(dev_priv); |
985 | |||
986 | return ret; |
||
2330 | Serge | 987 | } |
988 | |||
4560 | Serge | 989 | static const struct backlight_ops intel_backlight_device_ops = { |
990 | .update_status = intel_backlight_device_update_status, |
||
991 | .get_brightness = intel_backlight_device_get_brightness, |
||
2330 | Serge | 992 | }; |
993 | |||
4560 | Serge | 994 | static int intel_backlight_device_register(struct intel_connector *connector) |
2330 | Serge | 995 | { |
4560 | Serge | 996 | struct intel_panel *panel = &connector->panel; |
2330 | Serge | 997 | struct backlight_properties props; |
998 | |||
4560 | Serge | 999 | if (WARN_ON(panel->backlight.device)) |
3746 | Serge | 1000 | return -ENODEV; |
1001 | |||
5060 | serge | 1002 | WARN_ON(panel->backlight.max == 0); |
4560 | Serge | 1003 | |
3031 | serge | 1004 | memset(&props, 0, sizeof(props)); |
2330 | Serge | 1005 | props.type = BACKLIGHT_RAW; |
5060 | serge | 1006 | |
1007 | /* |
||
1008 | * Note: Everything should work even if the backlight device max |
||
1009 | * presented to the userspace is arbitrarily chosen. |
||
1010 | */ |
||
4560 | Serge | 1011 | props.max_brightness = panel->backlight.max; |
5060 | serge | 1012 | props.brightness = scale_hw_to_user(connector, |
1013 | panel->backlight.level, |
||
1014 | props.max_brightness); |
||
4104 | Serge | 1015 | |
4560 | Serge | 1016 | /* |
1017 | * Note: using the same name independent of the connector prevents |
||
1018 | * registration of multiple backlight devices in the driver. |
||
1019 | */ |
||
1020 | panel->backlight.device = |
||
2330 | Serge | 1021 | backlight_device_register("intel_backlight", |
4560 | Serge | 1022 | connector->base.kdev, |
1023 | connector, |
||
1024 | &intel_backlight_device_ops, &props); |
||
2330 | Serge | 1025 | |
4560 | Serge | 1026 | if (IS_ERR(panel->backlight.device)) { |
2330 | Serge | 1027 | DRM_ERROR("Failed to register backlight: %ld\n", |
4560 | Serge | 1028 | PTR_ERR(panel->backlight.device)); |
1029 | panel->backlight.device = NULL; |
||
2330 | Serge | 1030 | return -ENODEV; |
1031 | } |
||
1032 | return 0; |
||
1033 | } |
||
1034 | |||
4560 | Serge | 1035 | static void intel_backlight_device_unregister(struct intel_connector *connector) |
2330 | Serge | 1036 | { |
4560 | Serge | 1037 | struct intel_panel *panel = &connector->panel; |
1038 | |||
1039 | if (panel->backlight.device) { |
||
1040 | backlight_device_unregister(panel->backlight.device); |
||
1041 | panel->backlight.device = NULL; |
||
1042 | } |
||
1043 | } |
||
1044 | #else /* CONFIG_BACKLIGHT_CLASS_DEVICE */ |
||
1045 | static int intel_backlight_device_register(struct intel_connector *connector) |
||
1046 | { |
||
1047 | return 0; |
||
1048 | } |
||
1049 | static void intel_backlight_device_unregister(struct intel_connector *connector) |
||
1050 | { |
||
1051 | } |
||
1052 | #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ |
||
1053 | |||
1054 | /* |
||
1055 | * Note: The setup hooks can't assume pipe is set! |
||
1056 | * |
||
1057 | * XXX: Query mode clock or hardware clock and program PWM modulation frequency |
||
1058 | * appropriately when it's 0. Use VBT and/or sane defaults. |
||
1059 | */ |
||
5060 | serge | 1060 | static u32 get_backlight_min_vbt(struct intel_connector *connector) |
1061 | { |
||
1062 | struct drm_device *dev = connector->base.dev; |
||
1063 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1064 | struct intel_panel *panel = &connector->panel; |
||
1065 | |||
1066 | WARN_ON(panel->backlight.max == 0); |
||
1067 | |||
1068 | /* vbt value is a coefficient in range [0..255] */ |
||
1069 | return scale(dev_priv->vbt.backlight.min_brightness, 0, 255, |
||
1070 | 0, panel->backlight.max); |
||
1071 | } |
||
1072 | |||
4560 | Serge | 1073 | static int bdw_setup_backlight(struct intel_connector *connector) |
1074 | { |
||
1075 | struct drm_device *dev = connector->base.dev; |
||
2330 | Serge | 1076 | struct drm_i915_private *dev_priv = dev->dev_private; |
4560 | Serge | 1077 | struct intel_panel *panel = &connector->panel; |
1078 | u32 pch_ctl1, pch_ctl2, val; |
||
1079 | |||
1080 | pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1); |
||
1081 | panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY; |
||
1082 | |||
1083 | pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); |
||
1084 | panel->backlight.max = pch_ctl2 >> 16; |
||
1085 | if (!panel->backlight.max) |
||
1086 | return -ENODEV; |
||
1087 | |||
5060 | serge | 1088 | panel->backlight.min = get_backlight_min_vbt(connector); |
1089 | |||
4560 | Serge | 1090 | val = bdw_get_backlight(connector); |
1091 | panel->backlight.level = intel_panel_compute_brightness(connector, val); |
||
1092 | |||
1093 | panel->backlight.enabled = (pch_ctl1 & BLM_PCH_PWM_ENABLE) && |
||
1094 | panel->backlight.level != 0; |
||
1095 | |||
1096 | return 0; |
||
1097 | } |
||
1098 | |||
1099 | static int pch_setup_backlight(struct intel_connector *connector) |
||
1100 | { |
||
1101 | struct drm_device *dev = connector->base.dev; |
||
1102 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1103 | struct intel_panel *panel = &connector->panel; |
||
1104 | u32 cpu_ctl2, pch_ctl1, pch_ctl2, val; |
||
1105 | |||
1106 | pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1); |
||
1107 | panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY; |
||
1108 | |||
1109 | pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); |
||
1110 | panel->backlight.max = pch_ctl2 >> 16; |
||
1111 | if (!panel->backlight.max) |
||
1112 | return -ENODEV; |
||
1113 | |||
5060 | serge | 1114 | panel->backlight.min = get_backlight_min_vbt(connector); |
1115 | |||
4560 | Serge | 1116 | val = pch_get_backlight(connector); |
1117 | panel->backlight.level = intel_panel_compute_brightness(connector, val); |
||
1118 | |||
1119 | cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2); |
||
1120 | panel->backlight.enabled = (cpu_ctl2 & BLM_PWM_ENABLE) && |
||
1121 | (pch_ctl1 & BLM_PCH_PWM_ENABLE) && panel->backlight.level != 0; |
||
1122 | |||
1123 | return 0; |
||
1124 | } |
||
1125 | |||
1126 | static int i9xx_setup_backlight(struct intel_connector *connector) |
||
1127 | { |
||
1128 | struct drm_device *dev = connector->base.dev; |
||
1129 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1130 | struct intel_panel *panel = &connector->panel; |
||
1131 | u32 ctl, val; |
||
1132 | |||
1133 | ctl = I915_READ(BLC_PWM_CTL); |
||
1134 | |||
5060 | serge | 1135 | if (IS_GEN2(dev) || IS_I915GM(dev) || IS_I945GM(dev)) |
4560 | Serge | 1136 | panel->backlight.combination_mode = ctl & BLM_LEGACY_MODE; |
1137 | |||
1138 | if (IS_PINEVIEW(dev)) |
||
1139 | panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV; |
||
1140 | |||
1141 | panel->backlight.max = ctl >> 17; |
||
1142 | if (panel->backlight.combination_mode) |
||
1143 | panel->backlight.max *= 0xff; |
||
1144 | |||
1145 | if (!panel->backlight.max) |
||
1146 | return -ENODEV; |
||
1147 | |||
5060 | serge | 1148 | panel->backlight.min = get_backlight_min_vbt(connector); |
1149 | |||
4560 | Serge | 1150 | val = i9xx_get_backlight(connector); |
1151 | panel->backlight.level = intel_panel_compute_brightness(connector, val); |
||
1152 | |||
1153 | panel->backlight.enabled = panel->backlight.level != 0; |
||
1154 | |||
1155 | return 0; |
||
1156 | } |
||
1157 | |||
1158 | static int i965_setup_backlight(struct intel_connector *connector) |
||
1159 | { |
||
1160 | struct drm_device *dev = connector->base.dev; |
||
1161 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1162 | struct intel_panel *panel = &connector->panel; |
||
1163 | u32 ctl, ctl2, val; |
||
1164 | |||
1165 | ctl2 = I915_READ(BLC_PWM_CTL2); |
||
1166 | panel->backlight.combination_mode = ctl2 & BLM_COMBINATION_MODE; |
||
1167 | panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965; |
||
1168 | |||
1169 | ctl = I915_READ(BLC_PWM_CTL); |
||
1170 | panel->backlight.max = ctl >> 16; |
||
1171 | if (panel->backlight.combination_mode) |
||
1172 | panel->backlight.max *= 0xff; |
||
1173 | |||
1174 | if (!panel->backlight.max) |
||
1175 | return -ENODEV; |
||
1176 | |||
5060 | serge | 1177 | panel->backlight.min = get_backlight_min_vbt(connector); |
1178 | |||
4560 | Serge | 1179 | val = i9xx_get_backlight(connector); |
1180 | panel->backlight.level = intel_panel_compute_brightness(connector, val); |
||
1181 | |||
1182 | panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) && |
||
1183 | panel->backlight.level != 0; |
||
1184 | |||
1185 | return 0; |
||
1186 | } |
||
1187 | |||
1188 | static int vlv_setup_backlight(struct intel_connector *connector) |
||
1189 | { |
||
1190 | struct drm_device *dev = connector->base.dev; |
||
1191 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1192 | struct intel_panel *panel = &connector->panel; |
||
1193 | enum pipe pipe; |
||
1194 | u32 ctl, ctl2, val; |
||
1195 | |||
1196 | for_each_pipe(pipe) { |
||
1197 | u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe)); |
||
1198 | |||
1199 | /* Skip if the modulation freq is already set */ |
||
1200 | if (cur_val & ~BACKLIGHT_DUTY_CYCLE_MASK) |
||
1201 | continue; |
||
1202 | |||
1203 | cur_val &= BACKLIGHT_DUTY_CYCLE_MASK; |
||
1204 | I915_WRITE(VLV_BLC_PWM_CTL(pipe), (0xf42 << 16) | |
||
1205 | cur_val); |
||
3746 | Serge | 1206 | } |
4560 | Serge | 1207 | |
1208 | ctl2 = I915_READ(VLV_BLC_PWM_CTL2(PIPE_A)); |
||
1209 | panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965; |
||
1210 | |||
1211 | ctl = I915_READ(VLV_BLC_PWM_CTL(PIPE_A)); |
||
1212 | panel->backlight.max = ctl >> 16; |
||
1213 | if (!panel->backlight.max) |
||
1214 | return -ENODEV; |
||
1215 | |||
5060 | serge | 1216 | panel->backlight.min = get_backlight_min_vbt(connector); |
1217 | |||
4560 | Serge | 1218 | val = _vlv_get_backlight(dev, PIPE_A); |
1219 | panel->backlight.level = intel_panel_compute_brightness(connector, val); |
||
1220 | |||
1221 | panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) && |
||
1222 | panel->backlight.level != 0; |
||
1223 | |||
1224 | return 0; |
||
2330 | Serge | 1225 | } |
4560 | Serge | 1226 | |
3243 | Serge | 1227 | int intel_panel_setup_backlight(struct drm_connector *connector) |
2330 | Serge | 1228 | { |
4560 | Serge | 1229 | struct drm_device *dev = connector->dev; |
1230 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1231 | struct intel_connector *intel_connector = to_intel_connector(connector); |
||
1232 | struct intel_panel *panel = &intel_connector->panel; |
||
1233 | unsigned long flags; |
||
1234 | int ret; |
||
1235 | |||
5060 | serge | 1236 | if (!dev_priv->vbt.backlight.present) { |
1237 | if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) { |
||
1238 | DRM_DEBUG_KMS("no backlight present per VBT, but present per quirk\n"); |
||
1239 | } else { |
||
1240 | DRM_DEBUG_KMS("no backlight present per VBT\n"); |
||
1241 | return 0; |
||
1242 | } |
||
1243 | } |
||
1244 | |||
4560 | Serge | 1245 | /* set level and max in panel struct */ |
1246 | spin_lock_irqsave(&dev_priv->backlight_lock, flags); |
||
1247 | ret = dev_priv->display.setup_backlight(intel_connector); |
||
1248 | spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); |
||
1249 | |||
1250 | if (ret) { |
||
1251 | DRM_DEBUG_KMS("failed to setup backlight for connector %s\n", |
||
5060 | serge | 1252 | connector->name); |
4560 | Serge | 1253 | return ret; |
1254 | } |
||
1255 | |||
1256 | intel_backlight_device_register(intel_connector); |
||
1257 | |||
1258 | panel->backlight.present = true; |
||
1259 | |||
1260 | DRM_DEBUG_KMS("backlight initialized, %s, brightness %u/%u, " |
||
1261 | "sysfs interface %sregistered\n", |
||
1262 | panel->backlight.enabled ? "enabled" : "disabled", |
||
1263 | panel->backlight.level, panel->backlight.max, |
||
1264 | panel->backlight.device ? "" : "not "); |
||
1265 | |||
2330 | Serge | 1266 | return 0; |
1267 | } |
||
1268 | |||
4560 | Serge | 1269 | void intel_panel_destroy_backlight(struct drm_connector *connector) |
2330 | Serge | 1270 | { |
4560 | Serge | 1271 | struct intel_connector *intel_connector = to_intel_connector(connector); |
1272 | struct intel_panel *panel = &intel_connector->panel; |
||
1273 | |||
1274 | panel->backlight.present = false; |
||
1275 | intel_backlight_device_unregister(intel_connector); |
||
2330 | Serge | 1276 | } |
3243 | Serge | 1277 | |
4560 | Serge | 1278 | /* Set up chip specific backlight functions */ |
1279 | void intel_panel_init_backlight_funcs(struct drm_device *dev) |
||
1280 | { |
||
1281 | struct drm_i915_private *dev_priv = dev->dev_private; |
||
1282 | |||
1283 | if (IS_BROADWELL(dev)) { |
||
1284 | dev_priv->display.setup_backlight = bdw_setup_backlight; |
||
1285 | dev_priv->display.enable_backlight = bdw_enable_backlight; |
||
1286 | dev_priv->display.disable_backlight = pch_disable_backlight; |
||
1287 | dev_priv->display.set_backlight = bdw_set_backlight; |
||
1288 | dev_priv->display.get_backlight = bdw_get_backlight; |
||
1289 | } else if (HAS_PCH_SPLIT(dev)) { |
||
1290 | dev_priv->display.setup_backlight = pch_setup_backlight; |
||
1291 | dev_priv->display.enable_backlight = pch_enable_backlight; |
||
1292 | dev_priv->display.disable_backlight = pch_disable_backlight; |
||
1293 | dev_priv->display.set_backlight = pch_set_backlight; |
||
1294 | dev_priv->display.get_backlight = pch_get_backlight; |
||
1295 | } else if (IS_VALLEYVIEW(dev)) { |
||
1296 | dev_priv->display.setup_backlight = vlv_setup_backlight; |
||
1297 | dev_priv->display.enable_backlight = vlv_enable_backlight; |
||
1298 | dev_priv->display.disable_backlight = vlv_disable_backlight; |
||
1299 | dev_priv->display.set_backlight = vlv_set_backlight; |
||
1300 | dev_priv->display.get_backlight = vlv_get_backlight; |
||
1301 | } else if (IS_GEN4(dev)) { |
||
1302 | dev_priv->display.setup_backlight = i965_setup_backlight; |
||
1303 | dev_priv->display.enable_backlight = i965_enable_backlight; |
||
1304 | dev_priv->display.disable_backlight = i965_disable_backlight; |
||
1305 | dev_priv->display.set_backlight = i9xx_set_backlight; |
||
1306 | dev_priv->display.get_backlight = i9xx_get_backlight; |
||
1307 | } else { |
||
1308 | dev_priv->display.setup_backlight = i9xx_setup_backlight; |
||
1309 | dev_priv->display.enable_backlight = i9xx_enable_backlight; |
||
1310 | dev_priv->display.disable_backlight = i9xx_disable_backlight; |
||
1311 | dev_priv->display.set_backlight = i9xx_set_backlight; |
||
1312 | dev_priv->display.get_backlight = i9xx_get_backlight; |
||
1313 | } |
||
1314 | } |
||
1315 | |||
3243 | Serge | 1316 | int intel_panel_init(struct intel_panel *panel, |
5060 | serge | 1317 | struct drm_display_mode *fixed_mode, |
1318 | struct drm_display_mode *downclock_mode) |
||
3243 | Serge | 1319 | { |
1320 | panel->fixed_mode = fixed_mode; |
||
5060 | serge | 1321 | panel->downclock_mode = downclock_mode; |
3243 | Serge | 1322 | |
1323 | return 0; |
||
1324 | } |
||
1325 | |||
1326 | void intel_panel_fini(struct intel_panel *panel) |
||
1327 | { |
||
1328 | struct intel_connector *intel_connector = |
||
1329 | container_of(panel, struct intel_connector, panel); |
||
1330 | |||
1331 | if (panel->fixed_mode) |
||
1332 | drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode); |
||
4560 | Serge | 1333 | |
1334 | if (panel->downclock_mode) |
||
1335 | drm_mode_destroy(intel_connector->base.dev, |
||
1336 | panel->downclock_mode); |
||
3243 | Serge | 1337 | }><>><>><>><>><>><>=><=>>>>><>><>><>>><>><>>><>><>><>>>> |