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