aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_crt.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_crt.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_crt.c')
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
new file mode 100644
index 00000000000..5d9c94eb705
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -0,0 +1,284 @@
1/*
2 * Copyright © 2006-2007 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Eric Anholt <eric@anholt.net>
25 */
26
27#include <linux/i2c.h>
28#include "drmP.h"
29#include "drm.h"
30#include "drm_crtc.h"
31#include "drm_crtc_helper.h"
32#include "intel_drv.h"
33#include "i915_drm.h"
34#include "i915_drv.h"
35
36static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
37{
38 struct drm_device *dev = encoder->dev;
39 struct drm_i915_private *dev_priv = dev->dev_private;
40 u32 temp;
41
42 temp = I915_READ(ADPA);
43 temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
44 temp &= ~ADPA_DAC_ENABLE;
45
46 switch(mode) {
47 case DRM_MODE_DPMS_ON:
48 temp |= ADPA_DAC_ENABLE;
49 break;
50 case DRM_MODE_DPMS_STANDBY:
51 temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
52 break;
53 case DRM_MODE_DPMS_SUSPEND:
54 temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
55 break;
56 case DRM_MODE_DPMS_OFF:
57 temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
58 break;
59 }
60
61 I915_WRITE(ADPA, temp);
62}
63
64static int intel_crt_mode_valid(struct drm_connector *connector,
65 struct drm_display_mode *mode)
66{
67 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
68 return MODE_NO_DBLESCAN;
69
70 if (mode->clock > 400000 || mode->clock < 25000)
71 return MODE_CLOCK_RANGE;
72
73 return MODE_OK;
74}
75
76static bool intel_crt_mode_fixup(struct drm_encoder *encoder,
77 struct drm_display_mode *mode,
78 struct drm_display_mode *adjusted_mode)
79{
80 return true;
81}
82
83static void intel_crt_mode_set(struct drm_encoder *encoder,
84 struct drm_display_mode *mode,
85 struct drm_display_mode *adjusted_mode)
86{
87
88 struct drm_device *dev = encoder->dev;
89 struct drm_crtc *crtc = encoder->crtc;
90 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
91 struct drm_i915_private *dev_priv = dev->dev_private;
92 int dpll_md_reg;
93 u32 adpa, dpll_md;
94
95 if (intel_crtc->pipe == 0)
96 dpll_md_reg = DPLL_A_MD;
97 else
98 dpll_md_reg = DPLL_B_MD;
99
100 /*
101 * Disable separate mode multiplier used when cloning SDVO to CRT
102 * XXX this needs to be adjusted when we really are cloning
103 */
104 if (IS_I965G(dev)) {
105 dpll_md = I915_READ(dpll_md_reg);
106 I915_WRITE(dpll_md_reg,
107 dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
108 }
109
110 adpa = 0;
111 if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
112 adpa |= ADPA_HSYNC_ACTIVE_HIGH;
113 if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
114 adpa |= ADPA_VSYNC_ACTIVE_HIGH;
115
116 if (intel_crtc->pipe == 0)
117 adpa |= ADPA_PIPE_A_SELECT;
118 else
119 adpa |= ADPA_PIPE_B_SELECT;
120
121 I915_WRITE(ADPA, adpa);
122}
123
124/**
125 * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
126 *
127 * Not for i915G/i915GM
128 *
129 * \return true if CRT is connected.
130 * \return false if CRT is disconnected.
131 */
132static bool intel_crt_detect_hotplug(struct drm_connector *connector)
133{
134 struct drm_device *dev = connector->dev;
135 struct drm_i915_private *dev_priv = dev->dev_private;
136 u32 temp;
137
138 unsigned long timeout = jiffies + msecs_to_jiffies(1000);
139
140 temp = I915_READ(PORT_HOTPLUG_EN);
141
142 I915_WRITE(PORT_HOTPLUG_EN,
143 temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
144
145 do {
146 if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
147 break;
148 msleep(1);
149 } while (time_after(timeout, jiffies));
150
151 if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
152 CRT_HOTPLUG_MONITOR_COLOR)
153 return true;
154
155 return false;
156}
157
158static bool intel_crt_detect_ddc(struct drm_connector *connector)
159{
160 struct intel_output *intel_output = to_intel_output(connector);
161
162 /* CRT should always be at 0, but check anyway */
163 if (intel_output->type != INTEL_OUTPUT_ANALOG)
164 return false;
165
166 return intel_ddc_probe(intel_output);
167}
168
169static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)
170{
171 struct drm_device *dev = connector->dev;
172
173 if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
174 if (intel_crt_detect_hotplug(connector))
175 return connector_status_connected;
176 else
177 return connector_status_disconnected;
178 }
179
180 if (intel_crt_detect_ddc(connector))
181 return connector_status_connected;
182
183 /* TODO use load detect */
184 return connector_status_unknown;
185}
186
187static void intel_crt_destroy(struct drm_connector *connector)
188{
189 struct intel_output *intel_output = to_intel_output(connector);
190
191 intel_i2c_destroy(intel_output->ddc_bus);
192 drm_sysfs_connector_remove(connector);
193 drm_connector_cleanup(connector);
194 kfree(connector);
195}
196
197static int intel_crt_get_modes(struct drm_connector *connector)
198{
199 struct intel_output *intel_output = to_intel_output(connector);
200 return intel_ddc_get_modes(intel_output);
201}
202
203static int intel_crt_set_property(struct drm_connector *connector,
204 struct drm_property *property,
205 uint64_t value)
206{
207 struct drm_device *dev = connector->dev;
208
209 if (property == dev->mode_config.dpms_property && connector->encoder)
210 intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
211
212 return 0;
213}
214
215/*
216 * Routines for controlling stuff on the analog port
217 */
218
219static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
220 .dpms = intel_crt_dpms,
221 .mode_fixup = intel_crt_mode_fixup,
222 .prepare = intel_encoder_prepare,
223 .commit = intel_encoder_commit,
224 .mode_set = intel_crt_mode_set,
225};
226
227static const struct drm_connector_funcs intel_crt_connector_funcs = {
228 .detect = intel_crt_detect,
229 .fill_modes = drm_helper_probe_single_connector_modes,
230 .destroy = intel_crt_destroy,
231 .set_property = intel_crt_set_property,
232};
233
234static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
235 .mode_valid = intel_crt_mode_valid,
236 .get_modes = intel_crt_get_modes,
237 .best_encoder = intel_best_encoder,
238};
239
240void intel_crt_enc_destroy(struct drm_encoder *encoder)
241{
242 drm_encoder_cleanup(encoder);
243}
244
245static const struct drm_encoder_funcs intel_crt_enc_funcs = {
246 .destroy = intel_crt_enc_destroy,
247};
248
249void intel_crt_init(struct drm_device *dev)
250{
251 struct drm_connector *connector;
252 struct intel_output *intel_output;
253
254 intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
255 if (!intel_output)
256 return;
257
258 connector = &intel_output->base;
259 drm_connector_init(dev, &intel_output->base,
260 &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
261
262 drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
263 DRM_MODE_ENCODER_DAC);
264
265 drm_mode_connector_attach_encoder(&intel_output->base,
266 &intel_output->enc);
267
268 /* Set up the DDC bus. */
269 intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
270 if (!intel_output->ddc_bus) {
271 dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
272 "failed.\n");
273 return;
274 }
275
276 intel_output->type = INTEL_OUTPUT_ANALOG;
277 connector->interlace_allowed = 0;
278 connector->doublescan_allowed = 0;
279
280 drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
281 drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
282
283 drm_sysfs_connector_add(connector);
284}