Commit | Line | Data |
---|---|---|
6a227d5f AC |
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" | |
2acdc9fa | 35 | #include "cdv_device.h" |
6a227d5f AC |
36 | #include <linux/pm_runtime.h> |
37 | ||
38 | ||
39 | static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) | |
40 | { | |
41 | struct drm_device *dev = encoder->dev; | |
42 | u32 temp, reg; | |
43 | reg = ADPA; | |
44 | ||
45 | temp = REG_READ(reg); | |
46 | temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); | |
47 | temp &= ~ADPA_DAC_ENABLE; | |
48 | ||
49 | switch (mode) { | |
50 | case DRM_MODE_DPMS_ON: | |
51 | temp |= ADPA_DAC_ENABLE; | |
52 | break; | |
53 | case DRM_MODE_DPMS_STANDBY: | |
54 | temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; | |
55 | break; | |
56 | case DRM_MODE_DPMS_SUSPEND: | |
57 | temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; | |
58 | break; | |
59 | case DRM_MODE_DPMS_OFF: | |
60 | temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; | |
61 | break; | |
62 | } | |
63 | ||
64 | REG_WRITE(reg, temp); | |
65 | } | |
66 | ||
67 | static int cdv_intel_crt_mode_valid(struct drm_connector *connector, | |
68 | struct drm_display_mode *mode) | |
69 | { | |
6a227d5f AC |
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 */ | |
b60bfb65 | 78 | if (mode->clock > 355000) |
6a227d5f AC |
79 | return MODE_CLOCK_HIGH; |
80 | ||
6a227d5f AC |
81 | return MODE_OK; |
82 | } | |
83 | ||
6a227d5f AC |
84 | static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, |
85 | struct drm_display_mode *mode, | |
86 | struct drm_display_mode *adjusted_mode) | |
87 | { | |
88 | ||
89 | struct drm_device *dev = encoder->dev; | |
90 | struct drm_crtc *crtc = encoder->crtc; | |
6306865d | 91 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
6a227d5f AC |
92 | int dpll_md_reg; |
93 | u32 adpa, dpll_md; | |
94 | u32 adpa_reg; | |
95 | ||
6306865d | 96 | if (gma_crtc->pipe == 0) |
6a227d5f AC |
97 | dpll_md_reg = DPLL_A_MD; |
98 | else | |
99 | dpll_md_reg = DPLL_B_MD; | |
100 | ||
101 | adpa_reg = ADPA; | |
102 | ||
103 | /* | |
104 | * Disable separate mode multiplier used when cloning SDVO to CRT | |
105 | * XXX this needs to be adjusted when we really are cloning | |
106 | */ | |
107 | { | |
108 | dpll_md = REG_READ(dpll_md_reg); | |
109 | REG_WRITE(dpll_md_reg, | |
110 | dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); | |
111 | } | |
112 | ||
113 | adpa = 0; | |
114 | if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) | |
115 | adpa |= ADPA_HSYNC_ACTIVE_HIGH; | |
116 | if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) | |
117 | adpa |= ADPA_VSYNC_ACTIVE_HIGH; | |
118 | ||
6306865d | 119 | if (gma_crtc->pipe == 0) |
6a227d5f AC |
120 | adpa |= ADPA_PIPE_A_SELECT; |
121 | else | |
122 | adpa |= ADPA_PIPE_B_SELECT; | |
123 | ||
124 | REG_WRITE(adpa_reg, adpa); | |
125 | } | |
126 | ||
127 | ||
128 | /** | |
129 | * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. | |
130 | * | |
131 | * \return true if CRT is connected. | |
132 | * \return false if CRT is disconnected. | |
133 | */ | |
134 | static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, | |
135 | bool force) | |
136 | { | |
137 | struct drm_device *dev = connector->dev; | |
138 | u32 hotplug_en; | |
139 | int i, tries = 0, ret = false; | |
d235e64a | 140 | u32 orig; |
6a227d5f AC |
141 | |
142 | /* | |
143 | * On a CDV thep, CRT detect sequence need to be done twice | |
144 | * to get a reliable result. | |
145 | */ | |
146 | tries = 2; | |
147 | ||
d235e64a | 148 | orig = hotplug_en = REG_READ(PORT_HOTPLUG_EN); |
6a227d5f AC |
149 | hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); |
150 | hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; | |
151 | ||
152 | hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; | |
153 | hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; | |
154 | ||
155 | for (i = 0; i < tries ; i++) { | |
156 | unsigned long timeout; | |
157 | /* turn on the FORCE_DETECT */ | |
158 | REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); | |
159 | timeout = jiffies + msecs_to_jiffies(1000); | |
160 | /* wait for FORCE_DETECT to go off */ | |
161 | do { | |
162 | if (!(REG_READ(PORT_HOTPLUG_EN) & | |
163 | CRT_HOTPLUG_FORCE_DETECT)) | |
164 | break; | |
165 | msleep(1); | |
166 | } while (time_after(timeout, jiffies)); | |
167 | } | |
168 | ||
169 | if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != | |
170 | CRT_HOTPLUG_MONITOR_NONE) | |
171 | ret = true; | |
172 | ||
d235e64a AC |
173 | /* clear the interrupt we just generated, if any */ |
174 | REG_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); | |
175 | ||
176 | /* and put the bits back */ | |
177 | REG_WRITE(PORT_HOTPLUG_EN, orig); | |
6a227d5f AC |
178 | return ret; |
179 | } | |
180 | ||
181 | static enum drm_connector_status cdv_intel_crt_detect( | |
182 | struct drm_connector *connector, bool force) | |
183 | { | |
184 | if (cdv_intel_crt_detect_hotplug(connector, force)) | |
185 | return connector_status_connected; | |
186 | else | |
187 | return connector_status_disconnected; | |
188 | } | |
189 | ||
190 | static void cdv_intel_crt_destroy(struct drm_connector *connector) | |
191 | { | |
367e4408 | 192 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
6a227d5f | 193 | |
367e4408 | 194 | psb_intel_i2c_destroy(gma_encoder->ddc_bus); |
34ea3d38 | 195 | drm_connector_unregister(connector); |
6a227d5f AC |
196 | drm_connector_cleanup(connector); |
197 | kfree(connector); | |
198 | } | |
199 | ||
200 | static int cdv_intel_crt_get_modes(struct drm_connector *connector) | |
201 | { | |
367e4408 PJ |
202 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
203 | return psb_intel_ddc_get_modes(connector, | |
204 | &gma_encoder->ddc_bus->adapter); | |
6a227d5f AC |
205 | } |
206 | ||
207 | static int cdv_intel_crt_set_property(struct drm_connector *connector, | |
208 | struct drm_property *property, | |
209 | uint64_t value) | |
210 | { | |
211 | return 0; | |
212 | } | |
213 | ||
214 | /* | |
215 | * Routines for controlling stuff on the analog port | |
216 | */ | |
217 | ||
218 | static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { | |
219 | .dpms = cdv_intel_crt_dpms, | |
c9d49590 PJ |
220 | .prepare = gma_encoder_prepare, |
221 | .commit = gma_encoder_commit, | |
6a227d5f AC |
222 | .mode_set = cdv_intel_crt_mode_set, |
223 | }; | |
224 | ||
225 | static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { | |
226 | .dpms = drm_helper_connector_dpms, | |
227 | .detect = cdv_intel_crt_detect, | |
228 | .fill_modes = drm_helper_probe_single_connector_modes, | |
229 | .destroy = cdv_intel_crt_destroy, | |
230 | .set_property = cdv_intel_crt_set_property, | |
231 | }; | |
232 | ||
233 | static const struct drm_connector_helper_funcs | |
234 | cdv_intel_crt_connector_helper_funcs = { | |
235 | .mode_valid = cdv_intel_crt_mode_valid, | |
236 | .get_modes = cdv_intel_crt_get_modes, | |
c9d49590 | 237 | .best_encoder = gma_best_encoder, |
6a227d5f AC |
238 | }; |
239 | ||
240 | static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) | |
241 | { | |
242 | drm_encoder_cleanup(encoder); | |
243 | } | |
244 | ||
245 | static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { | |
246 | .destroy = cdv_intel_crt_enc_destroy, | |
247 | }; | |
248 | ||
249 | void cdv_intel_crt_init(struct drm_device *dev, | |
250 | struct psb_intel_mode_device *mode_dev) | |
251 | { | |
252 | ||
a3d5d75f | 253 | struct gma_connector *gma_connector; |
367e4408 | 254 | struct gma_encoder *gma_encoder; |
6a227d5f AC |
255 | struct drm_connector *connector; |
256 | struct drm_encoder *encoder; | |
257 | ||
258 | u32 i2c_reg; | |
259 | ||
367e4408 PJ |
260 | gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); |
261 | if (!gma_encoder) | |
6a227d5f AC |
262 | return; |
263 | ||
a3d5d75f PJ |
264 | gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); |
265 | if (!gma_connector) | |
a12d6a07 PJ |
266 | goto failed_connector; |
267 | ||
a3d5d75f | 268 | connector = &gma_connector->base; |
bda50031 | 269 | connector->polled = DRM_CONNECTOR_POLL_HPD; |
6a227d5f AC |
270 | drm_connector_init(dev, connector, |
271 | &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); | |
272 | ||
367e4408 | 273 | encoder = &gma_encoder->base; |
6a227d5f | 274 | drm_encoder_init(dev, encoder, |
13a3d91f | 275 | &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC, NULL); |
6a227d5f | 276 | |
367e4408 | 277 | gma_connector_attach_encoder(gma_connector, gma_encoder); |
6a227d5f AC |
278 | |
279 | /* Set up the DDC bus. */ | |
280 | i2c_reg = GPIOA; | |
281 | /* Remove the following code for CDV */ | |
282 | /* | |
283 | if (dev_priv->crt_ddc_bus != 0) | |
284 | i2c_reg = dev_priv->crt_ddc_bus; | |
285 | }*/ | |
367e4408 | 286 | gma_encoder->ddc_bus = psb_intel_i2c_create(dev, |
a12d6a07 | 287 | i2c_reg, "CRTDDC_A"); |
367e4408 | 288 | if (!gma_encoder->ddc_bus) { |
6a227d5f AC |
289 | dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " |
290 | "failed.\n"); | |
291 | goto failed_ddc; | |
292 | } | |
293 | ||
367e4408 | 294 | gma_encoder->type = INTEL_OUTPUT_ANALOG; |
6a227d5f AC |
295 | /* |
296 | psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); | |
297 | psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); | |
298 | */ | |
299 | connector->interlace_allowed = 0; | |
300 | connector->doublescan_allowed = 0; | |
301 | ||
302 | drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); | |
303 | drm_connector_helper_add(connector, | |
304 | &cdv_intel_crt_connector_helper_funcs); | |
305 | ||
34ea3d38 | 306 | drm_connector_register(connector); |
6a227d5f AC |
307 | |
308 | return; | |
309 | failed_ddc: | |
367e4408 | 310 | drm_encoder_cleanup(&gma_encoder->base); |
a3d5d75f PJ |
311 | drm_connector_cleanup(&gma_connector->base); |
312 | kfree(gma_connector); | |
a12d6a07 | 313 | failed_connector: |
367e4408 | 314 | kfree(gma_encoder); |
6a227d5f AC |
315 | return; |
316 | } |