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