diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/cdv_intel_crt.c')
-rw-r--r-- | drivers/gpu/drm/gma500/cdv_intel_crt.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c new file mode 100644 index 000000000000..6d0f10b7569c --- /dev/null +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c | |||
@@ -0,0 +1,333 @@ | |||
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 <drm/drmP.h> | ||
29 | |||
30 | #include "intel_bios.h" | ||
31 | #include "psb_drv.h" | ||
32 | #include "psb_intel_drv.h" | ||
33 | #include "psb_intel_reg.h" | ||
34 | #include "power.h" | ||
35 | #include <linux/pm_runtime.h> | ||
36 | |||
37 | |||
38 | static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) | ||
39 | { | ||
40 | struct drm_device *dev = encoder->dev; | ||
41 | u32 temp, reg; | ||
42 | reg = ADPA; | ||
43 | |||
44 | temp = REG_READ(reg); | ||
45 | temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); | ||
46 | temp &= ~ADPA_DAC_ENABLE; | ||
47 | |||
48 | switch (mode) { | ||
49 | case DRM_MODE_DPMS_ON: | ||
50 | temp |= ADPA_DAC_ENABLE; | ||
51 | break; | ||
52 | case DRM_MODE_DPMS_STANDBY: | ||
53 | temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; | ||
54 | break; | ||
55 | case DRM_MODE_DPMS_SUSPEND: | ||
56 | temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; | ||
57 | break; | ||
58 | case DRM_MODE_DPMS_OFF: | ||
59 | temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; | ||
60 | break; | ||
61 | } | ||
62 | |||
63 | REG_WRITE(reg, temp); | ||
64 | } | ||
65 | |||
66 | static int cdv_intel_crt_mode_valid(struct drm_connector *connector, | ||
67 | struct drm_display_mode *mode) | ||
68 | { | ||
69 | int max_clock = 0; | ||
70 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
71 | return MODE_NO_DBLESCAN; | ||
72 | |||
73 | /* The lowest clock for CDV is 20000KHz */ | ||
74 | if (mode->clock < 20000) | ||
75 | return MODE_CLOCK_LOW; | ||
76 | |||
77 | /* The max clock for CDV is 355 instead of 400 */ | ||
78 | max_clock = 355000; | ||
79 | if (mode->clock > max_clock) | ||
80 | return MODE_CLOCK_HIGH; | ||
81 | |||
82 | if (mode->hdisplay > 1680 || mode->vdisplay > 1050) | ||
83 | return MODE_PANEL; | ||
84 | |||
85 | return MODE_OK; | ||
86 | } | ||
87 | |||
88 | static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder, | ||
89 | struct drm_display_mode *mode, | ||
90 | struct drm_display_mode *adjusted_mode) | ||
91 | { | ||
92 | return true; | ||
93 | } | ||
94 | |||
95 | static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, | ||
96 | struct drm_display_mode *mode, | ||
97 | struct drm_display_mode *adjusted_mode) | ||
98 | { | ||
99 | |||
100 | struct drm_device *dev = encoder->dev; | ||
101 | struct drm_crtc *crtc = encoder->crtc; | ||
102 | struct psb_intel_crtc *psb_intel_crtc = | ||
103 | to_psb_intel_crtc(crtc); | ||
104 | int dpll_md_reg; | ||
105 | u32 adpa, dpll_md; | ||
106 | u32 adpa_reg; | ||
107 | |||
108 | if (psb_intel_crtc->pipe == 0) | ||
109 | dpll_md_reg = DPLL_A_MD; | ||
110 | else | ||
111 | dpll_md_reg = DPLL_B_MD; | ||
112 | |||
113 | adpa_reg = ADPA; | ||
114 | |||
115 | /* | ||
116 | * Disable separate mode multiplier used when cloning SDVO to CRT | ||
117 | * XXX this needs to be adjusted when we really are cloning | ||
118 | */ | ||
119 | { | ||
120 | dpll_md = REG_READ(dpll_md_reg); | ||
121 | REG_WRITE(dpll_md_reg, | ||
122 | dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); | ||
123 | } | ||
124 | |||
125 | adpa = 0; | ||
126 | if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
127 | adpa |= ADPA_HSYNC_ACTIVE_HIGH; | ||
128 | if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
129 | adpa |= ADPA_VSYNC_ACTIVE_HIGH; | ||
130 | |||
131 | if (psb_intel_crtc->pipe == 0) | ||
132 | adpa |= ADPA_PIPE_A_SELECT; | ||
133 | else | ||
134 | adpa |= ADPA_PIPE_B_SELECT; | ||
135 | |||
136 | REG_WRITE(adpa_reg, adpa); | ||
137 | } | ||
138 | |||
139 | |||
140 | /** | ||
141 | * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. | ||
142 | * | ||
143 | * \return true if CRT is connected. | ||
144 | * \return false if CRT is disconnected. | ||
145 | */ | ||
146 | static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, | ||
147 | bool force) | ||
148 | { | ||
149 | struct drm_device *dev = connector->dev; | ||
150 | u32 hotplug_en; | ||
151 | int i, tries = 0, ret = false; | ||
152 | u32 adpa_orig; | ||
153 | |||
154 | /* disable the DAC when doing the hotplug detection */ | ||
155 | |||
156 | adpa_orig = REG_READ(ADPA); | ||
157 | |||
158 | REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE)); | ||
159 | |||
160 | /* | ||
161 | * On a CDV thep, CRT detect sequence need to be done twice | ||
162 | * to get a reliable result. | ||
163 | */ | ||
164 | tries = 2; | ||
165 | |||
166 | hotplug_en = REG_READ(PORT_HOTPLUG_EN); | ||
167 | hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); | ||
168 | hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; | ||
169 | |||
170 | hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; | ||
171 | hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; | ||
172 | |||
173 | for (i = 0; i < tries ; i++) { | ||
174 | unsigned long timeout; | ||
175 | /* turn on the FORCE_DETECT */ | ||
176 | REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); | ||
177 | timeout = jiffies + msecs_to_jiffies(1000); | ||
178 | /* wait for FORCE_DETECT to go off */ | ||
179 | do { | ||
180 | if (!(REG_READ(PORT_HOTPLUG_EN) & | ||
181 | CRT_HOTPLUG_FORCE_DETECT)) | ||
182 | break; | ||
183 | msleep(1); | ||
184 | } while (time_after(timeout, jiffies)); | ||
185 | } | ||
186 | |||
187 | if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != | ||
188 | CRT_HOTPLUG_MONITOR_NONE) | ||
189 | ret = true; | ||
190 | |||
191 | /* Restore the saved ADPA */ | ||
192 | REG_WRITE(ADPA, adpa_orig); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static enum drm_connector_status cdv_intel_crt_detect( | ||
197 | struct drm_connector *connector, bool force) | ||
198 | { | ||
199 | if (cdv_intel_crt_detect_hotplug(connector, force)) | ||
200 | return connector_status_connected; | ||
201 | else | ||
202 | return connector_status_disconnected; | ||
203 | } | ||
204 | |||
205 | static void cdv_intel_crt_destroy(struct drm_connector *connector) | ||
206 | { | ||
207 | struct psb_intel_encoder *psb_intel_encoder = | ||
208 | psb_intel_attached_encoder(connector); | ||
209 | |||
210 | psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus); | ||
211 | drm_sysfs_connector_remove(connector); | ||
212 | drm_connector_cleanup(connector); | ||
213 | kfree(connector); | ||
214 | } | ||
215 | |||
216 | static int cdv_intel_crt_get_modes(struct drm_connector *connector) | ||
217 | { | ||
218 | struct psb_intel_encoder *psb_intel_encoder = | ||
219 | psb_intel_attached_encoder(connector); | ||
220 | return psb_intel_ddc_get_modes(connector, &psb_intel_encoder->ddc_bus->adapter); | ||
221 | } | ||
222 | |||
223 | static int cdv_intel_crt_set_property(struct drm_connector *connector, | ||
224 | struct drm_property *property, | ||
225 | uint64_t value) | ||
226 | { | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Routines for controlling stuff on the analog port | ||
232 | */ | ||
233 | |||
234 | static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { | ||
235 | .dpms = cdv_intel_crt_dpms, | ||
236 | .mode_fixup = cdv_intel_crt_mode_fixup, | ||
237 | .prepare = psb_intel_encoder_prepare, | ||
238 | .commit = psb_intel_encoder_commit, | ||
239 | .mode_set = cdv_intel_crt_mode_set, | ||
240 | }; | ||
241 | |||
242 | static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { | ||
243 | .dpms = drm_helper_connector_dpms, | ||
244 | .detect = cdv_intel_crt_detect, | ||
245 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
246 | .destroy = cdv_intel_crt_destroy, | ||
247 | .set_property = cdv_intel_crt_set_property, | ||
248 | }; | ||
249 | |||
250 | static const struct drm_connector_helper_funcs | ||
251 | cdv_intel_crt_connector_helper_funcs = { | ||
252 | .mode_valid = cdv_intel_crt_mode_valid, | ||
253 | .get_modes = cdv_intel_crt_get_modes, | ||
254 | .best_encoder = psb_intel_best_encoder, | ||
255 | }; | ||
256 | |||
257 | static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) | ||
258 | { | ||
259 | drm_encoder_cleanup(encoder); | ||
260 | } | ||
261 | |||
262 | static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { | ||
263 | .destroy = cdv_intel_crt_enc_destroy, | ||
264 | }; | ||
265 | |||
266 | void cdv_intel_crt_init(struct drm_device *dev, | ||
267 | struct psb_intel_mode_device *mode_dev) | ||
268 | { | ||
269 | |||
270 | struct psb_intel_connector *psb_intel_connector; | ||
271 | struct psb_intel_encoder *psb_intel_encoder; | ||
272 | struct drm_connector *connector; | ||
273 | struct drm_encoder *encoder; | ||
274 | |||
275 | u32 i2c_reg; | ||
276 | |||
277 | psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); | ||
278 | if (!psb_intel_encoder) | ||
279 | return; | ||
280 | |||
281 | psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); | ||
282 | if (!psb_intel_connector) | ||
283 | goto failed_connector; | ||
284 | |||
285 | connector = &psb_intel_connector->base; | ||
286 | drm_connector_init(dev, connector, | ||
287 | &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); | ||
288 | |||
289 | encoder = &psb_intel_encoder->base; | ||
290 | drm_encoder_init(dev, encoder, | ||
291 | &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); | ||
292 | |||
293 | psb_intel_connector_attach_encoder(psb_intel_connector, | ||
294 | psb_intel_encoder); | ||
295 | |||
296 | /* Set up the DDC bus. */ | ||
297 | i2c_reg = GPIOA; | ||
298 | /* Remove the following code for CDV */ | ||
299 | /* | ||
300 | if (dev_priv->crt_ddc_bus != 0) | ||
301 | i2c_reg = dev_priv->crt_ddc_bus; | ||
302 | }*/ | ||
303 | psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev, | ||
304 | i2c_reg, "CRTDDC_A"); | ||
305 | if (!psb_intel_encoder->ddc_bus) { | ||
306 | dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " | ||
307 | "failed.\n"); | ||
308 | goto failed_ddc; | ||
309 | } | ||
310 | |||
311 | psb_intel_encoder->type = INTEL_OUTPUT_ANALOG; | ||
312 | /* | ||
313 | psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); | ||
314 | psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); | ||
315 | */ | ||
316 | connector->interlace_allowed = 0; | ||
317 | connector->doublescan_allowed = 0; | ||
318 | |||
319 | drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); | ||
320 | drm_connector_helper_add(connector, | ||
321 | &cdv_intel_crt_connector_helper_funcs); | ||
322 | |||
323 | drm_sysfs_connector_add(connector); | ||
324 | |||
325 | return; | ||
326 | failed_ddc: | ||
327 | drm_encoder_cleanup(&psb_intel_encoder->base); | ||
328 | drm_connector_cleanup(&psb_intel_connector->base); | ||
329 | kfree(psb_intel_connector); | ||
330 | failed_connector: | ||
331 | kfree(psb_intel_encoder); | ||
332 | return; | ||
333 | } | ||