drm: attach an encoder.
[platform/upstream/libdrm.git] / linux-core / intel_crt.c
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
36 static void intel_crt_dpms(struct drm_output *output, int mode)
37 {
38         struct drm_device *dev = output->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 DPMSModeOn:
48                 temp |= ADPA_DAC_ENABLE;
49                 break;
50         case DPMSModeStandby:
51                 temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
52                 break;
53         case DPMSModeSuspend:
54                 temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
55                 break;
56         case DPMSModeOff:
57                 temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
58                 break;
59         }
60         
61         I915_WRITE(ADPA, temp);
62 }
63
64 static void intel_crt_save(struct drm_output *output)
65 {
66         
67 }
68
69 static void intel_crt_restore(struct drm_output *output)
70 {
71
72 }
73
74 static int intel_crt_mode_valid(struct drm_output *output,
75                                 struct drm_display_mode *mode)
76 {
77         if (mode->flags & V_DBLSCAN)
78                 return MODE_NO_DBLESCAN;
79
80         if (mode->clock > 400000 || mode->clock < 25000)
81                 return MODE_CLOCK_RANGE;
82
83         return MODE_OK;
84 }
85
86 static bool intel_crt_mode_fixup(struct drm_output *output,
87                                  struct drm_display_mode *mode,
88                                  struct drm_display_mode *adjusted_mode)
89 {
90         return true;
91 }
92
93 static void intel_crt_mode_set(struct drm_output *output,
94                                struct drm_display_mode *mode,
95                                struct drm_display_mode *adjusted_mode)
96 {
97         struct drm_device *dev = output->dev;
98         struct drm_crtc *crtc = output->crtc;
99         struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
100         struct drm_i915_private *dev_priv = dev->dev_private;
101         int dpll_md_reg;
102         u32 adpa, dpll_md;
103
104         if (intel_crtc->pipe == 0) 
105                 dpll_md_reg = DPLL_A_MD;
106         else
107                 dpll_md_reg = DPLL_B_MD;
108
109         /*
110          * Disable separate mode multiplier used when cloning SDVO to CRT
111          * XXX this needs to be adjusted when we really are cloning
112          */
113         if (IS_I965G(dev)) {
114                 dpll_md = I915_READ(dpll_md_reg);
115                 I915_WRITE(dpll_md_reg,
116                            dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
117         }
118         
119         adpa = 0;
120         if (adjusted_mode->flags & V_PHSYNC)
121                 adpa |= ADPA_HSYNC_ACTIVE_HIGH;
122         if (adjusted_mode->flags & V_PVSYNC)
123                 adpa |= ADPA_VSYNC_ACTIVE_HIGH;
124         
125         if (intel_crtc->pipe == 0)
126                 adpa |= ADPA_PIPE_A_SELECT;
127         else
128                 adpa |= ADPA_PIPE_B_SELECT;
129         
130         I915_WRITE(ADPA, adpa);
131 }
132
133 /**
134  * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
135  *
136  * Not for i915G/i915GM
137  *
138  * \return TRUE if CRT is connected.
139  * \return FALSE if CRT is disconnected.
140  */
141 static bool intel_crt_detect_hotplug(struct drm_output *output)
142 {
143         struct drm_device *dev = output->dev;
144         struct drm_i915_private *dev_priv = dev->dev_private;
145         u32 temp;
146
147         unsigned long timeout = jiffies + msecs_to_jiffies(1000);
148
149         temp = I915_READ(PORT_HOTPLUG_EN);
150
151         I915_WRITE(PORT_HOTPLUG_EN,
152                    temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
153
154         do {
155                 if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
156                         break;
157                 msleep(1);
158         } while (time_after(timeout, jiffies));
159
160         if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
161             CRT_HOTPLUG_MONITOR_COLOR)
162                 return true;
163
164         return false;
165 }
166
167 static bool intel_crt_detect_ddc(struct drm_output *output)
168 {
169         struct intel_output *intel_output = to_intel_output(output);
170
171         /* CRT should always be at 0, but check anyway */
172         if (intel_output->type != INTEL_OUTPUT_ANALOG)
173                 return false;
174         
175         return intel_ddc_probe(output);
176 }
177
178 static enum drm_output_status intel_crt_detect(struct drm_output *output)
179 {
180         struct drm_device *dev = output->dev;
181         
182         if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
183                 if (intel_crt_detect_hotplug(output))
184                         return output_status_connected;
185                 else
186                         return output_status_disconnected;
187         }
188
189         if (intel_crt_detect_ddc(output))
190                 return output_status_connected;
191
192         /* TODO use load detect */
193         return output_status_unknown;
194 }
195
196 static void intel_crt_destroy(struct drm_output *output)
197 {
198         struct intel_output *intel_output = to_intel_output(output);
199
200         intel_i2c_destroy(intel_output->ddc_bus);
201         drm_output_cleanup(output);
202         kfree(output);
203 }
204
205 static int intel_crt_get_modes(struct drm_output *output)
206 {
207         return intel_ddc_get_modes(output);
208 }
209
210 static bool intel_crt_set_property(struct drm_output *output,
211                                   struct drm_property *property,
212                                   uint64_t value)
213 {
214         struct drm_device *dev = output->dev;
215
216         if (property == dev->mode_config.dpms_property)
217                 intel_crt_dpms(output, (uint32_t)(value & 0xf));
218
219         return true;
220 }
221
222 /*
223  * Routines for controlling stuff on the analog port
224  */
225
226 static const struct drm_output_helper_funcs intel_crt_helper_funcs = {
227         .mode_fixup = intel_crt_mode_fixup,
228         .prepare = intel_output_prepare,
229         .commit = intel_output_commit,
230         .mode_set = intel_crt_mode_set,
231 };
232
233 static const struct drm_output_funcs intel_crt_output_funcs = {
234         .dpms = intel_crt_dpms,
235         .save = intel_crt_save,
236         .restore = intel_crt_restore,
237         .detect = intel_crt_detect,
238         .get_modes = intel_crt_get_modes,
239         .destroy = intel_crt_destroy,
240         .set_property = intel_crt_set_property,
241         .mode_valid = intel_crt_mode_valid,
242
243 };
244
245 void intel_crt_enc_destroy(struct drm_encoder *encoder)
246 {
247         drm_encoder_cleanup(encoder);
248 }
249
250 static const struct drm_encoder_funcs intel_crt_enc_funcs = {
251         .destroy = intel_crt_enc_destroy,
252 };
253
254 void intel_crt_init(struct drm_device *dev)
255 {
256         struct drm_output *output;
257         struct intel_output *intel_output;
258
259         intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
260         if (!intel_output)
261                 return;
262
263         output = &intel_output->base;
264         drm_output_init(dev, &intel_output->base, &intel_crt_output_funcs, DRM_MODE_OUTPUT_VGA);
265
266         drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC);
267
268         drm_mode_output_attach_encoder(&intel_output->base, &intel_output->enc);
269
270         /* Set up the DDC bus. */
271         intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
272         if (!intel_output->ddc_bus) {
273                 dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
274                            "failed.\n");
275                 intel_crt_destroy(output);
276                 return;
277         }
278
279         intel_output->type = INTEL_OUTPUT_ANALOG;
280         output->interlace_allowed = 0;
281         output->doublescan_allowed = 0;
282
283         drm_output_helper_add(output, &intel_crt_helper_funcs);
284
285         drm_sysfs_output_add(output);
286 }