OSDN Git Service

initial import
[android-x86/device-viliv-s5.git] / psb-kernel-source-4.41.1 / 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
29 static void intel_crt_dpms(struct drm_output *output, int mode)
30 {
31         struct drm_device *dev = output->dev;
32         DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private;
33         u32 temp;
34         
35         temp = I915_READ(ADPA);
36         temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
37         temp &= ~ADPA_DAC_ENABLE;
38         
39         switch(mode) {
40         case DPMSModeOn:
41                 temp |= ADPA_DAC_ENABLE;
42                 break;
43         case DPMSModeStandby:
44                 temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
45                 break;
46         case DPMSModeSuspend:
47                 temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
48                 break;
49         case DPMSModeOff:
50                 temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
51                 break;
52         }
53         
54         I915_WRITE(ADPA, temp);
55 }
56
57 static void intel_crt_save(struct drm_output *output)
58 {
59         
60 }
61
62 static void intel_crt_restore(struct drm_output *output)
63 {
64
65 }
66
67 static int intel_crt_mode_valid(struct drm_output *output,
68                                 struct drm_display_mode *mode)
69 {
70         if (mode->flags & V_DBLSCAN)
71                 return MODE_NO_DBLESCAN;
72
73         if (mode->clock > 400000 || mode->clock < 25000)
74                 return MODE_CLOCK_RANGE;
75
76         return MODE_OK;
77 }
78
79 static bool intel_crt_mode_fixup(struct drm_output *output,
80                                  struct drm_display_mode *mode,
81                                  struct drm_display_mode *adjusted_mode)
82 {
83         return true;
84 }
85
86 static void intel_crt_mode_set(struct drm_output *output,
87                                struct drm_display_mode *mode,
88                                struct drm_display_mode *adjusted_mode)
89 {
90         struct drm_device *dev = output->dev;
91         struct drm_crtc *crtc = output->crtc;
92         struct intel_crtc *intel_crtc = crtc->driver_private;
93         DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private;
94         int dpll_md_reg;
95         u32 adpa, dpll_md;
96
97         if (intel_crtc->pipe == 0) 
98                 dpll_md_reg = DPLL_A_MD;
99         else
100                 dpll_md_reg = DPLL_B_MD;
101
102         /*
103          * Disable separate mode multiplier used when cloning SDVO to CRT
104          * XXX this needs to be adjusted when we really are cloning
105          */
106         if (IS_I965G(dev)) {
107                 dpll_md = I915_READ(dpll_md_reg);
108                 I915_WRITE(dpll_md_reg,
109                            dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
110         }
111         
112         adpa = 0;
113         if (adjusted_mode->flags & V_PHSYNC)
114                 adpa |= ADPA_HSYNC_ACTIVE_HIGH;
115         if (adjusted_mode->flags & V_PVSYNC)
116                 adpa |= ADPA_VSYNC_ACTIVE_HIGH;
117         
118         if (intel_crtc->pipe == 0)
119                 adpa |= ADPA_PIPE_A_SELECT;
120         else
121                 adpa |= ADPA_PIPE_B_SELECT;
122         
123         I915_WRITE(ADPA, adpa);
124 }
125
126 /**
127  * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
128  *
129  * Only for I945G/GM.
130  *
131  * \return TRUE if CRT is connected.
132  * \return FALSE if CRT is disconnected.
133  */
134 static bool intel_crt_detect_hotplug(struct drm_output *output)
135 {
136         struct drm_device *dev = output->dev;
137         DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private;
138         u32 temp;
139         unsigned long timeout = jiffies + msecs_to_jiffies(1000);
140
141         temp = I915_READ(PORT_HOTPLUG_EN);
142
143         I915_WRITE(PORT_HOTPLUG_EN,
144                    temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
145
146         do {
147                 if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
148                         break;
149                 msleep(1);
150         } while (time_after(timeout, jiffies));
151
152         if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
153             CRT_HOTPLUG_MONITOR_COLOR)
154                 return true;
155
156         return false;
157 }
158
159 static bool intel_crt_detect_ddc(struct drm_output *output)
160 {
161         struct intel_output *intel_output = output->driver_private;
162
163         /* CRT should always be at 0, but check anyway */
164         if (intel_output->type != INTEL_OUTPUT_ANALOG)
165                 return false;
166         
167         return intel_ddc_probe(output);
168 }
169
170 static enum drm_output_status intel_crt_detect(struct drm_output *output)
171 {
172         struct drm_device *dev = output->dev;
173         
174         if (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) {
175                 if (intel_crt_detect_hotplug(output))
176                         return output_status_connected;
177                 else
178                         return output_status_disconnected;
179         }
180
181         if (intel_crt_detect_ddc(output))
182                 return output_status_connected;
183
184         /* TODO use load detect */
185         return output_status_unknown;
186 }
187
188 static void intel_crt_destroy(struct drm_output *output)
189 {
190         struct intel_output *intel_output = output->driver_private;
191
192         intel_i2c_destroy(intel_output->ddc_bus);
193         kfree(output->driver_private);
194 }
195
196 static int intel_crt_get_modes(struct drm_output *output)
197 {
198         return intel_ddc_get_modes(output);
199 }
200
201 /*
202  * Routines for controlling stuff on the analog port
203  */
204 static const struct drm_output_funcs intel_crt_output_funcs = {
205         .dpms = intel_crt_dpms,
206         .save = intel_crt_save,
207         .restore = intel_crt_restore,
208         .mode_valid = intel_crt_mode_valid,
209         .mode_fixup = intel_crt_mode_fixup,
210         .prepare = intel_output_prepare,
211         .mode_set = intel_crt_mode_set,
212         .commit = intel_output_commit,
213         .detect = intel_crt_detect,
214         .get_modes = intel_crt_get_modes,
215         .cleanup = intel_crt_destroy,
216 };
217
218 void intel_crt_init(struct drm_device *dev)
219 {
220         struct drm_output *output;
221         struct intel_output *intel_output;
222
223         output = drm_output_create(dev, &intel_crt_output_funcs, "VGA");
224
225         intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL);
226         if (!intel_output) {
227                 drm_output_destroy(output);
228                 return;
229         }
230         /* Set up the DDC bus. */
231         intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
232         if (!intel_output->ddc_bus) {
233                 dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
234                            "failed.\n");
235                 return;
236         }
237
238         intel_output->type = INTEL_OUTPUT_ANALOG;
239         output->driver_private = intel_output;
240         output->interlace_allowed = 0;
241         output->doublescan_allowed = 0;
242 }