1 | /* |
2 | * Copyright © 2006 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 | |
28 | #include "dvo.h" |
29 | |
30 | /* |
31 | * register definitions for the i82807aa. |
32 | * |
33 | * Documentation on this chipset can be found in datasheet #29069001 at |
34 | * intel.com. |
35 | */ |
36 | |
37 | /* |
38 | * VCH Revision & GMBus Base Addr |
39 | */ |
40 | #define VR00 0x00 |
41 | # define VR00_BASE_ADDRESS_MASK 0x007f |
42 | |
43 | /* |
44 | * Functionality Enable |
45 | */ |
46 | #define VR01 0x01 |
47 | |
48 | /* |
49 | * Enable the panel fitter |
50 | */ |
51 | # define VR01_PANEL_FIT_ENABLE (1 << 3) |
52 | /* |
53 | * Enables the LCD display. |
54 | * |
55 | * This must not be set while VR01_DVO_BYPASS_ENABLE is set. |
56 | */ |
57 | # define VR01_LCD_ENABLE (1 << 2) |
58 | /** Enables the DVO repeater. */ |
59 | # define VR01_DVO_BYPASS_ENABLE (1 << 1) |
60 | /** Enables the DVO clock */ |
61 | # define VR01_DVO_ENABLE (1 << 0) |
62 | |
63 | /* |
64 | * LCD Interface Format |
65 | */ |
66 | #define VR10 0x10 |
67 | /** Enables LVDS output instead of CMOS */ |
68 | # define VR10_LVDS_ENABLE (1 << 4) |
69 | /** Enables 18-bit LVDS output. */ |
70 | # define VR10_INTERFACE_1X18 (0 << 2) |
71 | /** Enables 24-bit LVDS or CMOS output */ |
72 | # define VR10_INTERFACE_1X24 (1 << 2) |
73 | /** Enables 2x18-bit LVDS or CMOS output. */ |
74 | # define VR10_INTERFACE_2X18 (2 << 2) |
75 | /** Enables 2x24-bit LVDS output */ |
76 | # define VR10_INTERFACE_2X24 (3 << 2) |
77 | |
78 | /* |
79 | * VR20 LCD Horizontal Display Size |
80 | */ |
81 | #define VR20 0x20 |
82 | |
83 | /* |
84 | * LCD Vertical Display Size |
85 | */ |
86 | #define VR21 0x20 |
87 | |
88 | /* |
89 | * Panel power down status |
90 | */ |
91 | #define VR30 0x30 |
92 | /** Read only bit indicating that the panel is not in a safe poweroff state. */ |
93 | # define VR30_PANEL_ON (1 << 15) |
94 | |
95 | #define VR40 0x40 |
96 | # define VR40_STALL_ENABLE (1 << 13) |
97 | # define VR40_VERTICAL_INTERP_ENABLE (1 << 12) |
98 | # define VR40_ENHANCED_PANEL_FITTING (1 << 11) |
99 | # define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10) |
100 | # define VR40_AUTO_RATIO_ENABLE (1 << 9) |
101 | # define VR40_CLOCK_GATING_ENABLE (1 << 8) |
102 | |
103 | /* |
104 | * Panel Fitting Vertical Ratio |
105 | * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2 |
106 | */ |
107 | #define VR41 0x41 |
108 | |
109 | /* |
110 | * Panel Fitting Horizontal Ratio |
111 | * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2 |
112 | */ |
113 | #define VR42 0x42 |
114 | |
115 | /* |
116 | * Horizontal Image Size |
117 | */ |
118 | #define VR43 0x43 |
119 | |
120 | /* VR80 GPIO 0 |
121 | */ |
122 | #define VR80 0x80 |
123 | #define VR81 0x81 |
124 | #define VR82 0x82 |
125 | #define VR83 0x83 |
126 | #define VR84 0x84 |
127 | #define VR85 0x85 |
128 | #define VR86 0x86 |
129 | #define VR87 0x87 |
130 | |
131 | /* VR88 GPIO 8 |
132 | */ |
133 | #define VR88 0x88 |
134 | |
135 | /* Graphics BIOS scratch 0 |
136 | */ |
137 | #define VR8E 0x8E |
138 | # define VR8E_PANEL_TYPE_MASK (0xf << 0) |
139 | # define VR8E_PANEL_INTERFACE_CMOS (0 << 4) |
140 | # define VR8E_PANEL_INTERFACE_LVDS (1 << 4) |
141 | # define VR8E_FORCE_DEFAULT_PANEL (1 << 5) |
142 | |
143 | /* Graphics BIOS scratch 1 |
144 | */ |
145 | #define VR8F 0x8F |
146 | # define VR8F_VCH_PRESENT (1 << 0) |
147 | # define VR8F_DISPLAY_CONN (1 << 1) |
148 | # define VR8F_POWER_MASK (0x3c) |
149 | # define VR8F_POWER_POS (2) |
150 | |
151 | |
152 | struct ivch_priv { |
153 | bool quiet; |
154 | |
155 | uint16_t width, height; |
156 | }; |
157 | |
158 | |
159 | static void ivch_dump_regs(struct intel_dvo_device *dvo); |
160 | |
161 | /** |
162 | * Reads a register on the ivch. |
163 | * |
164 | * Each of the 256 registers are 16 bits long. |
165 | */ |
166 | static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) |
167 | { |
168 | struct ivch_priv *priv = dvo->dev_priv; |
169 | struct i2c_adapter *adapter = dvo->i2c_bus; |
170 | u8 out_buf[1]; |
171 | u8 in_buf[2]; |
172 | |
173 | struct i2c_msg msgs[] = { |
174 | { |
175 | .addr = dvo->slave_addr, |
176 | .flags = I2C_M_RD, |
177 | .len = 0, |
178 | }, |
179 | { |
180 | .addr = 0, |
181 | .flags = I2C_M_NOSTART, |
182 | .len = 1, |
183 | .buf = out_buf, |
184 | }, |
185 | { |
186 | .addr = dvo->slave_addr, |
187 | .flags = I2C_M_RD | I2C_M_NOSTART, |
188 | .len = 2, |
189 | .buf = in_buf, |
190 | } |
191 | }; |
192 | |
193 | out_buf[0] = addr; |
194 | |
195 | if (i2c_transfer(adapter, msgs, 3) == 3) { |
196 | *data = (in_buf[1] << 8) | in_buf[0]; |
197 | return true; |
198 | }; |
199 | |
200 | if (!priv->quiet) { |
201 | DRM_DEBUG_KMS("Unable to read register 0x%02x from " |
202 | "%s:%02x.\n" , |
203 | addr, adapter->name, dvo->slave_addr); |
204 | } |
205 | return false; |
206 | } |
207 | |
208 | /** Writes a 16-bit register on the ivch */ |
209 | static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) |
210 | { |
211 | struct ivch_priv *priv = dvo->dev_priv; |
212 | struct i2c_adapter *adapter = dvo->i2c_bus; |
213 | u8 out_buf[3]; |
214 | struct i2c_msg msg = { |
215 | .addr = dvo->slave_addr, |
216 | .flags = 0, |
217 | .len = 3, |
218 | .buf = out_buf, |
219 | }; |
220 | |
221 | out_buf[0] = addr; |
222 | out_buf[1] = data & 0xff; |
223 | out_buf[2] = data >> 8; |
224 | |
225 | if (i2c_transfer(adapter, &msg, 1) == 1) |
226 | return true; |
227 | |
228 | if (!priv->quiet) { |
229 | DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n" , |
230 | addr, adapter->name, dvo->slave_addr); |
231 | } |
232 | |
233 | return false; |
234 | } |
235 | |
236 | /** Probes the given bus and slave address for an ivch */ |
237 | static bool ivch_init(struct intel_dvo_device *dvo, |
238 | struct i2c_adapter *adapter) |
239 | { |
240 | struct ivch_priv *priv; |
241 | uint16_t temp; |
242 | |
243 | priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL); |
244 | if (priv == NULL) |
245 | return false; |
246 | |
247 | dvo->i2c_bus = adapter; |
248 | dvo->dev_priv = priv; |
249 | priv->quiet = true; |
250 | |
251 | if (!ivch_read(dvo, VR00, &temp)) |
252 | goto out; |
253 | priv->quiet = false; |
254 | |
255 | /* Since the identification bits are probably zeroes, which doesn't seem |
256 | * very unique, check that the value in the base address field matches |
257 | * the address it's responding on. |
258 | */ |
259 | if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) { |
260 | DRM_DEBUG_KMS("ivch detect failed due to address mismatch " |
261 | "(%d vs %d)\n" , |
262 | (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr); |
263 | goto out; |
264 | } |
265 | |
266 | ivch_read(dvo, VR20, &priv->width); |
267 | ivch_read(dvo, VR21, &priv->height); |
268 | |
269 | return true; |
270 | |
271 | out: |
272 | kfree(priv); |
273 | return false; |
274 | } |
275 | |
276 | static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo) |
277 | { |
278 | return connector_status_connected; |
279 | } |
280 | |
281 | static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, |
282 | struct drm_display_mode *mode) |
283 | { |
284 | if (mode->clock > 112000) |
285 | return MODE_CLOCK_HIGH; |
286 | |
287 | return MODE_OK; |
288 | } |
289 | |
290 | /** Sets the power state of the panel connected to the ivch */ |
291 | static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) |
292 | { |
293 | int i; |
294 | uint16_t vr01, vr30, backlight; |
295 | |
296 | /* Set the new power state of the panel. */ |
297 | if (!ivch_read(dvo, VR01, &vr01)) |
298 | return; |
299 | |
300 | if (enable) |
301 | backlight = 1; |
302 | else |
303 | backlight = 0; |
304 | ivch_write(dvo, VR80, backlight); |
305 | |
306 | if (enable) |
307 | vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; |
308 | else |
309 | vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); |
310 | |
311 | ivch_write(dvo, VR01, vr01); |
312 | |
313 | /* Wait for the panel to make its state transition */ |
314 | for (i = 0; i < 100; i++) { |
315 | if (!ivch_read(dvo, VR30, &vr30)) |
316 | break; |
317 | |
318 | if (((vr30 & VR30_PANEL_ON) != 0) == enable) |
319 | break; |
320 | udelay(1000); |
321 | } |
322 | /* wait some more; vch may fail to resync sometimes without this */ |
323 | udelay(16 * 1000); |
324 | } |
325 | |
326 | static bool ivch_get_hw_state(struct intel_dvo_device *dvo) |
327 | { |
328 | uint16_t vr01; |
329 | |
330 | /* Set the new power state of the panel. */ |
331 | if (!ivch_read(dvo, VR01, &vr01)) |
332 | return false; |
333 | |
334 | if (vr01 & VR01_LCD_ENABLE) |
335 | return true; |
336 | else |
337 | return false; |
338 | } |
339 | |
340 | static void ivch_mode_set(struct intel_dvo_device *dvo, |
341 | struct drm_display_mode *mode, |
342 | struct drm_display_mode *adjusted_mode) |
343 | { |
344 | uint16_t vr40 = 0; |
345 | uint16_t vr01; |
346 | |
347 | vr01 = 0; |
348 | vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE | |
349 | VR40_HORIZONTAL_INTERP_ENABLE); |
350 | |
351 | if (mode->hdisplay != adjusted_mode->hdisplay || |
352 | mode->vdisplay != adjusted_mode->vdisplay) { |
353 | uint16_t x_ratio, y_ratio; |
354 | |
355 | vr01 |= VR01_PANEL_FIT_ENABLE; |
356 | vr40 |= VR40_CLOCK_GATING_ENABLE; |
357 | x_ratio = (((mode->hdisplay - 1) << 16) / |
358 | (adjusted_mode->hdisplay - 1)) >> 2; |
359 | y_ratio = (((mode->vdisplay - 1) << 16) / |
360 | (adjusted_mode->vdisplay - 1)) >> 2; |
361 | ivch_write(dvo, VR42, x_ratio); |
362 | ivch_write(dvo, VR41, y_ratio); |
363 | } else { |
364 | vr01 &= ~VR01_PANEL_FIT_ENABLE; |
365 | vr40 &= ~VR40_CLOCK_GATING_ENABLE; |
366 | } |
367 | vr40 &= ~VR40_AUTO_RATIO_ENABLE; |
368 | |
369 | ivch_write(dvo, VR01, vr01); |
370 | ivch_write(dvo, VR40, vr40); |
371 | |
372 | ivch_dump_regs(dvo); |
373 | } |
374 | |
375 | static void ivch_dump_regs(struct intel_dvo_device *dvo) |
376 | { |
377 | uint16_t val; |
378 | |
379 | ivch_read(dvo, VR00, &val); |
380 | DRM_DEBUG_KMS("VR00: 0x%04x\n" , val); |
381 | ivch_read(dvo, VR01, &val); |
382 | DRM_DEBUG_KMS("VR01: 0x%04x\n" , val); |
383 | ivch_read(dvo, VR30, &val); |
384 | DRM_DEBUG_KMS("VR30: 0x%04x\n" , val); |
385 | ivch_read(dvo, VR40, &val); |
386 | DRM_DEBUG_KMS("VR40: 0x%04x\n" , val); |
387 | |
388 | /* GPIO registers */ |
389 | ivch_read(dvo, VR80, &val); |
390 | DRM_DEBUG_KMS("VR80: 0x%04x\n" , val); |
391 | ivch_read(dvo, VR81, &val); |
392 | DRM_DEBUG_KMS("VR81: 0x%04x\n" , val); |
393 | ivch_read(dvo, VR82, &val); |
394 | DRM_DEBUG_KMS("VR82: 0x%04x\n" , val); |
395 | ivch_read(dvo, VR83, &val); |
396 | DRM_DEBUG_KMS("VR83: 0x%04x\n" , val); |
397 | ivch_read(dvo, VR84, &val); |
398 | DRM_DEBUG_KMS("VR84: 0x%04x\n" , val); |
399 | ivch_read(dvo, VR85, &val); |
400 | DRM_DEBUG_KMS("VR85: 0x%04x\n" , val); |
401 | ivch_read(dvo, VR86, &val); |
402 | DRM_DEBUG_KMS("VR86: 0x%04x\n" , val); |
403 | ivch_read(dvo, VR87, &val); |
404 | DRM_DEBUG_KMS("VR87: 0x%04x\n" , val); |
405 | ivch_read(dvo, VR88, &val); |
406 | DRM_DEBUG_KMS("VR88: 0x%04x\n" , val); |
407 | |
408 | /* Scratch register 0 - AIM Panel type */ |
409 | ivch_read(dvo, VR8E, &val); |
410 | DRM_DEBUG_KMS("VR8E: 0x%04x\n" , val); |
411 | |
412 | /* Scratch register 1 - Status register */ |
413 | ivch_read(dvo, VR8F, &val); |
414 | DRM_DEBUG_KMS("VR8F: 0x%04x\n" , val); |
415 | } |
416 | |
417 | static void ivch_destroy(struct intel_dvo_device *dvo) |
418 | { |
419 | struct ivch_priv *priv = dvo->dev_priv; |
420 | |
421 | if (priv) { |
422 | kfree(priv); |
423 | dvo->dev_priv = NULL; |
424 | } |
425 | } |
426 | |
427 | struct intel_dvo_dev_ops ivch_ops = { |
428 | .init = ivch_init, |
429 | .dpms = ivch_dpms, |
430 | .get_hw_state = ivch_get_hw_state, |
431 | .mode_valid = ivch_mode_valid, |
432 | .mode_set = ivch_mode_set, |
433 | .detect = ivch_detect, |
434 | .dump_regs = ivch_dump_regs, |
435 | .destroy = ivch_destroy, |
436 | }; |
437 | |