diff options
author | Alan Cox <alan@linux.intel.com> | 2011-11-03 14:22:26 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-11-16 06:27:12 -0500 |
commit | 1b082ccf5901108d3acd860a73d8c0442556c0bb (patch) | |
tree | d5b362913e21881a790490fe50228a13cb556be0 /drivers/gpu/drm/gma500/oaktrail_lvds.c | |
parent | 89c78134cc54dff016c83367912eb055637fa50c (diff) |
gma500: Add Oaktrail support
Oaktrail (GMA600) is found on some tablet/slate PC type systems. It's a bit
different to the GMA500 but similar enough it makes sense to plug it into
the same driver.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/gma500/oaktrail_lvds.c')
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_lvds.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c new file mode 100644 index 000000000000..a552226a08ff --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c | |||
@@ -0,0 +1,406 @@ | |||
1 | /* | ||
2 | * Copyright © 2006-2009 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 <drm/drmP.h> | ||
25 | #include <asm/mrst.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 | |||
34 | /* The max/min PWM frequency in BPCR[31:17] - */ | ||
35 | /* The smallest number is 1 (not 0) that can fit in the | ||
36 | * 15-bit field of the and then*/ | ||
37 | /* shifts to the left by one bit to get the actual 16-bit | ||
38 | * value that the 15-bits correspond to.*/ | ||
39 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF | ||
40 | #define BRIGHTNESS_MAX_LEVEL 100 | ||
41 | |||
42 | /** | ||
43 | * Sets the power state for the panel. | ||
44 | */ | ||
45 | static void oaktrail_lvds_set_power(struct drm_device *dev, | ||
46 | struct psb_intel_output *output, bool on) | ||
47 | { | ||
48 | u32 pp_status; | ||
49 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
50 | |||
51 | if (!gma_power_begin(dev, true)) | ||
52 | return; | ||
53 | |||
54 | if (on) { | ||
55 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | | ||
56 | POWER_TARGET_ON); | ||
57 | do { | ||
58 | pp_status = REG_READ(PP_STATUS); | ||
59 | } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); | ||
60 | dev_priv->is_lvds_on = true; | ||
61 | if (dev_priv->ops->lvds_bl_power) | ||
62 | dev_priv->ops->lvds_bl_power(dev, true); | ||
63 | } else { | ||
64 | if (dev_priv->ops->lvds_bl_power) | ||
65 | dev_priv->ops->lvds_bl_power(dev, false); | ||
66 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & | ||
67 | ~POWER_TARGET_ON); | ||
68 | do { | ||
69 | pp_status = REG_READ(PP_STATUS); | ||
70 | } while (pp_status & PP_ON); | ||
71 | dev_priv->is_lvds_on = false; | ||
72 | pm_request_idle(&dev->pdev->dev); | ||
73 | } | ||
74 | gma_power_end(dev); | ||
75 | } | ||
76 | |||
77 | static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode) | ||
78 | { | ||
79 | struct drm_device *dev = encoder->dev; | ||
80 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
81 | |||
82 | if (mode == DRM_MODE_DPMS_ON) | ||
83 | oaktrail_lvds_set_power(dev, output, true); | ||
84 | else | ||
85 | oaktrail_lvds_set_power(dev, output, false); | ||
86 | |||
87 | /* XXX: We never power down the LVDS pairs. */ | ||
88 | } | ||
89 | |||
90 | static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, | ||
91 | struct drm_display_mode *mode, | ||
92 | struct drm_display_mode *adjusted_mode) | ||
93 | { | ||
94 | struct psb_intel_mode_device *mode_dev = | ||
95 | enc_to_psb_intel_output(encoder)->mode_dev; | ||
96 | struct drm_device *dev = encoder->dev; | ||
97 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
98 | u32 lvds_port; | ||
99 | uint64_t v = DRM_MODE_SCALE_FULLSCREEN; | ||
100 | |||
101 | if (!gma_power_begin(dev, true)) | ||
102 | return; | ||
103 | |||
104 | /* | ||
105 | * The LVDS pin pair will already have been turned on in the | ||
106 | * psb_intel_crtc_mode_set since it has a large impact on the DPLL | ||
107 | * settings. | ||
108 | */ | ||
109 | lvds_port = (REG_READ(LVDS) & | ||
110 | (~LVDS_PIPEB_SELECT)) | | ||
111 | LVDS_PORT_EN | | ||
112 | LVDS_BORDER_EN; | ||
113 | |||
114 | /* If the firmware says dither on Moorestown, or the BIOS does | ||
115 | on Oaktrail then enable dithering */ | ||
116 | if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) | ||
117 | lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; | ||
118 | |||
119 | REG_WRITE(LVDS, lvds_port); | ||
120 | |||
121 | drm_connector_property_get_value( | ||
122 | &enc_to_psb_intel_output(encoder)->base, | ||
123 | dev->mode_config.scaling_mode_property, | ||
124 | &v); | ||
125 | |||
126 | if (v == DRM_MODE_SCALE_NO_SCALE) | ||
127 | REG_WRITE(PFIT_CONTROL, 0); | ||
128 | else if (v == DRM_MODE_SCALE_ASPECT) { | ||
129 | if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || | ||
130 | (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { | ||
131 | if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == | ||
132 | (mode->hdisplay * adjusted_mode->crtc_vdisplay)) | ||
133 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
134 | else if ((adjusted_mode->crtc_hdisplay * | ||
135 | mode->vdisplay) > (mode->hdisplay * | ||
136 | adjusted_mode->crtc_vdisplay)) | ||
137 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | ||
138 | PFIT_SCALING_MODE_PILLARBOX); | ||
139 | else | ||
140 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | ||
141 | PFIT_SCALING_MODE_LETTERBOX); | ||
142 | } else | ||
143 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
144 | } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ | ||
145 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | ||
146 | |||
147 | gma_power_end(dev); | ||
148 | } | ||
149 | |||
150 | static void oaktrail_lvds_prepare(struct drm_encoder *encoder) | ||
151 | { | ||
152 | struct drm_device *dev = encoder->dev; | ||
153 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
154 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
155 | |||
156 | if (!gma_power_begin(dev, true)) | ||
157 | return; | ||
158 | |||
159 | mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); | ||
160 | mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & | ||
161 | BACKLIGHT_DUTY_CYCLE_MASK); | ||
162 | oaktrail_lvds_set_power(dev, output, false); | ||
163 | gma_power_end(dev); | ||
164 | } | ||
165 | |||
166 | static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev) | ||
167 | { | ||
168 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
169 | u32 ret; | ||
170 | |||
171 | if (gma_power_begin(dev, false)) { | ||
172 | ret = ((REG_READ(BLC_PWM_CTL) & | ||
173 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
174 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
175 | |||
176 | gma_power_end(dev); | ||
177 | } else | ||
178 | ret = ((dev_priv->saveBLC_PWM_CTL & | ||
179 | BACKLIGHT_MODULATION_FREQ_MASK) >> | ||
180 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static void oaktrail_lvds_commit(struct drm_encoder *encoder) | ||
186 | { | ||
187 | struct drm_device *dev = encoder->dev; | ||
188 | struct psb_intel_output *output = enc_to_psb_intel_output(encoder); | ||
189 | struct psb_intel_mode_device *mode_dev = output->mode_dev; | ||
190 | |||
191 | if (mode_dev->backlight_duty_cycle == 0) | ||
192 | mode_dev->backlight_duty_cycle = | ||
193 | oaktrail_lvds_get_max_backlight(dev); | ||
194 | oaktrail_lvds_set_power(dev, output, true); | ||
195 | } | ||
196 | |||
197 | static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { | ||
198 | .dpms = oaktrail_lvds_dpms, | ||
199 | .mode_fixup = psb_intel_lvds_mode_fixup, | ||
200 | .prepare = oaktrail_lvds_prepare, | ||
201 | .mode_set = oaktrail_lvds_mode_set, | ||
202 | .commit = oaktrail_lvds_commit, | ||
203 | }; | ||
204 | |||
205 | static struct drm_display_mode lvds_configuration_modes[] = { | ||
206 | /* hard coded fixed mode for TPO LTPS LPJ040K001A */ | ||
207 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, | ||
208 | 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, | ||
209 | /* hard coded fixed mode for LVDS 800x480 */ | ||
210 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, | ||
211 | 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, | ||
212 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | ||
213 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, | ||
214 | 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, | ||
215 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | ||
216 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, | ||
217 | 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, | ||
218 | /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ | ||
219 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, | ||
220 | 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, | ||
221 | /* hard coded fixed mode for LVDS 1024x768 */ | ||
222 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, | ||
223 | 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, | ||
224 | /* hard coded fixed mode for LVDS 1366x768 */ | ||
225 | { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, | ||
226 | 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, | ||
227 | }; | ||
228 | |||
229 | /* Returns the panel fixed mode from configuration. */ | ||
230 | |||
231 | static struct drm_display_mode * | ||
232 | oaktrail_lvds_get_configuration_mode(struct drm_device *dev) | ||
233 | { | ||
234 | struct drm_display_mode *mode = NULL; | ||
235 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
236 | struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; | ||
237 | |||
238 | if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/ | ||
239 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); | ||
240 | if (!mode) | ||
241 | return NULL; | ||
242 | |||
243 | mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; | ||
244 | mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; | ||
245 | mode->hsync_start = mode->hdisplay + \ | ||
246 | ((ti->hsync_offset_hi << 8) | \ | ||
247 | ti->hsync_offset_lo); | ||
248 | mode->hsync_end = mode->hsync_start + \ | ||
249 | ((ti->hsync_pulse_width_hi << 8) | \ | ||
250 | ti->hsync_pulse_width_lo); | ||
251 | mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ | ||
252 | ti->hblank_lo); | ||
253 | mode->vsync_start = \ | ||
254 | mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ | ||
255 | ti->vsync_offset_lo); | ||
256 | mode->vsync_end = \ | ||
257 | mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ | ||
258 | ti->vsync_pulse_width_lo); | ||
259 | mode->vtotal = mode->vdisplay + \ | ||
260 | ((ti->vblank_hi << 8) | ti->vblank_lo); | ||
261 | mode->clock = ti->pixel_clock * 10; | ||
262 | #if 0 | ||
263 | printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); | ||
264 | printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); | ||
265 | printk(KERN_INFO "HSS is %d\n", mode->hsync_start); | ||
266 | printk(KERN_INFO "HSE is %d\n", mode->hsync_end); | ||
267 | printk(KERN_INFO "htotal is %d\n", mode->htotal); | ||
268 | printk(KERN_INFO "VSS is %d\n", mode->vsync_start); | ||
269 | printk(KERN_INFO "VSE is %d\n", mode->vsync_end); | ||
270 | printk(KERN_INFO "vtotal is %d\n", mode->vtotal); | ||
271 | printk(KERN_INFO "clock is %d\n", mode->clock); | ||
272 | #endif | ||
273 | } else | ||
274 | mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); | ||
275 | |||
276 | drm_mode_set_name(mode); | ||
277 | drm_mode_set_crtcinfo(mode, 0); | ||
278 | |||
279 | return mode; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * oaktrail_lvds_init - setup LVDS connectors on this device | ||
284 | * @dev: drm device | ||
285 | * | ||
286 | * Create the connector, register the LVDS DDC bus, and try to figure out what | ||
287 | * modes we can display on the LVDS panel (if present). | ||
288 | */ | ||
289 | void oaktrail_lvds_init(struct drm_device *dev, | ||
290 | struct psb_intel_mode_device *mode_dev) | ||
291 | { | ||
292 | struct psb_intel_output *psb_intel_output; | ||
293 | struct drm_connector *connector; | ||
294 | struct drm_encoder *encoder; | ||
295 | struct drm_psb_private *dev_priv = | ||
296 | (struct drm_psb_private *) dev->dev_private; | ||
297 | struct edid *edid; | ||
298 | int ret = 0; | ||
299 | struct i2c_adapter *i2c_adap; | ||
300 | struct drm_display_mode *scan; /* *modes, *bios_mode; */ | ||
301 | |||
302 | psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); | ||
303 | if (!psb_intel_output) | ||
304 | return; | ||
305 | |||
306 | psb_intel_output->mode_dev = mode_dev; | ||
307 | connector = &psb_intel_output->base; | ||
308 | encoder = &psb_intel_output->enc; | ||
309 | dev_priv->is_lvds_on = true; | ||
310 | drm_connector_init(dev, &psb_intel_output->base, | ||
311 | &psb_intel_lvds_connector_funcs, | ||
312 | DRM_MODE_CONNECTOR_LVDS); | ||
313 | |||
314 | drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, | ||
315 | DRM_MODE_ENCODER_LVDS); | ||
316 | |||
317 | drm_mode_connector_attach_encoder(&psb_intel_output->base, | ||
318 | &psb_intel_output->enc); | ||
319 | psb_intel_output->type = INTEL_OUTPUT_LVDS; | ||
320 | |||
321 | drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs); | ||
322 | drm_connector_helper_add(connector, | ||
323 | &psb_intel_lvds_connector_helper_funcs); | ||
324 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | ||
325 | connector->interlace_allowed = false; | ||
326 | connector->doublescan_allowed = false; | ||
327 | |||
328 | drm_connector_attach_property(connector, | ||
329 | dev->mode_config.scaling_mode_property, | ||
330 | DRM_MODE_SCALE_FULLSCREEN); | ||
331 | drm_connector_attach_property(connector, | ||
332 | dev_priv->backlight_property, | ||
333 | BRIGHTNESS_MAX_LEVEL); | ||
334 | |||
335 | mode_dev->panel_wants_dither = false; | ||
336 | if (dev_priv->vbt_data.size != 0x00) | ||
337 | mode_dev->panel_wants_dither = (dev_priv->gct_data. | ||
338 | Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); | ||
339 | |||
340 | /* | ||
341 | * LVDS discovery: | ||
342 | * 1) check for EDID on DDC | ||
343 | * 2) check for VBT data | ||
344 | * 3) check to see if LVDS is already on | ||
345 | * if none of the above, no panel | ||
346 | * 4) make sure lid is open | ||
347 | * if closed, act like it's not there for now | ||
348 | */ | ||
349 | |||
350 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); | ||
351 | if (i2c_adap == NULL) | ||
352 | dev_err(dev->dev, "No ddc adapter available!\n"); | ||
353 | /* | ||
354 | * Attempt to get the fixed panel mode from DDC. Assume that the | ||
355 | * preferred mode is the right one. | ||
356 | */ | ||
357 | if (i2c_adap) { | ||
358 | edid = drm_get_edid(connector, i2c_adap); | ||
359 | if (edid) { | ||
360 | drm_mode_connector_update_edid_property(connector, | ||
361 | edid); | ||
362 | ret = drm_add_edid_modes(connector, edid); | ||
363 | kfree(edid); | ||
364 | } | ||
365 | |||
366 | list_for_each_entry(scan, &connector->probed_modes, head) { | ||
367 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | ||
368 | mode_dev->panel_fixed_mode = | ||
369 | drm_mode_duplicate(dev, scan); | ||
370 | goto out; /* FIXME: check for quirks */ | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | /* | ||
375 | * If we didn't get EDID, try geting panel timing | ||
376 | * from configuration data | ||
377 | */ | ||
378 | mode_dev->panel_fixed_mode = oaktrail_lvds_get_configuration_mode(dev); | ||
379 | |||
380 | if (mode_dev->panel_fixed_mode) { | ||
381 | mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
382 | goto out; /* FIXME: check for quirks */ | ||
383 | } | ||
384 | |||
385 | /* If we still don't have a mode after all that, give up. */ | ||
386 | if (!mode_dev->panel_fixed_mode) { | ||
387 | dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); | ||
388 | goto failed_find; | ||
389 | } | ||
390 | |||
391 | out: | ||
392 | drm_sysfs_connector_add(connector); | ||
393 | return; | ||
394 | |||
395 | failed_find: | ||
396 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); | ||
397 | if (psb_intel_output->ddc_bus) | ||
398 | psb_intel_i2c_destroy(psb_intel_output->ddc_bus); | ||
399 | |||
400 | /* failed_ddc: */ | ||
401 | |||
402 | drm_encoder_cleanup(encoder); | ||
403 | drm_connector_cleanup(connector); | ||
404 | kfree(connector); | ||
405 | } | ||
406 | |||