Commit | Line | Data |
---|---|---|
1b082ccf AC |
1 | /* |
2 | * Copyright © 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 | ||
18 | #include <linux/i2c.h> | |
19 | #include <linux/pm_runtime.h> | |
20 | ||
21 | #include <drm/drmP.h> | |
22 | #include "framebuffer.h" | |
23 | #include "psb_drv.h" | |
24 | #include "psb_intel_drv.h" | |
25 | #include "psb_intel_reg.h" | |
fe477cc1 | 26 | #include "gma_display.h" |
1b082ccf AC |
27 | #include "power.h" |
28 | ||
29 | struct psb_intel_range_t { | |
30 | int min, max; | |
31 | }; | |
32 | ||
33 | struct oaktrail_limit_t { | |
34 | struct psb_intel_range_t dot, m, p1; | |
35 | }; | |
36 | ||
37 | struct oaktrail_clock_t { | |
38 | /* derived values */ | |
39 | int dot; | |
40 | int m; | |
41 | int p1; | |
42 | }; | |
43 | ||
44 | #define MRST_LIMIT_LVDS_100L 0 | |
45 | #define MRST_LIMIT_LVDS_83 1 | |
46 | #define MRST_LIMIT_LVDS_100 2 | |
47 | ||
48 | #define MRST_DOT_MIN 19750 | |
49 | #define MRST_DOT_MAX 120000 | |
50 | #define MRST_M_MIN_100L 20 | |
51 | #define MRST_M_MIN_100 10 | |
52 | #define MRST_M_MIN_83 12 | |
53 | #define MRST_M_MAX_100L 34 | |
54 | #define MRST_M_MAX_100 17 | |
55 | #define MRST_M_MAX_83 20 | |
56 | #define MRST_P1_MIN 2 | |
57 | #define MRST_P1_MAX_0 7 | |
58 | #define MRST_P1_MAX_1 8 | |
59 | ||
60 | static const struct oaktrail_limit_t oaktrail_limits[] = { | |
61 | { /* MRST_LIMIT_LVDS_100L */ | |
62 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | |
63 | .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, | |
64 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, | |
65 | }, | |
66 | { /* MRST_LIMIT_LVDS_83L */ | |
67 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | |
68 | .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, | |
69 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, | |
70 | }, | |
71 | { /* MRST_LIMIT_LVDS_100 */ | |
72 | .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, | |
73 | .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, | |
74 | .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, | |
75 | }, | |
76 | }; | |
77 | ||
78 | #define MRST_M_MIN 10 | |
79 | static const u32 oaktrail_m_converts[] = { | |
80 | 0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C, | |
81 | 0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25, | |
82 | 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, | |
83 | }; | |
84 | ||
85 | static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc) | |
86 | { | |
87 | const struct oaktrail_limit_t *limit = NULL; | |
88 | struct drm_device *dev = crtc->dev; | |
89 | struct drm_psb_private *dev_priv = dev->dev_private; | |
90 | ||
fe477cc1 PJ |
91 | if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) |
92 | || gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { | |
1b082ccf AC |
93 | switch (dev_priv->core_freq) { |
94 | case 100: | |
95 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L]; | |
96 | break; | |
97 | case 166: | |
98 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_83]; | |
99 | break; | |
100 | case 200: | |
101 | limit = &oaktrail_limits[MRST_LIMIT_LVDS_100]; | |
102 | break; | |
103 | } | |
104 | } else { | |
105 | limit = NULL; | |
106 | dev_err(dev->dev, "oaktrail_limit Wrong display type.\n"); | |
107 | } | |
108 | ||
109 | return limit; | |
110 | } | |
111 | ||
112 | /** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ | |
113 | static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock) | |
114 | { | |
115 | clock->dot = (refclk * clock->m) / (14 * clock->p1); | |
116 | } | |
117 | ||
44332ddf | 118 | static void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock) |
1b082ccf AC |
119 | { |
120 | pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", | |
121 | prefix, clock->dot, clock->m, clock->p1); | |
122 | } | |
123 | ||
124 | /** | |
125 | * Returns a set of divisors for the desired target clock with the given refclk, | |
126 | * or FALSE. Divisor values are the actual divisors for | |
127 | */ | |
128 | static bool | |
129 | mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, | |
130 | struct oaktrail_clock_t *best_clock) | |
131 | { | |
132 | struct oaktrail_clock_t clock; | |
133 | const struct oaktrail_limit_t *limit = oaktrail_limit(crtc); | |
134 | int err = target; | |
135 | ||
136 | memset(best_clock, 0, sizeof(*best_clock)); | |
137 | ||
138 | for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { | |
139 | for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; | |
140 | clock.p1++) { | |
141 | int this_err; | |
142 | ||
143 | oaktrail_clock(refclk, &clock); | |
144 | ||
145 | this_err = abs(clock.dot - target); | |
146 | if (this_err < err) { | |
147 | *best_clock = clock; | |
148 | err = this_err; | |
149 | } | |
150 | } | |
151 | } | |
152 | dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err); | |
153 | return err != target; | |
154 | } | |
155 | ||
156 | /** | |
157 | * Sets the power management mode of the pipe and plane. | |
158 | * | |
159 | * This code should probably grow support for turning the cursor off and back | |
160 | * on appropriately at the same time as we're turning the pipe off/on. | |
161 | */ | |
162 | static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) | |
163 | { | |
164 | struct drm_device *dev = crtc->dev; | |
213a8434 | 165 | struct drm_psb_private *dev_priv = dev->dev_private; |
1b082ccf AC |
166 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); |
167 | int pipe = psb_intel_crtc->pipe; | |
213a8434 | 168 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
1b082ccf | 169 | u32 temp; |
1b082ccf | 170 | |
39ec748f AC |
171 | if (pipe == 1) { |
172 | oaktrail_crtc_hdmi_dpms(crtc, mode); | |
173 | return; | |
174 | } | |
175 | ||
1b082ccf AC |
176 | if (!gma_power_begin(dev, true)) |
177 | return; | |
178 | ||
179 | /* XXX: When our outputs are all unaware of DPMS modes other than off | |
180 | * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. | |
181 | */ | |
182 | switch (mode) { | |
183 | case DRM_MODE_DPMS_ON: | |
184 | case DRM_MODE_DPMS_STANDBY: | |
185 | case DRM_MODE_DPMS_SUSPEND: | |
186 | /* Enable the DPLL */ | |
213a8434 | 187 | temp = REG_READ(map->dpll); |
1b082ccf | 188 | if ((temp & DPLL_VCO_ENABLE) == 0) { |
213a8434 AC |
189 | REG_WRITE(map->dpll, temp); |
190 | REG_READ(map->dpll); | |
1b082ccf AC |
191 | /* Wait for the clocks to stabilize. */ |
192 | udelay(150); | |
213a8434 AC |
193 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); |
194 | REG_READ(map->dpll); | |
1b082ccf AC |
195 | /* Wait for the clocks to stabilize. */ |
196 | udelay(150); | |
213a8434 AC |
197 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); |
198 | REG_READ(map->dpll); | |
1b082ccf AC |
199 | /* Wait for the clocks to stabilize. */ |
200 | udelay(150); | |
201 | } | |
202 | /* Enable the pipe */ | |
213a8434 | 203 | temp = REG_READ(map->conf); |
1b082ccf | 204 | if ((temp & PIPEACONF_ENABLE) == 0) |
213a8434 | 205 | REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); |
1b082ccf | 206 | /* Enable the plane */ |
213a8434 | 207 | temp = REG_READ(map->cntr); |
1b082ccf | 208 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { |
213a8434 | 209 | REG_WRITE(map->cntr, |
1b082ccf AC |
210 | temp | DISPLAY_PLANE_ENABLE); |
211 | /* Flush the plane changes */ | |
213a8434 | 212 | REG_WRITE(map->base, REG_READ(map->base)); |
1b082ccf AC |
213 | } |
214 | ||
6443ea1a | 215 | gma_crtc_load_lut(crtc); |
1b082ccf AC |
216 | |
217 | /* Give the overlay scaler a chance to enable | |
218 | if it's on this pipe */ | |
219 | /* psb_intel_crtc_dpms_video(crtc, true); TODO */ | |
220 | break; | |
221 | case DRM_MODE_DPMS_OFF: | |
222 | /* Give the overlay scaler a chance to disable | |
223 | * if it's on this pipe */ | |
224 | /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ | |
225 | ||
226 | /* Disable the VGA plane that we never use */ | |
227 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | |
228 | /* Disable display plane */ | |
213a8434 | 229 | temp = REG_READ(map->cntr); |
1b082ccf | 230 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { |
213a8434 | 231 | REG_WRITE(map->cntr, |
1b082ccf AC |
232 | temp & ~DISPLAY_PLANE_ENABLE); |
233 | /* Flush the plane changes */ | |
213a8434 AC |
234 | REG_WRITE(map->base, REG_READ(map->base)); |
235 | REG_READ(map->base); | |
1b082ccf AC |
236 | } |
237 | ||
238 | /* Next, disable display pipes */ | |
213a8434 | 239 | temp = REG_READ(map->conf); |
1b082ccf | 240 | if ((temp & PIPEACONF_ENABLE) != 0) { |
213a8434 AC |
241 | REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); |
242 | REG_READ(map->conf); | |
1b082ccf AC |
243 | } |
244 | /* Wait for for the pipe disable to take effect. */ | |
d1fa08f3 | 245 | gma_wait_for_vblank(dev); |
1b082ccf | 246 | |
213a8434 | 247 | temp = REG_READ(map->dpll); |
1b082ccf | 248 | if ((temp & DPLL_VCO_ENABLE) != 0) { |
213a8434 AC |
249 | REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); |
250 | REG_READ(map->dpll); | |
1b082ccf AC |
251 | } |
252 | ||
253 | /* Wait for the clocks to turn off. */ | |
254 | udelay(150); | |
255 | break; | |
256 | } | |
257 | ||
1b082ccf AC |
258 | /*Set FIFO Watermarks*/ |
259 | REG_WRITE(DSPARB, 0x3FFF); | |
260 | REG_WRITE(DSPFW1, 0x3F88080A); | |
261 | REG_WRITE(DSPFW2, 0x0b060808); | |
262 | REG_WRITE(DSPFW3, 0x0); | |
263 | REG_WRITE(DSPFW4, 0x08030404); | |
264 | REG_WRITE(DSPFW5, 0x04040404); | |
265 | REG_WRITE(DSPFW6, 0x78); | |
266 | REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000); | |
267 | /* Must write Bit 14 of the Chicken Bit Register */ | |
268 | ||
269 | gma_power_end(dev); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Return the pipe currently connected to the panel fitter, | |
274 | * or -1 if the panel fitter is not present or not in use | |
275 | */ | |
276 | static int oaktrail_panel_fitter_pipe(struct drm_device *dev) | |
277 | { | |
278 | u32 pfit_control; | |
279 | ||
280 | pfit_control = REG_READ(PFIT_CONTROL); | |
281 | ||
282 | /* See if the panel fitter is in use */ | |
283 | if ((pfit_control & PFIT_ENABLE) == 0) | |
284 | return -1; | |
285 | return (pfit_control >> 29) & 3; | |
286 | } | |
287 | ||
288 | static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, | |
289 | struct drm_display_mode *mode, | |
290 | struct drm_display_mode *adjusted_mode, | |
291 | int x, int y, | |
292 | struct drm_framebuffer *old_fb) | |
293 | { | |
294 | struct drm_device *dev = crtc->dev; | |
295 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | |
296 | struct drm_psb_private *dev_priv = dev->dev_private; | |
297 | int pipe = psb_intel_crtc->pipe; | |
213a8434 | 298 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
1b082ccf AC |
299 | int refclk = 0; |
300 | struct oaktrail_clock_t clock; | |
301 | u32 dpll = 0, fp = 0, dspcntr, pipeconf; | |
302 | bool ok, is_sdvo = false; | |
4398e58c | 303 | bool is_lvds = false; |
1b082ccf AC |
304 | bool is_mipi = false; |
305 | struct drm_mode_config *mode_config = &dev->mode_config; | |
9bd81acd | 306 | struct psb_intel_encoder *psb_intel_encoder = NULL; |
1b082ccf | 307 | uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; |
9bd81acd | 308 | struct drm_connector *connector; |
1b082ccf | 309 | |
39ec748f AC |
310 | if (pipe == 1) |
311 | return oaktrail_crtc_hdmi_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); | |
312 | ||
1b082ccf AC |
313 | if (!gma_power_begin(dev, true)) |
314 | return 0; | |
315 | ||
316 | memcpy(&psb_intel_crtc->saved_mode, | |
317 | mode, | |
318 | sizeof(struct drm_display_mode)); | |
319 | memcpy(&psb_intel_crtc->saved_adjusted_mode, | |
320 | adjusted_mode, | |
321 | sizeof(struct drm_display_mode)); | |
322 | ||
9bd81acd PJ |
323 | list_for_each_entry(connector, &mode_config->connector_list, head) { |
324 | if (!connector->encoder || connector->encoder->crtc != crtc) | |
1b082ccf AC |
325 | continue; |
326 | ||
9bd81acd PJ |
327 | psb_intel_encoder = psb_intel_attached_encoder(connector); |
328 | ||
329 | switch (psb_intel_encoder->type) { | |
1b082ccf AC |
330 | case INTEL_OUTPUT_LVDS: |
331 | is_lvds = true; | |
332 | break; | |
333 | case INTEL_OUTPUT_SDVO: | |
334 | is_sdvo = true; | |
335 | break; | |
1b082ccf AC |
336 | case INTEL_OUTPUT_MIPI: |
337 | is_mipi = true; | |
338 | break; | |
339 | } | |
340 | } | |
341 | ||
342 | /* Disable the VGA plane that we never use */ | |
343 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | |
344 | ||
345 | /* Disable the panel fitter if it was on our pipe */ | |
346 | if (oaktrail_panel_fitter_pipe(dev) == pipe) | |
347 | REG_WRITE(PFIT_CONTROL, 0); | |
348 | ||
213a8434 | 349 | REG_WRITE(map->src, |
1b082ccf AC |
350 | ((mode->crtc_hdisplay - 1) << 16) | |
351 | (mode->crtc_vdisplay - 1)); | |
352 | ||
9bd81acd | 353 | if (psb_intel_encoder) |
a69ac9ea | 354 | drm_object_property_get_value(&connector->base, |
1b082ccf AC |
355 | dev->mode_config.scaling_mode_property, &scalingType); |
356 | ||
357 | if (scalingType == DRM_MODE_SCALE_NO_SCALE) { | |
358 | /* Moorestown doesn't have register support for centering so | |
359 | * we need to mess with the h/vblank and h/vsync start and | |
360 | * ends to get centering */ | |
361 | int offsetX = 0, offsetY = 0; | |
362 | ||
363 | offsetX = (adjusted_mode->crtc_hdisplay - | |
364 | mode->crtc_hdisplay) / 2; | |
365 | offsetY = (adjusted_mode->crtc_vdisplay - | |
366 | mode->crtc_vdisplay) / 2; | |
367 | ||
213a8434 | 368 | REG_WRITE(map->htotal, (mode->crtc_hdisplay - 1) | |
1b082ccf | 369 | ((adjusted_mode->crtc_htotal - 1) << 16)); |
213a8434 | 370 | REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) | |
1b082ccf | 371 | ((adjusted_mode->crtc_vtotal - 1) << 16)); |
213a8434 | 372 | REG_WRITE(map->hblank, |
1b082ccf AC |
373 | (adjusted_mode->crtc_hblank_start - offsetX - 1) | |
374 | ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); | |
213a8434 | 375 | REG_WRITE(map->hsync, |
1b082ccf AC |
376 | (adjusted_mode->crtc_hsync_start - offsetX - 1) | |
377 | ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); | |
213a8434 | 378 | REG_WRITE(map->vblank, |
1b082ccf AC |
379 | (adjusted_mode->crtc_vblank_start - offsetY - 1) | |
380 | ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); | |
213a8434 | 381 | REG_WRITE(map->vsync, |
1b082ccf AC |
382 | (adjusted_mode->crtc_vsync_start - offsetY - 1) | |
383 | ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); | |
384 | } else { | |
213a8434 | 385 | REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | |
1b082ccf | 386 | ((adjusted_mode->crtc_htotal - 1) << 16)); |
213a8434 | 387 | REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | |
1b082ccf | 388 | ((adjusted_mode->crtc_vtotal - 1) << 16)); |
213a8434 | 389 | REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | |
1b082ccf | 390 | ((adjusted_mode->crtc_hblank_end - 1) << 16)); |
213a8434 | 391 | REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | |
1b082ccf | 392 | ((adjusted_mode->crtc_hsync_end - 1) << 16)); |
213a8434 | 393 | REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | |
1b082ccf | 394 | ((adjusted_mode->crtc_vblank_end - 1) << 16)); |
213a8434 | 395 | REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | |
1b082ccf AC |
396 | ((adjusted_mode->crtc_vsync_end - 1) << 16)); |
397 | } | |
398 | ||
399 | /* Flush the plane changes */ | |
400 | { | |
401 | struct drm_crtc_helper_funcs *crtc_funcs = | |
402 | crtc->helper_private; | |
403 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); | |
404 | } | |
405 | ||
406 | /* setup pipeconf */ | |
213a8434 | 407 | pipeconf = REG_READ(map->conf); |
1b082ccf AC |
408 | |
409 | /* Set up the display plane register */ | |
213a8434 | 410 | dspcntr = REG_READ(map->cntr); |
1b082ccf AC |
411 | dspcntr |= DISPPLANE_GAMMA_ENABLE; |
412 | ||
413 | if (pipe == 0) | |
414 | dspcntr |= DISPPLANE_SEL_PIPE_A; | |
415 | else | |
416 | dspcntr |= DISPPLANE_SEL_PIPE_B; | |
417 | ||
1b082ccf AC |
418 | if (is_mipi) |
419 | goto oaktrail_crtc_mode_set_exit; | |
420 | ||
421 | refclk = dev_priv->core_freq * 1000; | |
422 | ||
423 | dpll = 0; /*BIT16 = 0 for 100MHz reference */ | |
424 | ||
425 | ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock); | |
426 | ||
427 | if (!ok) { | |
428 | dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n"); | |
429 | } else { | |
430 | dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d," | |
431 | "m = %x, p1 = %x.\n", clock.dot, clock.m, | |
432 | clock.p1); | |
433 | } | |
434 | ||
435 | fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8; | |
436 | ||
437 | dpll |= DPLL_VGA_MODE_DIS; | |
438 | ||
439 | ||
440 | dpll |= DPLL_VCO_ENABLE; | |
441 | ||
442 | if (is_lvds) | |
443 | dpll |= DPLLA_MODE_LVDS; | |
444 | else | |
445 | dpll |= DPLLB_MODE_DAC_SERIAL; | |
446 | ||
447 | if (is_sdvo) { | |
448 | int sdvo_pixel_multiply = | |
449 | adjusted_mode->clock / mode->clock; | |
450 | ||
451 | dpll |= DPLL_DVO_HIGH_SPEED; | |
452 | dpll |= | |
453 | (sdvo_pixel_multiply - | |
454 | 1) << SDVO_MULTIPLIER_SHIFT_HIRES; | |
455 | } | |
456 | ||
457 | ||
458 | /* compute bitmask from p1 value */ | |
459 | dpll |= (1 << (clock.p1 - 2)) << 17; | |
460 | ||
461 | dpll |= DPLL_VCO_ENABLE; | |
462 | ||
463 | mrstPrintPll("chosen", &clock); | |
464 | ||
465 | if (dpll & DPLL_VCO_ENABLE) { | |
213a8434 AC |
466 | REG_WRITE(map->fp0, fp); |
467 | REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE); | |
468 | REG_READ(map->dpll); | |
1b082ccf AC |
469 | /* Check the DPLLA lock bit PIPEACONF[29] */ |
470 | udelay(150); | |
471 | } | |
472 | ||
213a8434 AC |
473 | REG_WRITE(map->fp0, fp); |
474 | REG_WRITE(map->dpll, dpll); | |
475 | REG_READ(map->dpll); | |
1b082ccf AC |
476 | /* Wait for the clocks to stabilize. */ |
477 | udelay(150); | |
478 | ||
479 | /* write it again -- the BIOS does, after all */ | |
213a8434 AC |
480 | REG_WRITE(map->dpll, dpll); |
481 | REG_READ(map->dpll); | |
1b082ccf AC |
482 | /* Wait for the clocks to stabilize. */ |
483 | udelay(150); | |
484 | ||
213a8434 AC |
485 | REG_WRITE(map->conf, pipeconf); |
486 | REG_READ(map->conf); | |
d1fa08f3 | 487 | gma_wait_for_vblank(dev); |
1b082ccf | 488 | |
213a8434 | 489 | REG_WRITE(map->cntr, dspcntr); |
d1fa08f3 | 490 | gma_wait_for_vblank(dev); |
1b082ccf AC |
491 | |
492 | oaktrail_crtc_mode_set_exit: | |
493 | gma_power_end(dev); | |
494 | return 0; | |
495 | } | |
496 | ||
497 | static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc, | |
e811f5ae | 498 | const struct drm_display_mode *mode, |
1b082ccf AC |
499 | struct drm_display_mode *adjusted_mode) |
500 | { | |
501 | return true; | |
502 | } | |
503 | ||
44332ddf | 504 | static int oaktrail_pipe_set_base(struct drm_crtc *crtc, |
1b082ccf AC |
505 | int x, int y, struct drm_framebuffer *old_fb) |
506 | { | |
507 | struct drm_device *dev = crtc->dev; | |
213a8434 | 508 | struct drm_psb_private *dev_priv = dev->dev_private; |
1b082ccf AC |
509 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); |
510 | struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); | |
511 | int pipe = psb_intel_crtc->pipe; | |
213a8434 | 512 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
1b082ccf | 513 | unsigned long start, offset; |
a746092b | 514 | |
1b082ccf AC |
515 | u32 dspcntr; |
516 | int ret = 0; | |
517 | ||
518 | /* no fb bound */ | |
519 | if (!crtc->fb) { | |
520 | dev_dbg(dev->dev, "No FB bound\n"); | |
521 | return 0; | |
522 | } | |
523 | ||
524 | if (!gma_power_begin(dev, true)) | |
525 | return 0; | |
526 | ||
527 | start = psbfb->gtt->offset; | |
01f2c773 | 528 | offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); |
1b082ccf | 529 | |
213a8434 | 530 | REG_WRITE(map->stride, crtc->fb->pitches[0]); |
1b082ccf | 531 | |
213a8434 | 532 | dspcntr = REG_READ(map->cntr); |
1b082ccf AC |
533 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; |
534 | ||
535 | switch (crtc->fb->bits_per_pixel) { | |
536 | case 8: | |
537 | dspcntr |= DISPPLANE_8BPP; | |
538 | break; | |
539 | case 16: | |
540 | if (crtc->fb->depth == 15) | |
541 | dspcntr |= DISPPLANE_15_16BPP; | |
542 | else | |
543 | dspcntr |= DISPPLANE_16BPP; | |
544 | break; | |
545 | case 24: | |
546 | case 32: | |
547 | dspcntr |= DISPPLANE_32BPP_NO_ALPHA; | |
548 | break; | |
549 | default: | |
550 | dev_err(dev->dev, "Unknown color depth\n"); | |
551 | ret = -EINVAL; | |
552 | goto pipe_set_base_exit; | |
553 | } | |
213a8434 | 554 | REG_WRITE(map->cntr, dspcntr); |
1b082ccf | 555 | |
213a8434 AC |
556 | REG_WRITE(map->base, offset); |
557 | REG_READ(map->base); | |
558 | REG_WRITE(map->surf, start); | |
559 | REG_READ(map->surf); | |
1b082ccf AC |
560 | |
561 | pipe_set_base_exit: | |
562 | gma_power_end(dev); | |
563 | return ret; | |
564 | } | |
565 | ||
566 | static void oaktrail_crtc_prepare(struct drm_crtc *crtc) | |
567 | { | |
568 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | |
569 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | |
570 | } | |
571 | ||
572 | static void oaktrail_crtc_commit(struct drm_crtc *crtc) | |
573 | { | |
574 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | |
575 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); | |
576 | } | |
577 | ||
578 | const struct drm_crtc_helper_funcs oaktrail_helper_funcs = { | |
579 | .dpms = oaktrail_crtc_dpms, | |
580 | .mode_fixup = oaktrail_crtc_mode_fixup, | |
581 | .mode_set = oaktrail_crtc_mode_set, | |
582 | .mode_set_base = oaktrail_pipe_set_base, | |
583 | .prepare = oaktrail_crtc_prepare, | |
584 | .commit = oaktrail_crtc_commit, | |
585 | }; | |
586 |