drm/i915: protect backlight registers and data with a spinlock
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_panel.c
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 "intel_drv.h"
35
36 #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
37
38 void
39 intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
40 struct drm_display_mode *adjusted_mode)
41 {
42 adjusted_mode->hdisplay = fixed_mode->hdisplay;
43 adjusted_mode->hsync_start = fixed_mode->hsync_start;
44 adjusted_mode->hsync_end = fixed_mode->hsync_end;
45 adjusted_mode->htotal = fixed_mode->htotal;
46
47 adjusted_mode->vdisplay = fixed_mode->vdisplay;
48 adjusted_mode->vsync_start = fixed_mode->vsync_start;
49 adjusted_mode->vsync_end = fixed_mode->vsync_end;
50 adjusted_mode->vtotal = fixed_mode->vtotal;
51
52 adjusted_mode->clock = fixed_mode->clock;
53 }
54
55 /* adjusted_mode has been preset to be the panel's fixed mode */
56 void
57 intel_pch_panel_fitting(struct drm_device *dev,
58 int fitting_mode,
59 const struct drm_display_mode *mode,
60 struct drm_display_mode *adjusted_mode)
61 {
62 struct drm_i915_private *dev_priv = dev->dev_private;
63 int x, y, width, height;
64
65 x = y = width = height = 0;
66
67 /* Native modes don't need fitting */
68 if (adjusted_mode->hdisplay == mode->hdisplay &&
69 adjusted_mode->vdisplay == mode->vdisplay)
70 goto done;
71
72 switch (fitting_mode) {
73 case DRM_MODE_SCALE_CENTER:
74 width = mode->hdisplay;
75 height = mode->vdisplay;
76 x = (adjusted_mode->hdisplay - width + 1)/2;
77 y = (adjusted_mode->vdisplay - height + 1)/2;
78 break;
79
80 case DRM_MODE_SCALE_ASPECT:
81 /* Scale but preserve the aspect ratio */
82 {
83 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
84 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
85 if (scaled_width > scaled_height) { /* pillar */
86 width = scaled_height / mode->vdisplay;
87 if (width & 1)
88 width++;
89 x = (adjusted_mode->hdisplay - width + 1) / 2;
90 y = 0;
91 height = adjusted_mode->vdisplay;
92 } else if (scaled_width < scaled_height) { /* letter */
93 height = scaled_width / mode->hdisplay;
94 if (height & 1)
95 height++;
96 y = (adjusted_mode->vdisplay - height + 1) / 2;
97 x = 0;
98 width = adjusted_mode->hdisplay;
99 } else {
100 x = y = 0;
101 width = adjusted_mode->hdisplay;
102 height = adjusted_mode->vdisplay;
103 }
104 }
105 break;
106
107 default:
108 case DRM_MODE_SCALE_FULLSCREEN:
109 x = y = 0;
110 width = adjusted_mode->hdisplay;
111 height = adjusted_mode->vdisplay;
112 break;
113 }
114
115 done:
116 dev_priv->pch_pf_pos = (x << 16) | y;
117 dev_priv->pch_pf_size = (width << 16) | height;
118 }
119
120 static int is_backlight_combination_mode(struct drm_device *dev)
121 {
122 struct drm_i915_private *dev_priv = dev->dev_private;
123
124 if (INTEL_INFO(dev)->gen >= 4)
125 return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
126
127 if (IS_GEN2(dev))
128 return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
129
130 return 0;
131 }
132
133 /* XXX: query mode clock or hardware clock and program max PWM appropriately
134 * when it's 0.
135 */
136 static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
137 {
138 struct drm_i915_private *dev_priv = dev->dev_private;
139 u32 val;
140
141 WARN_ON(!spin_is_locked(&dev_priv->backlight.lock));
142
143 /* Restore the CTL value if it lost, e.g. GPU reset */
144
145 if (HAS_PCH_SPLIT(dev_priv->dev)) {
146 val = I915_READ(BLC_PWM_PCH_CTL2);
147 if (dev_priv->regfile.saveBLC_PWM_CTL2 == 0) {
148 dev_priv->regfile.saveBLC_PWM_CTL2 = val;
149 } else if (val == 0) {
150 val = dev_priv->regfile.saveBLC_PWM_CTL2;
151 I915_WRITE(BLC_PWM_PCH_CTL2, val);
152 }
153 } else {
154 val = I915_READ(BLC_PWM_CTL);
155 if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
156 dev_priv->regfile.saveBLC_PWM_CTL = val;
157 if (INTEL_INFO(dev)->gen >= 4)
158 dev_priv->regfile.saveBLC_PWM_CTL2 =
159 I915_READ(BLC_PWM_CTL2);
160 } else if (val == 0) {
161 val = dev_priv->regfile.saveBLC_PWM_CTL;
162 I915_WRITE(BLC_PWM_CTL, val);
163 if (INTEL_INFO(dev)->gen >= 4)
164 I915_WRITE(BLC_PWM_CTL2,
165 dev_priv->regfile.saveBLC_PWM_CTL2);
166 }
167 }
168
169 return val;
170 }
171
172 static u32 intel_panel_get_max_backlight(struct drm_device *dev)
173 {
174 u32 max;
175
176 max = i915_read_blc_pwm_ctl(dev);
177
178 if (HAS_PCH_SPLIT(dev)) {
179 max >>= 16;
180 } else {
181 if (INTEL_INFO(dev)->gen < 4)
182 max >>= 17;
183 else
184 max >>= 16;
185
186 if (is_backlight_combination_mode(dev))
187 max *= 0xff;
188 }
189
190 DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
191
192 return max;
193 }
194
195 static int i915_panel_invert_brightness;
196 MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
197 "(-1 force normal, 0 machine defaults, 1 force inversion), please "
198 "report PCI device ID, subsystem vendor and subsystem device ID "
199 "to dri-devel@lists.freedesktop.org, if your machine needs it. "
200 "It will then be included in an upcoming module version.");
201 module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
202 static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
203 {
204 struct drm_i915_private *dev_priv = dev->dev_private;
205
206 if (i915_panel_invert_brightness < 0)
207 return val;
208
209 if (i915_panel_invert_brightness > 0 ||
210 dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
211 u32 max = intel_panel_get_max_backlight(dev);
212 if (max)
213 return max - val;
214 }
215
216 return val;
217 }
218
219 static u32 intel_panel_get_backlight(struct drm_device *dev)
220 {
221 struct drm_i915_private *dev_priv = dev->dev_private;
222 u32 val;
223 unsigned long flags;
224
225 spin_lock_irqsave(&dev_priv->backlight.lock, flags);
226
227 if (HAS_PCH_SPLIT(dev)) {
228 val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
229 } else {
230 val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
231 if (INTEL_INFO(dev)->gen < 4)
232 val >>= 1;
233
234 if (is_backlight_combination_mode(dev)) {
235 u8 lbpc;
236
237 pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
238 val *= lbpc;
239 }
240 }
241
242 val = intel_panel_compute_brightness(dev, val);
243
244 spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
245
246 DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
247 return val;
248 }
249
250 static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
251 {
252 struct drm_i915_private *dev_priv = dev->dev_private;
253 u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
254 I915_WRITE(BLC_PWM_CPU_CTL, val | level);
255 }
256
257 static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
258 {
259 struct drm_i915_private *dev_priv = dev->dev_private;
260 u32 tmp;
261
262 DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
263 level = intel_panel_compute_brightness(dev, level);
264
265 if (HAS_PCH_SPLIT(dev))
266 return intel_pch_panel_set_backlight(dev, level);
267
268 if (is_backlight_combination_mode(dev)) {
269 u32 max = intel_panel_get_max_backlight(dev);
270 u8 lbpc;
271
272 /* we're screwed, but keep behaviour backwards compatible */
273 if (!max)
274 max = 1;
275
276 lbpc = level * 0xfe / max + 1;
277 level /= lbpc;
278 pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
279 }
280
281 tmp = I915_READ(BLC_PWM_CTL);
282 if (INTEL_INFO(dev)->gen < 4)
283 level <<= 1;
284 tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
285 I915_WRITE(BLC_PWM_CTL, tmp | level);
286 }
287
288 /* set backlight brightness to level in range [0..max] */
289 void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
290 {
291 struct drm_i915_private *dev_priv = dev->dev_private;
292 u32 freq;
293 unsigned long flags;
294
295 spin_lock_irqsave(&dev_priv->backlight.lock, flags);
296
297 freq = intel_panel_get_max_backlight(dev);
298 if (!freq) {
299 /* we are screwed, bail out */
300 goto out;
301 }
302
303 /* scale to hardware */
304 level = level * freq / max;
305
306 dev_priv->backlight.level = level;
307 if (dev_priv->backlight.device)
308 dev_priv->backlight.device->props.brightness = level;
309
310 if (dev_priv->backlight.enabled)
311 intel_panel_actually_set_backlight(dev, level);
312 out:
313 spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
314 }
315
316 void intel_panel_disable_backlight(struct drm_device *dev)
317 {
318 struct drm_i915_private *dev_priv = dev->dev_private;
319 unsigned long flags;
320
321 spin_lock_irqsave(&dev_priv->backlight.lock, flags);
322
323 dev_priv->backlight.enabled = false;
324 intel_panel_actually_set_backlight(dev, 0);
325
326 if (INTEL_INFO(dev)->gen >= 4) {
327 uint32_t reg, tmp;
328
329 reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
330
331 I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE);
332
333 if (HAS_PCH_SPLIT(dev)) {
334 tmp = I915_READ(BLC_PWM_PCH_CTL1);
335 tmp &= ~BLM_PCH_PWM_ENABLE;
336 I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
337 }
338 }
339
340 spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
341 }
342
343 void intel_panel_enable_backlight(struct drm_device *dev,
344 enum pipe pipe)
345 {
346 struct drm_i915_private *dev_priv = dev->dev_private;
347 unsigned long flags;
348
349 spin_lock_irqsave(&dev_priv->backlight.lock, flags);
350
351 if (dev_priv->backlight.level == 0) {
352 dev_priv->backlight.level = intel_panel_get_max_backlight(dev);
353 if (dev_priv->backlight.device)
354 dev_priv->backlight.device->props.brightness =
355 dev_priv->backlight.level;
356 }
357
358 if (INTEL_INFO(dev)->gen >= 4) {
359 uint32_t reg, tmp;
360
361 reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
362
363
364 tmp = I915_READ(reg);
365
366 /* Note that this can also get called through dpms changes. And
367 * we don't track the backlight dpms state, hence check whether
368 * we have to do anything first. */
369 if (tmp & BLM_PWM_ENABLE)
370 goto set_level;
371
372 if (INTEL_INFO(dev)->num_pipes == 3)
373 tmp &= ~BLM_PIPE_SELECT_IVB;
374 else
375 tmp &= ~BLM_PIPE_SELECT;
376
377 tmp |= BLM_PIPE(pipe);
378 tmp &= ~BLM_PWM_ENABLE;
379
380 I915_WRITE(reg, tmp);
381 POSTING_READ(reg);
382 I915_WRITE(reg, tmp | BLM_PWM_ENABLE);
383
384 if (HAS_PCH_SPLIT(dev)) {
385 tmp = I915_READ(BLC_PWM_PCH_CTL1);
386 tmp |= BLM_PCH_PWM_ENABLE;
387 tmp &= ~BLM_PCH_OVERRIDE_ENABLE;
388 I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
389 }
390 }
391
392 set_level:
393 /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
394 * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
395 * registers are set.
396 */
397 dev_priv->backlight.enabled = true;
398 intel_panel_actually_set_backlight(dev, dev_priv->backlight.level);
399
400 spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
401 }
402
403 static void intel_panel_init_backlight(struct drm_device *dev)
404 {
405 struct drm_i915_private *dev_priv = dev->dev_private;
406
407 dev_priv->backlight.level = intel_panel_get_backlight(dev);
408 dev_priv->backlight.enabled = dev_priv->backlight.level != 0;
409 }
410
411 enum drm_connector_status
412 intel_panel_detect(struct drm_device *dev)
413 {
414 struct drm_i915_private *dev_priv = dev->dev_private;
415
416 /* Assume that the BIOS does not lie through the OpRegion... */
417 if (!i915_panel_ignore_lid && dev_priv->opregion.lid_state) {
418 return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
419 connector_status_connected :
420 connector_status_disconnected;
421 }
422
423 switch (i915_panel_ignore_lid) {
424 case -2:
425 return connector_status_connected;
426 case -1:
427 return connector_status_disconnected;
428 default:
429 return connector_status_unknown;
430 }
431 }
432
433 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
434 static int intel_panel_update_status(struct backlight_device *bd)
435 {
436 struct drm_device *dev = bl_get_data(bd);
437 intel_panel_set_backlight(dev, bd->props.brightness,
438 bd->props.max_brightness);
439 return 0;
440 }
441
442 static int intel_panel_get_brightness(struct backlight_device *bd)
443 {
444 struct drm_device *dev = bl_get_data(bd);
445 return intel_panel_get_backlight(dev);
446 }
447
448 static const struct backlight_ops intel_panel_bl_ops = {
449 .update_status = intel_panel_update_status,
450 .get_brightness = intel_panel_get_brightness,
451 };
452
453 int intel_panel_setup_backlight(struct drm_connector *connector)
454 {
455 struct drm_device *dev = connector->dev;
456 struct drm_i915_private *dev_priv = dev->dev_private;
457 struct backlight_properties props;
458 unsigned long flags;
459
460 intel_panel_init_backlight(dev);
461
462 if (WARN_ON(dev_priv->backlight.device))
463 return -ENODEV;
464
465 memset(&props, 0, sizeof(props));
466 props.type = BACKLIGHT_RAW;
467 props.brightness = dev_priv->backlight.level;
468
469 spin_lock_irqsave(&dev_priv->backlight.lock, flags);
470 props.max_brightness = intel_panel_get_max_backlight(dev);
471 spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
472
473 if (props.max_brightness == 0) {
474 DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n");
475 return -ENODEV;
476 }
477 dev_priv->backlight.device =
478 backlight_device_register("intel_backlight",
479 &connector->kdev, dev,
480 &intel_panel_bl_ops, &props);
481
482 if (IS_ERR(dev_priv->backlight.device)) {
483 DRM_ERROR("Failed to register backlight: %ld\n",
484 PTR_ERR(dev_priv->backlight.device));
485 dev_priv->backlight.device = NULL;
486 return -ENODEV;
487 }
488 return 0;
489 }
490
491 void intel_panel_destroy_backlight(struct drm_device *dev)
492 {
493 struct drm_i915_private *dev_priv = dev->dev_private;
494 if (dev_priv->backlight.device) {
495 backlight_device_unregister(dev_priv->backlight.device);
496 dev_priv->backlight.device = NULL;
497 }
498 }
499 #else
500 int intel_panel_setup_backlight(struct drm_connector *connector)
501 {
502 intel_panel_init_backlight(connector->dev);
503 return 0;
504 }
505
506 void intel_panel_destroy_backlight(struct drm_device *dev)
507 {
508 return;
509 }
510 #endif
511
512 int intel_panel_init(struct intel_panel *panel,
513 struct drm_display_mode *fixed_mode)
514 {
515 panel->fixed_mode = fixed_mode;
516
517 return 0;
518 }
519
520 void intel_panel_fini(struct intel_panel *panel)
521 {
522 struct intel_connector *intel_connector =
523 container_of(panel, struct intel_connector, panel);
524
525 if (panel->fixed_mode)
526 drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
527 }
This page took 0.041529 seconds and 5 git commands to generate.