diff options
author | Alan Cox <alan@linux.intel.com> | 2011-11-03 14:22:37 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-11-16 06:27:35 -0500 |
commit | 6a227d5fd6c4abe6a9226a40f6981825e9da5fbe (patch) | |
tree | c9bf4f59f45c84f668d15de32ac7d6420e700578 /drivers/gpu/drm/gma500/cdv_intel_lvds.c | |
parent | 1b082ccf5901108d3acd860a73d8c0442556c0bb (diff) |
gma500: Add support for Cedarview
Again this is similar but has some differences so we have a set of plug in
support. This does make the driver bigger than is needed in some respects
but the tradeoff for maintainability is huge.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/gma500/cdv_intel_lvds.c')
-rw-r--r-- | drivers/gpu/drm/gma500/cdv_intel_lvds.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c new file mode 100644 index 000000000000..988b2d0acf43 --- /dev/null +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c | |||
@@ -0,0 +1,721 @@ | |||
1 | /* | ||
2 | * Copyright © 2006-2011 Intel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * Authors: | ||
18 | * Eric Anholt <eric@anholt.net> | ||
19 | * Dave Airlie <airlied@linux.ie> | ||
20 | * Jesse Barnes <jesse.barnes@intel.com> | ||
21 | */ | ||
22 | |||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/dmi.h> | ||
25 | #include <drm/drmP.h> | ||
26 | |||
27 | #include "intel_bios.h" | ||
28 | #include "psb_drv.h" | ||
29 | #include "psb_intel_drv.h" | ||
30 | #include "psb_intel_reg.h" | ||
31 | #include "power.h" | ||
32 | #include <linux/pm_runtime.h> | ||
33 | #include "cdv_device.h" | ||
34 | |||
35 | /** | ||
36 | * LVDS I2C backlight control macros | ||
37 | */ | ||
38 | #define BRIGHTNESS_MAX_LEVEL 100 | ||
39 | #define BRIGHTNESS_MASK 0xFF | ||
40 | #define BLC_I2C_TYPE 0x01 | ||
41 | #define BLC_PWM_TYPT 0x02 | ||
42 | |||
43 | #define BLC_POLARITY_NORMAL 0 | ||
44 | #define BLC_POLARITY_INVERSE 1 | ||
45 | |||
46 | #define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) | ||
47 | #define PSB_BLC_MIN_PWM_REG_FREQ (0x2) | ||
48 | #define PSB_BLC_PWM_PRECISION_FACTOR (10) | ||
49 | #define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) | ||
50 | #define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) | ||
51 | |||
52 | struct cdv_intel_lvds_priv { | ||
53 | /** | ||
54 | * Saved LVDO output states | ||
55 | */ | ||
56 | uint32_t savePP_ON; | ||
57 | uint32_t savePP_OFF; | ||
58 | uint32_t saveLVDS; | ||
59 | uint32_t savePP_CONTROL; | ||
60 | uint32_t savePP_CYCLE; | ||
61 | uint32_t savePFIT_CONTROL; | ||
62 | uint32_t savePFIT_PGM_RATIOS; | ||
63 | uint32_t saveBLC_PWM_CTL; | ||
64 | }; | ||
65 | |||
66 | /* | ||
67 | * Returns the maximum level of the backlight duty cycle field. | ||
68 | */ | ||
69 | static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev) | ||
70 | { | ||
71 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
72 | u32 retval; | ||
73 | |||
74 | if (gma_power_begin(dev, false)) { | ||
75 | retval = ((REG_READ(BLC_PWM_CTL) & | ||
76 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
77 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
78 | |||
79 | gma_power_end(dev); | ||
80 | } else | ||
81 | retval = ((dev_priv->saveBLC_PWM_CTL & | ||
82 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
83 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
84 | |||
85 | return retval; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Set LVDS backlight level by I2C command | ||
90 | */ | ||
91 | static int cdv_lvds_i2c_set_brightness(struct drm_device *dev, | ||
92 | unsigned int level) | ||
93 | { | ||
94 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
95 | struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; | ||
96 | u8 out_buf[2]; | ||
97 | unsigned int blc_i2c_brightness; | ||
98 | |||
99 | struct i2c_msg msgs[] = { | ||
100 | { | ||
101 | .addr = lvds_i2c_bus->slave_addr, | ||
102 | .flags = 0, | ||
103 | .len = 2, | ||
104 | .buf = out_buf, | ||
105 | } | ||
106 | }; | ||
107 | |||
108 | blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level * | ||
109 | BRIGHTNESS_MASK / | ||
110 | BRIGHTNESS_MAX_LEVEL); | ||
111 | |||
112 | if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) | ||
113 | blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness; | ||
114 | |||
115 | out_buf[0] = dev_priv->lvds_bl->brightnesscmd; | ||
116 | out_buf[1] = (u8)blc_i2c_brightness; | ||
117 | |||
118 | if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) | ||
119 | return 0; | ||
120 | |||
121 | DRM_ERROR("I2C transfer error\n"); | ||
122 | return -1; | ||
123 | } | ||
124 | |||
125 | |||
126 | static int cdv_lvds_pwm_set_brightness(struct drm_device *dev, int level) | ||
127 | { | ||
128 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
129 | |||
130 | u32 max_pwm_blc; | ||
131 | u32 blc_pwm_duty_cycle; | ||
132 | |||
133 | max_pwm_blc = cdv_intel_lvds_get_max_backlight(dev); | ||
134 | |||
135 | /*BLC_PWM_CTL Should be initiated while backlight device init*/ | ||
136 | BUG_ON((max_pwm_blc & PSB_BLC_MAX_PWM_REG_FREQ) == 0); | ||
137 | |||
138 | blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL; | ||
139 | |||
140 | if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) | ||
141 | blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle; | ||
142 | |||
143 | blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; | ||
144 | REG_WRITE(BLC_PWM_CTL, | ||
145 | (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | | ||
146 | (blc_pwm_duty_cycle)); | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Set LVDS backlight level either by I2C or PWM | ||
153 | */ | ||
154 | void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level) | ||
155 | { | ||
156 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
157 | |||
158 | if (!dev_priv->lvds_bl) { | ||
159 | DRM_ERROR("NO LVDS Backlight Info\n"); | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | if (dev_priv->lvds_bl->type == BLC_I2C_TYPE) | ||
164 | cdv_lvds_i2c_set_brightness(dev, level); | ||
165 | else | ||
166 | cdv_lvds_pwm_set_brightness(dev, level); | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Sets the backlight level. | ||
171 | * | ||
172 | * level backlight level, from 0 to cdv_intel_lvds_get_max_backlight(). | ||
173 | */ | ||
174 | static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level) | ||
175 | { | ||
176 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
177 | u32 blc_pwm_ctl; | ||
178 | |||
179 | if (gma_power_begin(dev, false)) { | ||
180 | blc_pwm_ctl = | ||
181 | REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; | ||
182 | REG_WRITE(BLC_PWM_CTL, | ||
183 | (blc_pwm_ctl | | ||
184 | (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); | ||
185 | gma_power_end(dev); | ||
186 | } else { | ||
187 | blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & | ||
188 | ~BACKLIGHT_DUTY_CYCLE_MASK; | ||
189 | dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | | ||
190 | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Sets the power state for the panel. | ||
196 | */ | ||
197 | static void cdv_intel_lvds_set_power(struct drm_device *dev, | ||
198 | struct psb_intel_output *output, bool on) | ||
199 | { | ||
200 | u32 pp_status; | ||
201 | |||
202 | if (!gma_power_begin(dev, true)) | ||
203 | return; | ||
204 | |||
205 | if (on) { | ||
206 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | | ||
207 | POWER_TARGET_ON); | ||
208 | do { | ||
209 | pp_status = REG_READ(PP_STATUS); | ||
210 | } while ((pp_status & PP_ON) == 0); | ||
211 | |||
212 | cdv_intel_lvds_set_backlight(dev, | ||
213 | output-> | ||
214 | mode_dev->backlight_duty_cycle); | ||
215 | } else { | ||
216 | cdv_intel_lvds_set_backlight(dev, 0); | ||
217 | |||
218 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & | ||
219 | ~POWER_TARGET_ON); | ||
220 | do { | ||
221 | pp_status = REG_READ(PP_STATUS); | ||
222 | } while (pp_status & PP_ON); | ||
223 | } | ||
224 | gma_power_end(dev); | ||
225 | } | ||
226 | |||
227 | static void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
228 | { | ||
229 | struct drm_device *dev = encoder->dev; | ||
230 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
231 | if (mode == DRM_MODE_DPMS_ON) | ||
232 | cdv_intel_lvds_set_power(dev, output, true); | ||
233 | else | ||
234 | cdv_intel_lvds_set_power(dev, output, false); | ||
235 | /* XXX: We never power down the LVDS pairs. */ | ||
236 | } | ||
237 | |||
238 | static void cdv_intel_lvds_save(struct drm_connector *connector) | ||
239 | { | ||
240 | } | ||
241 | |||
242 | static void cdv_intel_lvds_restore(struct drm_connector *connector) | ||
243 | { | ||
244 | } | ||
245 | |||
246 | int cdv_intel_lvds_mode_valid(struct drm_connector *connector, | ||
247 | struct drm_display_mode *mode) | ||
248 | { | ||
249 | struct psb_intel_output *psb_intel_output = | ||
250 | to_psb_intel_output(connector); | ||
251 | struct drm_display_mode *fixed_mode = | ||
252 | psb_intel_output->mode_dev->panel_fixed_mode; | ||
253 | |||
254 | /* just in case */ | ||
255 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
256 | return MODE_NO_DBLESCAN; | ||
257 | |||
258 | /* just in case */ | ||
259 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
260 | return MODE_NO_INTERLACE; | ||
261 | |||
262 | if (fixed_mode) { | ||
263 | if (mode->hdisplay > fixed_mode->hdisplay) | ||
264 | return MODE_PANEL; | ||
265 | if (mode->vdisplay > fixed_mode->vdisplay) | ||
266 | return MODE_PANEL; | ||
267 | } | ||
268 | return MODE_OK; | ||
269 | } | ||
270 | |||
271 | bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, | ||
272 | struct drm_display_mode *mode, | ||
273 | struct drm_display_mode *adjusted_mode) | ||
274 | { | ||
275 | struct psb_intel_mode_device *mode_dev = | ||
276 | enc_to_psb_intel_output(encoder)->mode_dev; | ||
277 | struct drm_device *dev = encoder->dev; | ||
278 | struct drm_encoder *tmp_encoder; | ||
279 | struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; | ||
280 | |||
281 | /* Should never happen!! */ | ||
282 | list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, | ||
283 | head) { | ||
284 | if (tmp_encoder != encoder | ||
285 | && tmp_encoder->crtc == encoder->crtc) { | ||
286 | printk(KERN_ERR "Can't enable LVDS and another " | ||
287 | "encoder on the same pipe\n"); | ||
288 | return false; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * If we have timings from the BIOS for the panel, put them in | ||
294 | * to the adjusted mode. The CRTC will be set up for this mode, | ||
295 | * with the panel scaling set up to source from the H/VDisplay | ||
296 | * of the original mode. | ||
297 | */ | ||
298 | if (panel_fixed_mode != NULL) { | ||
299 | adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; | ||
300 | adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; | ||
301 | adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; | ||
302 | adjusted_mode->htotal = panel_fixed_mode->htotal; | ||
303 | adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; | ||
304 | adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; | ||
305 | adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; | ||
306 | adjusted_mode->vtotal = panel_fixed_mode->vtotal; | ||
307 | adjusted_mode->clock = panel_fixed_mode->clock; | ||
308 | drm_mode_set_crtcinfo(adjusted_mode, | ||
309 | CRTC_INTERLACE_HALVE_V); | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * XXX: It would be nice to support lower refresh rates on the | ||
314 | * panels to reduce power consumption, and perhaps match the | ||
315 | * user's requested refresh rate. | ||
316 | */ | ||
317 | |||
318 | return true; | ||
319 | } | ||
320 | |||
321 | static void cdv_intel_lvds_prepare(struct drm_encoder *encoder) | ||
322 | { | ||
323 | struct drm_device *dev = encoder->dev; | ||
324 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
325 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
326 | |||
327 | if (!gma_power_begin(dev, true)) | ||
328 | return; | ||
329 | |||
330 | mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); | ||
331 | mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & | ||
332 | BACKLIGHT_DUTY_CYCLE_MASK); | ||
333 | |||
334 | cdv_intel_lvds_set_power(dev, output, false); | ||
335 | |||
336 | gma_power_end(dev); | ||
337 | } | ||
338 | |||
339 | static void cdv_intel_lvds_commit(struct drm_encoder *encoder) | ||
340 | { | ||
341 | struct drm_device *dev = encoder->dev; | ||
342 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
343 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
344 | |||
345 | if (mode_dev->backlight_duty_cycle == 0) | ||
346 | mode_dev->backlight_duty_cycle = | ||
347 | cdv_intel_lvds_get_max_backlight(dev); | ||
348 | |||
349 | cdv_intel_lvds_set_power(dev, output, true); | ||
350 | } | ||
351 | |||
352 | static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, | ||
353 | struct drm_display_mode *mode, | ||
354 | struct drm_display_mode *adjusted_mode) | ||
355 | { | ||
356 | struct drm_device *dev = encoder->dev; | ||
357 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
358 | u32 pfit_control; | ||
359 | |||
360 | /* | ||
361 | * The LVDS pin pair will already have been turned on in the | ||
362 | * cdv_intel_crtc_mode_set since it has a large impact on the DPLL | ||
363 | * settings. | ||
364 | */ | ||
365 | |||
366 | /* | ||
367 | * Enable automatic panel scaling so that non-native modes fill the | ||
368 | * screen. Should be enabled before the pipe is enabled, according to | ||
369 | * register description and PRM. | ||
370 | */ | ||
371 | if (mode->hdisplay != adjusted_mode->hdisplay || | ||
372 | mode->vdisplay != adjusted_mode->vdisplay) | ||
373 | pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | | ||
374 | HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | | ||
375 | HORIZ_INTERP_BILINEAR); | ||
376 | else | ||
377 | pfit_control = 0; | ||
378 | |||
379 | if (dev_priv->lvds_dither) | ||
380 | pfit_control |= PANEL_8TO6_DITHER_ENABLE; | ||
381 | |||
382 | REG_WRITE(PFIT_CONTROL, pfit_control); | ||
383 | } | ||
384 | |||
385 | /** | ||
386 | * Detect the LVDS connection. | ||
387 | * | ||
388 | * This always returns CONNECTOR_STATUS_CONNECTED. | ||
389 | * This connector should only have | ||
390 | * been set up if the LVDS was actually connected anyway. | ||
391 | */ | ||
392 | static enum drm_connector_status cdv_intel_lvds_detect( | ||
393 | struct drm_connector *connector, bool force) | ||
394 | { | ||
395 | return connector_status_connected; | ||
396 | } | ||
397 | |||
398 | /** | ||
399 | * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. | ||
400 | */ | ||
401 | static int cdv_intel_lvds_get_modes(struct drm_connector *connector) | ||
402 | { | ||
403 | struct drm_device *dev = connector->dev; | ||
404 | struct psb_intel_output *psb_intel_output = | ||
405 | to_psb_intel_output(connector); | ||
406 | struct psb_intel_mode_device *mode_dev = | ||
407 | psb_intel_output->mode_dev; | ||
408 | int ret; | ||
409 | |||
410 | ret = psb_intel_ddc_get_modes(psb_intel_output); | ||
411 | |||
412 | if (ret) | ||
413 | return ret; | ||
414 | |||
415 | /* Didn't get an EDID, so | ||
416 | * Set wide sync ranges so we get all modes | ||
417 | * handed to valid_mode for checking | ||
418 | */ | ||
419 | connector->display_info.min_vfreq = 0; | ||
420 | connector->display_info.max_vfreq = 200; | ||
421 | connector->display_info.min_hfreq = 0; | ||
422 | connector->display_info.max_hfreq = 200; | ||
423 | if (mode_dev->panel_fixed_mode != NULL) { | ||
424 | struct drm_display_mode *mode = | ||
425 | drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); | ||
426 | drm_mode_probed_add(connector, mode); | ||
427 | return 1; | ||
428 | } | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | /** | ||
434 | * cdv_intel_lvds_destroy - unregister and free LVDS structures | ||
435 | * @connector: connector to free | ||
436 | * | ||
437 | * Unregister the DDC bus for this connector then free the driver private | ||
438 | * structure. | ||
439 | */ | ||
440 | void cdv_intel_lvds_destroy(struct drm_connector *connector) | ||
441 | { | ||
442 | struct psb_intel_output *psb_intel_output = | ||
443 | to_psb_intel_output(connector); | ||
444 | |||
445 | if (psb_intel_output->ddc_bus) | ||
446 | psb_intel_i2c_destroy(psb_intel_output->ddc_bus); | ||
447 | drm_sysfs_connector_remove(connector); | ||
448 | drm_connector_cleanup(connector); | ||
449 | kfree(connector); | ||
450 | } | ||
451 | |||
452 | int cdv_intel_lvds_set_property(struct drm_connector *connector, | ||
453 | struct drm_property *property, | ||
454 | uint64_t value) | ||
455 | { | ||
456 | struct drm_encoder *encoder = connector->encoder; | ||
457 | |||
458 | if (!strcmp(property->name, "scaling mode") && encoder) { | ||
459 | struct psb_intel_crtc *crtc = | ||
460 | to_psb_intel_crtc(encoder->crtc); | ||
461 | uint64_t curValue; | ||
462 | |||
463 | if (!crtc) | ||
464 | return -1; | ||
465 | |||
466 | switch (value) { | ||
467 | case DRM_MODE_SCALE_FULLSCREEN: | ||
468 | break; | ||
469 | case DRM_MODE_SCALE_NO_SCALE: | ||
470 | break; | ||
471 | case DRM_MODE_SCALE_ASPECT: | ||
472 | break; | ||
473 | default: | ||
474 | return -1; | ||
475 | } | ||
476 | |||
477 | if (drm_connector_property_get_value(connector, | ||
478 | property, | ||
479 | &curValue)) | ||
480 | return -1; | ||
481 | |||
482 | if (curValue == value) | ||
483 | return 0; | ||
484 | |||
485 | if (drm_connector_property_set_value(connector, | ||
486 | property, | ||
487 | value)) | ||
488 | return -1; | ||
489 | |||
490 | if (crtc->saved_mode.hdisplay != 0 && | ||
491 | crtc->saved_mode.vdisplay != 0) { | ||
492 | if (!drm_crtc_helper_set_mode(encoder->crtc, | ||
493 | &crtc->saved_mode, | ||
494 | encoder->crtc->x, | ||
495 | encoder->crtc->y, | ||
496 | encoder->crtc->fb)) | ||
497 | return -1; | ||
498 | } | ||
499 | } else if (!strcmp(property->name, "backlight") && encoder) { | ||
500 | if (drm_connector_property_set_value(connector, | ||
501 | property, | ||
502 | value)) | ||
503 | return -1; | ||
504 | else { | ||
505 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE | ||
506 | struct drm_psb_private *dev_priv = | ||
507 | encoder->dev->dev_private; | ||
508 | struct backlight_device *bd = | ||
509 | dev_priv->backlight_device; | ||
510 | bd->props.brightness = value; | ||
511 | backlight_update_status(bd); | ||
512 | #endif | ||
513 | } | ||
514 | } else if (!strcmp(property->name, "DPMS") && encoder) { | ||
515 | struct drm_encoder_helper_funcs *helpers = | ||
516 | encoder->helper_private; | ||
517 | helpers->dpms(encoder, value); | ||
518 | } | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static const struct drm_encoder_helper_funcs | ||
523 | cdv_intel_lvds_helper_funcs = { | ||
524 | .dpms = cdv_intel_lvds_encoder_dpms, | ||
525 | .mode_fixup = cdv_intel_lvds_mode_fixup, | ||
526 | .prepare = cdv_intel_lvds_prepare, | ||
527 | .mode_set = cdv_intel_lvds_mode_set, | ||
528 | .commit = cdv_intel_lvds_commit, | ||
529 | }; | ||
530 | |||
531 | static const struct drm_connector_helper_funcs | ||
532 | cdv_intel_lvds_connector_helper_funcs = { | ||
533 | .get_modes = cdv_intel_lvds_get_modes, | ||
534 | .mode_valid = cdv_intel_lvds_mode_valid, | ||
535 | .best_encoder = psb_intel_best_encoder, | ||
536 | }; | ||
537 | |||
538 | static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = { | ||
539 | .dpms = drm_helper_connector_dpms, | ||
540 | .save = cdv_intel_lvds_save, | ||
541 | .restore = cdv_intel_lvds_restore, | ||
542 | .detect = cdv_intel_lvds_detect, | ||
543 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
544 | .set_property = cdv_intel_lvds_set_property, | ||
545 | .destroy = cdv_intel_lvds_destroy, | ||
546 | }; | ||
547 | |||
548 | |||
549 | static void cdv_intel_lvds_enc_destroy(struct drm_encoder *encoder) | ||
550 | { | ||
551 | drm_encoder_cleanup(encoder); | ||
552 | } | ||
553 | |||
554 | const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { | ||
555 | .destroy = cdv_intel_lvds_enc_destroy, | ||
556 | }; | ||
557 | |||
558 | /** | ||
559 | * cdv_intel_lvds_init - setup LVDS connectors on this device | ||
560 | * @dev: drm device | ||
561 | * | ||
562 | * Create the connector, register the LVDS DDC bus, and try to figure out what | ||
563 | * modes we can display on the LVDS panel (if present). | ||
564 | */ | ||
565 | void cdv_intel_lvds_init(struct drm_device *dev, | ||
566 | struct psb_intel_mode_device *mode_dev) | ||
567 | { | ||
568 | struct psb_intel_output *psb_intel_output; | ||
569 | struct cdv_intel_lvds_priv *lvds_priv; | ||
570 | struct drm_connector *connector; | ||
571 | struct drm_encoder *encoder; | ||
572 | struct drm_display_mode *scan; | ||
573 | struct drm_crtc *crtc; | ||
574 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
575 | u32 lvds; | ||
576 | int pipe; | ||
577 | |||
578 | psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + | ||
579 | sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); | ||
580 | if (!psb_intel_output) | ||
581 | return; | ||
582 | |||
583 | lvds_priv = (struct cdv_intel_lvds_priv *)(psb_intel_output + 1); | ||
584 | |||
585 | psb_intel_output->dev_priv = lvds_priv; | ||
586 | |||
587 | psb_intel_output->mode_dev = mode_dev; | ||
588 | connector = &psb_intel_output->base; | ||
589 | encoder = &psb_intel_output->enc; | ||
590 | |||
591 | |||
592 | drm_connector_init(dev, &psb_intel_output->base, | ||
593 | &cdv_intel_lvds_connector_funcs, | ||
594 | DRM_MODE_CONNECTOR_LVDS); | ||
595 | |||
596 | drm_encoder_init(dev, &psb_intel_output->enc, | ||
597 | &cdv_intel_lvds_enc_funcs, | ||
598 | DRM_MODE_ENCODER_LVDS); | ||
599 | |||
600 | |||
601 | drm_mode_connector_attach_encoder(&psb_intel_output->base, | ||
602 | &psb_intel_output->enc); | ||
603 | psb_intel_output->type = INTEL_OUTPUT_LVDS; | ||
604 | |||
605 | drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs); | ||
606 | drm_connector_helper_add(connector, | ||
607 | &cdv_intel_lvds_connector_helper_funcs); | ||
608 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | ||
609 | connector->interlace_allowed = false; | ||
610 | connector->doublescan_allowed = false; | ||
611 | |||
612 | /*Attach connector properties*/ | ||
613 | drm_connector_attach_property(connector, | ||
614 | dev->mode_config.scaling_mode_property, | ||
615 | DRM_MODE_SCALE_FULLSCREEN); | ||
616 | drm_connector_attach_property(connector, | ||
617 | dev_priv->backlight_property, | ||
618 | BRIGHTNESS_MAX_LEVEL); | ||
619 | |||
620 | /** | ||
621 | * Set up I2C bus | ||
622 | * FIXME: distroy i2c_bus when exit | ||
623 | */ | ||
624 | psb_intel_output->i2c_bus = psb_intel_i2c_create(dev, | ||
625 | GPIOB, | ||
626 | "LVDSBLC_B"); | ||
627 | if (!psb_intel_output->i2c_bus) { | ||
628 | dev_printk(KERN_ERR, | ||
629 | &dev->pdev->dev, "I2C bus registration failed.\n"); | ||
630 | goto failed_blc_i2c; | ||
631 | } | ||
632 | psb_intel_output->i2c_bus->slave_addr = 0x2C; | ||
633 | dev_priv->lvds_i2c_bus = psb_intel_output->i2c_bus; | ||
634 | |||
635 | /* | ||
636 | * LVDS discovery: | ||
637 | * 1) check for EDID on DDC | ||
638 | * 2) check for VBT data | ||
639 | * 3) check to see if LVDS is already on | ||
640 | * if none of the above, no panel | ||
641 | * 4) make sure lid is open | ||
642 | * if closed, act like it's not there for now | ||
643 | */ | ||
644 | |||
645 | /* Set up the DDC bus. */ | ||
646 | psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, | ||
647 | GPIOC, | ||
648 | "LVDSDDC_C"); | ||
649 | if (!psb_intel_output->ddc_bus) { | ||
650 | dev_printk(KERN_ERR, &dev->pdev->dev, | ||
651 | "DDC bus registration " "failed.\n"); | ||
652 | goto failed_ddc; | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * Attempt to get the fixed panel mode from DDC. Assume that the | ||
657 | * preferred mode is the right one. | ||
658 | */ | ||
659 | psb_intel_ddc_get_modes(psb_intel_output); | ||
660 | list_for_each_entry(scan, &connector->probed_modes, head) { | ||
661 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | ||
662 | mode_dev->panel_fixed_mode = | ||
663 | drm_mode_duplicate(dev, scan); | ||
664 | goto out; /* FIXME: check for quirks */ | ||
665 | } | ||
666 | } | ||
667 | |||
668 | /* Failed to get EDID, what about VBT? do we need this?*/ | ||
669 | if (dev_priv->lfp_lvds_vbt_mode) { | ||
670 | mode_dev->panel_fixed_mode = | ||
671 | drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); | ||
672 | if (mode_dev->panel_fixed_mode) { | ||
673 | mode_dev->panel_fixed_mode->type |= | ||
674 | DRM_MODE_TYPE_PREFERRED; | ||
675 | goto out; /* FIXME: check for quirks */ | ||
676 | } | ||
677 | } | ||
678 | /* | ||
679 | * If we didn't get EDID, try checking if the panel is already turned | ||
680 | * on. If so, assume that whatever is currently programmed is the | ||
681 | * correct mode. | ||
682 | */ | ||
683 | lvds = REG_READ(LVDS); | ||
684 | pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; | ||
685 | crtc = psb_intel_get_crtc_from_pipe(dev, pipe); | ||
686 | |||
687 | if (crtc && (lvds & LVDS_PORT_EN)) { | ||
688 | mode_dev->panel_fixed_mode = | ||
689 | cdv_intel_crtc_mode_get(dev, crtc); | ||
690 | if (mode_dev->panel_fixed_mode) { | ||
691 | mode_dev->panel_fixed_mode->type |= | ||
692 | DRM_MODE_TYPE_PREFERRED; | ||
693 | goto out; /* FIXME: check for quirks */ | ||
694 | } | ||
695 | } | ||
696 | |||
697 | /* If we still don't have a mode after all that, give up. */ | ||
698 | if (!mode_dev->panel_fixed_mode) { | ||
699 | DRM_DEBUG | ||
700 | ("Found no modes on the lvds, ignoring the LVDS\n"); | ||
701 | goto failed_find; | ||
702 | } | ||
703 | |||
704 | out: | ||
705 | drm_sysfs_connector_add(connector); | ||
706 | return; | ||
707 | |||
708 | failed_find: | ||
709 | printk(KERN_ERR "Failed find\n"); | ||
710 | if (psb_intel_output->ddc_bus) | ||
711 | psb_intel_i2c_destroy(psb_intel_output->ddc_bus); | ||
712 | failed_ddc: | ||
713 | printk(KERN_ERR "Failed DDC\n"); | ||
714 | if (psb_intel_output->i2c_bus) | ||
715 | psb_intel_i2c_destroy(psb_intel_output->i2c_bus); | ||
716 | failed_blc_i2c: | ||
717 | printk(KERN_ERR "Failed BLC\n"); | ||
718 | drm_encoder_cleanup(encoder); | ||
719 | drm_connector_cleanup(connector); | ||
720 | kfree(connector); | ||
721 | } | ||