aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_lvds.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2008-11-07 17:24:08 -0500
committerDave Airlie <airlied@linux.ie>2008-12-29 02:47:23 -0500
commit79e539453b34e35f39299a899d263b0a1f1670bd (patch)
tree6d1285f2b78fab399aab75a3557b7d6bc0dbd112 /drivers/gpu/drm/i915/intel_lvds.c
parentf453ba0460742ad027ae0c4c7d61e62817b3e7ef (diff)
DRM: i915: add mode setting support
This commit adds i915 driver support for the DRM mode setting APIs. Currently, VGA, LVDS, SDVO DVI & VGA, TV and DVO LVDS outputs are supported. HDMI, DisplayPort and additional SDVO output support will follow. Support for the mode setting code is controlled by the new 'modeset' module option. A new config option, CONFIG_DRM_I915_KMS controls the default behavior, and whether a PCI ID list is built into the module for use by user level module utilities. Note that if mode setting is enabled, user level drivers that access display registers directly or that don't use the kernel graphics memory manager will likely corrupt kernel graphics memory, disrupt output configuration (possibly leading to hangs and/or blank displays), and prevent panic/oops messages from appearing. So use caution when enabling this code; be sure your user level code supports the new interfaces. A new SysRq key, 'g', provides emergency support for switching back to the kernel's framebuffer console; which is useful for testing. Co-authors: Dave Airlie <airlied@linux.ie>, Hong Liu <hong.liu@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lvds.c')
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
new file mode 100644
index 000000000000..ccecfaf6307b
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -0,0 +1,525 @@
1/*
2 * Copyright © 2006-2007 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 */
29
30#include <linux/i2c.h>
31#include "drmP.h"
32#include "drm.h"
33#include "drm_crtc.h"
34#include "drm_edid.h"
35#include "intel_drv.h"
36#include "i915_drm.h"
37#include "i915_drv.h"
38
39/**
40 * Sets the backlight level.
41 *
42 * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
43 */
44static void intel_lvds_set_backlight(struct drm_device *dev, int level)
45{
46 struct drm_i915_private *dev_priv = dev->dev_private;
47 u32 blc_pwm_ctl;
48
49 blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
50 I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
51 (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
52}
53
54/**
55 * Returns the maximum level of the backlight duty cycle field.
56 */
57static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
58{
59 struct drm_i915_private *dev_priv = dev->dev_private;
60
61 return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
62 BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
63}
64
65/**
66 * Sets the power state for the panel.
67 */
68static void intel_lvds_set_power(struct drm_device *dev, bool on)
69{
70 struct drm_i915_private *dev_priv = dev->dev_private;
71 u32 pp_status;
72
73 if (on) {
74 I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
75 POWER_TARGET_ON);
76 do {
77 pp_status = I915_READ(PP_STATUS);
78 } while ((pp_status & PP_ON) == 0);
79
80 intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
81 } else {
82 intel_lvds_set_backlight(dev, 0);
83
84 I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
85 ~POWER_TARGET_ON);
86 do {
87 pp_status = I915_READ(PP_STATUS);
88 } while (pp_status & PP_ON);
89 }
90}
91
92static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
93{
94 struct drm_device *dev = encoder->dev;
95
96 if (mode == DRM_MODE_DPMS_ON)
97 intel_lvds_set_power(dev, true);
98 else
99 intel_lvds_set_power(dev, false);
100
101 /* XXX: We never power down the LVDS pairs. */
102}
103
104static void intel_lvds_save(struct drm_connector *connector)
105{
106 struct drm_device *dev = connector->dev;
107 struct drm_i915_private *dev_priv = dev->dev_private;
108
109 dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
110 dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
111 dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
112 dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
113 dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
114 dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
115 BACKLIGHT_DUTY_CYCLE_MASK);
116
117 /*
118 * If the light is off at server startup, just make it full brightness
119 */
120 if (dev_priv->backlight_duty_cycle == 0)
121 dev_priv->backlight_duty_cycle =
122 intel_lvds_get_max_backlight(dev);
123}
124
125static void intel_lvds_restore(struct drm_connector *connector)
126{
127 struct drm_device *dev = connector->dev;
128 struct drm_i915_private *dev_priv = dev->dev_private;
129
130 I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
131 I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
132 I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
133 I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
134 I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
135 if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
136 intel_lvds_set_power(dev, true);
137 else
138 intel_lvds_set_power(dev, false);
139}
140
141static int intel_lvds_mode_valid(struct drm_connector *connector,
142 struct drm_display_mode *mode)
143{
144 struct drm_device *dev = connector->dev;
145 struct drm_i915_private *dev_priv = dev->dev_private;
146 struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
147
148 if (fixed_mode) {
149 if (mode->hdisplay > fixed_mode->hdisplay)
150 return MODE_PANEL;
151 if (mode->vdisplay > fixed_mode->vdisplay)
152 return MODE_PANEL;
153 }
154
155 return MODE_OK;
156}
157
158static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
159 struct drm_display_mode *mode,
160 struct drm_display_mode *adjusted_mode)
161{
162 struct drm_device *dev = encoder->dev;
163 struct drm_i915_private *dev_priv = dev->dev_private;
164 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
165 struct drm_encoder *tmp_encoder;
166
167 /* Should never happen!! */
168 if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
169 printk(KERN_ERR "Can't support LVDS on pipe A\n");
170 return false;
171 }
172
173 /* Should never happen!! */
174 list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
175 if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
176 printk(KERN_ERR "Can't enable LVDS and another "
177 "encoder on the same pipe\n");
178 return false;
179 }
180 }
181
182 /*
183 * If we have timings from the BIOS for the panel, put them in
184 * to the adjusted mode. The CRTC will be set up for this mode,
185 * with the panel scaling set up to source from the H/VDisplay
186 * of the original mode.
187 */
188 if (dev_priv->panel_fixed_mode != NULL) {
189 adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
190 adjusted_mode->hsync_start =
191 dev_priv->panel_fixed_mode->hsync_start;
192 adjusted_mode->hsync_end =
193 dev_priv->panel_fixed_mode->hsync_end;
194 adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
195 adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
196 adjusted_mode->vsync_start =
197 dev_priv->panel_fixed_mode->vsync_start;
198 adjusted_mode->vsync_end =
199 dev_priv->panel_fixed_mode->vsync_end;
200 adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
201 adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
202 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
203 }
204
205 /*
206 * XXX: It would be nice to support lower refresh rates on the
207 * panels to reduce power consumption, and perhaps match the
208 * user's requested refresh rate.
209 */
210
211 return true;
212}
213
214static void intel_lvds_prepare(struct drm_encoder *encoder)
215{
216 struct drm_device *dev = encoder->dev;
217 struct drm_i915_private *dev_priv = dev->dev_private;
218
219 dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
220 dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
221 BACKLIGHT_DUTY_CYCLE_MASK);
222
223 intel_lvds_set_power(dev, false);
224}
225
226static void intel_lvds_commit( struct drm_encoder *encoder)
227{
228 struct drm_device *dev = encoder->dev;
229 struct drm_i915_private *dev_priv = dev->dev_private;
230
231 if (dev_priv->backlight_duty_cycle == 0)
232 dev_priv->backlight_duty_cycle =
233 intel_lvds_get_max_backlight(dev);
234
235 intel_lvds_set_power(dev, true);
236}
237
238static void intel_lvds_mode_set(struct drm_encoder *encoder,
239 struct drm_display_mode *mode,
240 struct drm_display_mode *adjusted_mode)
241{
242 struct drm_device *dev = encoder->dev;
243 struct drm_i915_private *dev_priv = dev->dev_private;
244 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
245 u32 pfit_control;
246
247 /*
248 * The LVDS pin pair will already have been turned on in the
249 * intel_crtc_mode_set since it has a large impact on the DPLL
250 * settings.
251 */
252
253 /*
254 * Enable automatic panel scaling so that non-native modes fill the
255 * screen. Should be enabled before the pipe is enabled, according to
256 * register description and PRM.
257 */
258 if (mode->hdisplay != adjusted_mode->hdisplay ||
259 mode->vdisplay != adjusted_mode->vdisplay)
260 pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
261 HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
262 HORIZ_INTERP_BILINEAR);
263 else
264 pfit_control = 0;
265
266 if (!IS_I965G(dev)) {
267 if (dev_priv->panel_wants_dither)
268 pfit_control |= PANEL_8TO6_DITHER_ENABLE;
269 }
270 else
271 pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
272
273 I915_WRITE(PFIT_CONTROL, pfit_control);
274}
275
276/**
277 * Detect the LVDS connection.
278 *
279 * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have
280 * been set up if the LVDS was actually connected anyway.
281 */
282static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector)
283{
284 return connector_status_connected;
285}
286
287/**
288 * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
289 */
290static int intel_lvds_get_modes(struct drm_connector *connector)
291{
292 struct drm_device *dev = connector->dev;
293 struct intel_output *intel_output = to_intel_output(connector);
294 struct drm_i915_private *dev_priv = dev->dev_private;
295 int ret = 0;
296
297 ret = intel_ddc_get_modes(intel_output);
298
299 if (ret)
300 return ret;
301
302 /* Didn't get an EDID, so
303 * Set wide sync ranges so we get all modes
304 * handed to valid_mode for checking
305 */
306 connector->display_info.min_vfreq = 0;
307 connector->display_info.max_vfreq = 200;
308 connector->display_info.min_hfreq = 0;
309 connector->display_info.max_hfreq = 200;
310
311 if (dev_priv->panel_fixed_mode != NULL) {
312 struct drm_display_mode *mode;
313
314 mutex_unlock(&dev->mode_config.mutex);
315 mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
316 drm_mode_probed_add(connector, mode);
317 mutex_unlock(&dev->mode_config.mutex);
318
319 return 1;
320 }
321
322 return 0;
323}
324
325/**
326 * intel_lvds_destroy - unregister and free LVDS structures
327 * @connector: connector to free
328 *
329 * Unregister the DDC bus for this connector then free the driver private
330 * structure.
331 */
332static void intel_lvds_destroy(struct drm_connector *connector)
333{
334 struct intel_output *intel_output = to_intel_output(connector);
335
336 if (intel_output->ddc_bus)
337 intel_i2c_destroy(intel_output->ddc_bus);
338 drm_sysfs_connector_remove(connector);
339 drm_connector_cleanup(connector);
340 kfree(connector);
341}
342
343static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
344 .dpms = intel_lvds_dpms,
345 .mode_fixup = intel_lvds_mode_fixup,
346 .prepare = intel_lvds_prepare,
347 .mode_set = intel_lvds_mode_set,
348 .commit = intel_lvds_commit,
349};
350
351static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
352 .get_modes = intel_lvds_get_modes,
353 .mode_valid = intel_lvds_mode_valid,
354 .best_encoder = intel_best_encoder,
355};
356
357static const struct drm_connector_funcs intel_lvds_connector_funcs = {
358 .save = intel_lvds_save,
359 .restore = intel_lvds_restore,
360 .detect = intel_lvds_detect,
361 .fill_modes = drm_helper_probe_single_connector_modes,
362 .destroy = intel_lvds_destroy,
363};
364
365
366static void intel_lvds_enc_destroy(struct drm_encoder *encoder)
367{
368 drm_encoder_cleanup(encoder);
369}
370
371static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
372 .destroy = intel_lvds_enc_destroy,
373};
374
375
376
377/**
378 * intel_lvds_init - setup LVDS connectors on this device
379 * @dev: drm device
380 *
381 * Create the connector, register the LVDS DDC bus, and try to figure out what
382 * modes we can display on the LVDS panel (if present).
383 */
384void intel_lvds_init(struct drm_device *dev)
385{
386 struct drm_i915_private *dev_priv = dev->dev_private;
387 struct intel_output *intel_output;
388 struct drm_connector *connector;
389 struct drm_encoder *encoder;
390 struct drm_display_mode *scan; /* *modes, *bios_mode; */
391 struct drm_crtc *crtc;
392 u32 lvds;
393 int pipe;
394
395 intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
396 if (!intel_output) {
397 return;
398 }
399
400 connector = &intel_output->base;
401 encoder = &intel_output->enc;
402 drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs,
403 DRM_MODE_CONNECTOR_LVDS);
404
405 drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
406 DRM_MODE_ENCODER_LVDS);
407
408 drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
409 intel_output->type = INTEL_OUTPUT_LVDS;
410
411 drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
412 drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
413 connector->display_info.subpixel_order = SubPixelHorizontalRGB;
414 connector->interlace_allowed = false;
415 connector->doublescan_allowed = false;
416
417
418 /*
419 * LVDS discovery:
420 * 1) check for EDID on DDC
421 * 2) check for VBT data
422 * 3) check to see if LVDS is already on
423 * if none of the above, no panel
424 * 4) make sure lid is open
425 * if closed, act like it's not there for now
426 */
427
428 /* Set up the DDC bus. */
429 intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
430 if (!intel_output->ddc_bus) {
431 dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
432 "failed.\n");
433 goto failed;
434 }
435
436 /*
437 * Attempt to get the fixed panel mode from DDC. Assume that the
438 * preferred mode is the right one.
439 */
440 intel_ddc_get_modes(intel_output);
441
442 list_for_each_entry(scan, &connector->probed_modes, head) {
443 mutex_lock(&dev->mode_config.mutex);
444 if (scan->type & DRM_MODE_TYPE_PREFERRED) {
445 dev_priv->panel_fixed_mode =
446 drm_mode_duplicate(dev, scan);
447 mutex_unlock(&dev->mode_config.mutex);
448 goto out; /* FIXME: check for quirks */
449 }
450 mutex_unlock(&dev->mode_config.mutex);
451 }
452
453 /* Failed to get EDID, what about VBT? */
454 if (dev_priv->vbt_mode) {
455 mutex_lock(&dev->mode_config.mutex);
456 dev_priv->panel_fixed_mode =
457 drm_mode_duplicate(dev, dev_priv->vbt_mode);
458 mutex_unlock(&dev->mode_config.mutex);
459 }
460
461 /*
462 * If we didn't get EDID, try checking if the panel is already turned
463 * on. If so, assume that whatever is currently programmed is the
464 * correct mode.
465 */
466 lvds = I915_READ(LVDS);
467 pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
468 crtc = intel_get_crtc_from_pipe(dev, pipe);
469
470 if (crtc && (lvds & LVDS_PORT_EN)) {
471 dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
472 if (dev_priv->panel_fixed_mode) {
473 dev_priv->panel_fixed_mode->type |=
474 DRM_MODE_TYPE_PREFERRED;
475 goto out; /* FIXME: check for quirks */
476 }
477 }
478
479 /* If we still don't have a mode after all that, give up. */
480 if (!dev_priv->panel_fixed_mode)
481 goto failed;
482
483 /* FIXME: detect aopen & mac mini type stuff automatically? */
484 /*
485 * Blacklist machines with BIOSes that list an LVDS panel without
486 * actually having one.
487 */
488 if (IS_I945GM(dev)) {
489 /* aopen mini pc */
490 if (dev->pdev->subsystem_vendor == 0xa0a0)
491 goto failed;
492
493 if ((dev->pdev->subsystem_vendor == 0x8086) &&
494 (dev->pdev->subsystem_device == 0x7270)) {
495 /* It's a Mac Mini or Macbook Pro.
496 *
497 * Apple hardware is out to get us. The macbook pro
498 * has a real LVDS panel, but the mac mini does not,
499 * and they have the same device IDs. We'll
500 * distinguish by panel size, on the assumption
501 * that Apple isn't about to make any machines with an
502 * 800x600 display.
503 */
504
505 if (dev_priv->panel_fixed_mode != NULL &&
506 dev_priv->panel_fixed_mode->hdisplay == 800 &&
507 dev_priv->panel_fixed_mode->vdisplay == 600) {
508 DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n");
509 goto failed;
510 }
511 }
512 }
513
514
515out:
516 drm_sysfs_connector_add(connector);
517 return;
518
519failed:
520 DRM_DEBUG("No LVDS modes found, disabling.\n");
521 if (intel_output->ddc_bus)
522 intel_i2c_destroy(intel_output->ddc_bus);
523 drm_connector_cleanup(connector);
524 kfree(connector);
525}