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