Commit | Line | Data |
---|---|---|
5ea75e0f PJ |
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 | * Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | |
20 | */ | |
21 | ||
22 | #include <drm/drmP.h> | |
23 | #include "gma_display.h" | |
24 | #include "psb_intel_drv.h" | |
25 | #include "psb_intel_reg.h" | |
26 | #include "psb_drv.h" | |
2eff0b33 | 27 | #include "framebuffer.h" |
5ea75e0f PJ |
28 | |
29 | /** | |
30 | * Returns whether any output on the specified pipe is of the specified type | |
31 | */ | |
32 | bool gma_pipe_has_type(struct drm_crtc *crtc, int type) | |
33 | { | |
34 | struct drm_device *dev = crtc->dev; | |
35 | struct drm_mode_config *mode_config = &dev->mode_config; | |
36 | struct drm_connector *l_entry; | |
37 | ||
38 | list_for_each_entry(l_entry, &mode_config->connector_list, head) { | |
39 | if (l_entry->encoder && l_entry->encoder->crtc == crtc) { | |
367e4408 | 40 | struct gma_encoder *gma_encoder = |
c9d49590 | 41 | gma_attached_encoder(l_entry); |
367e4408 | 42 | if (gma_encoder->type == type) |
5ea75e0f PJ |
43 | return true; |
44 | } | |
45 | } | |
46 | ||
47 | return false; | |
48 | } | |
49 | ||
2eff0b33 PJ |
50 | void gma_wait_for_vblank(struct drm_device *dev) |
51 | { | |
52 | /* Wait for 20ms, i.e. one cycle at 50hz. */ | |
53 | mdelay(20); | |
54 | } | |
55 | ||
56 | int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, | |
57 | struct drm_framebuffer *old_fb) | |
58 | { | |
59 | struct drm_device *dev = crtc->dev; | |
60 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d | 61 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
51560195 VS |
62 | struct drm_framebuffer *fb = crtc->primary->fb; |
63 | struct psb_framebuffer *psbfb = to_psb_fb(fb); | |
6306865d | 64 | int pipe = gma_crtc->pipe; |
2eff0b33 PJ |
65 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
66 | unsigned long start, offset; | |
67 | u32 dspcntr; | |
68 | int ret = 0; | |
69 | ||
70 | if (!gma_power_begin(dev, true)) | |
71 | return 0; | |
72 | ||
73 | /* no fb bound */ | |
51560195 | 74 | if (!fb) { |
2eff0b33 PJ |
75 | dev_err(dev->dev, "No FB bound\n"); |
76 | goto gma_pipe_cleaner; | |
77 | } | |
78 | ||
79 | /* We are displaying this buffer, make sure it is actually loaded | |
80 | into the GTT */ | |
81 | ret = psb_gtt_pin(psbfb->gtt); | |
82 | if (ret < 0) | |
83 | goto gma_pipe_set_base_exit; | |
84 | start = psbfb->gtt->offset; | |
272725c7 | 85 | offset = y * fb->pitches[0] + x * fb->format->cpp[0]; |
2eff0b33 | 86 | |
51560195 | 87 | REG_WRITE(map->stride, fb->pitches[0]); |
2eff0b33 PJ |
88 | |
89 | dspcntr = REG_READ(map->cntr); | |
90 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; | |
91 | ||
272725c7 | 92 | switch (fb->format->cpp[0] * 8) { |
2eff0b33 PJ |
93 | case 8: |
94 | dspcntr |= DISPPLANE_8BPP; | |
95 | break; | |
96 | case 16: | |
b00c600e | 97 | if (fb->format->depth == 15) |
2eff0b33 PJ |
98 | dspcntr |= DISPPLANE_15_16BPP; |
99 | else | |
100 | dspcntr |= DISPPLANE_16BPP; | |
101 | break; | |
102 | case 24: | |
103 | case 32: | |
104 | dspcntr |= DISPPLANE_32BPP_NO_ALPHA; | |
105 | break; | |
106 | default: | |
107 | dev_err(dev->dev, "Unknown color depth\n"); | |
108 | ret = -EINVAL; | |
109 | goto gma_pipe_set_base_exit; | |
110 | } | |
111 | REG_WRITE(map->cntr, dspcntr); | |
112 | ||
113 | dev_dbg(dev->dev, | |
114 | "Writing base %08lX %08lX %d %d\n", start, offset, x, y); | |
115 | ||
116 | /* FIXME: Investigate whether this really is the base for psb and why | |
117 | the linear offset is named base for the other chips. map->surf | |
118 | should be the base and map->linoff the offset for all chips */ | |
119 | if (IS_PSB(dev)) { | |
120 | REG_WRITE(map->base, offset + start); | |
121 | REG_READ(map->base); | |
122 | } else { | |
123 | REG_WRITE(map->base, offset); | |
124 | REG_READ(map->base); | |
125 | REG_WRITE(map->surf, start); | |
126 | REG_READ(map->surf); | |
127 | } | |
128 | ||
129 | gma_pipe_cleaner: | |
130 | /* If there was a previous display we can now unpin it */ | |
131 | if (old_fb) | |
132 | psb_gtt_unpin(to_psb_fb(old_fb)->gtt); | |
133 | ||
134 | gma_pipe_set_base_exit: | |
135 | gma_power_end(dev); | |
136 | return ret; | |
137 | } | |
138 | ||
139 | /* Loads the palette/gamma unit for the CRTC with the prepared values */ | |
140 | void gma_crtc_load_lut(struct drm_crtc *crtc) | |
141 | { | |
142 | struct drm_device *dev = crtc->dev; | |
143 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d PJ |
144 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
145 | const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; | |
2eff0b33 PJ |
146 | int palreg = map->palette; |
147 | int i; | |
148 | ||
149 | /* The clocks have to be on to load the palette. */ | |
150 | if (!crtc->enabled) | |
151 | return; | |
152 | ||
153 | if (gma_power_begin(dev, false)) { | |
154 | for (i = 0; i < 256; i++) { | |
155 | REG_WRITE(palreg + 4 * i, | |
6306865d PJ |
156 | ((gma_crtc->lut_r[i] + |
157 | gma_crtc->lut_adj[i]) << 16) | | |
158 | ((gma_crtc->lut_g[i] + | |
159 | gma_crtc->lut_adj[i]) << 8) | | |
160 | (gma_crtc->lut_b[i] + | |
161 | gma_crtc->lut_adj[i])); | |
2eff0b33 PJ |
162 | } |
163 | gma_power_end(dev); | |
164 | } else { | |
165 | for (i = 0; i < 256; i++) { | |
166 | /* FIXME: Why pipe[0] and not pipe[..._crtc->pipe]? */ | |
167 | dev_priv->regs.pipe[0].palette[i] = | |
6306865d PJ |
168 | ((gma_crtc->lut_r[i] + |
169 | gma_crtc->lut_adj[i]) << 16) | | |
170 | ((gma_crtc->lut_g[i] + | |
171 | gma_crtc->lut_adj[i]) << 8) | | |
172 | (gma_crtc->lut_b[i] + | |
173 | gma_crtc->lut_adj[i]); | |
2eff0b33 PJ |
174 | } |
175 | ||
176 | } | |
177 | } | |
178 | ||
7ea77283 ML |
179 | int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, |
180 | u32 size) | |
2eff0b33 | 181 | { |
6306865d | 182 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
2eff0b33 | 183 | int i; |
2eff0b33 | 184 | |
7ea77283 | 185 | for (i = 0; i < size; i++) { |
6306865d PJ |
186 | gma_crtc->lut_r[i] = red[i] >> 8; |
187 | gma_crtc->lut_g[i] = green[i] >> 8; | |
188 | gma_crtc->lut_b[i] = blue[i] >> 8; | |
2eff0b33 PJ |
189 | } |
190 | ||
191 | gma_crtc_load_lut(crtc); | |
7ea77283 ML |
192 | |
193 | return 0; | |
2eff0b33 PJ |
194 | } |
195 | ||
196 | /** | |
197 | * Sets the power management mode of the pipe and plane. | |
198 | * | |
199 | * This code should probably grow support for turning the cursor off and back | |
200 | * on appropriately at the same time as we're turning the pipe off/on. | |
201 | */ | |
202 | void gma_crtc_dpms(struct drm_crtc *crtc, int mode) | |
203 | { | |
204 | struct drm_device *dev = crtc->dev; | |
205 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d PJ |
206 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
207 | int pipe = gma_crtc->pipe; | |
2eff0b33 PJ |
208 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
209 | u32 temp; | |
210 | ||
211 | /* XXX: When our outputs are all unaware of DPMS modes other than off | |
212 | * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. | |
213 | */ | |
214 | ||
2eff0b33 | 215 | if (IS_CDV(dev)) |
75346fe9 | 216 | dev_priv->ops->disable_sr(dev); |
2eff0b33 PJ |
217 | |
218 | switch (mode) { | |
219 | case DRM_MODE_DPMS_ON: | |
220 | case DRM_MODE_DPMS_STANDBY: | |
221 | case DRM_MODE_DPMS_SUSPEND: | |
6306865d | 222 | if (gma_crtc->active) |
2eff0b33 PJ |
223 | break; |
224 | ||
6306865d | 225 | gma_crtc->active = true; |
2eff0b33 PJ |
226 | |
227 | /* Enable the DPLL */ | |
228 | temp = REG_READ(map->dpll); | |
229 | if ((temp & DPLL_VCO_ENABLE) == 0) { | |
230 | REG_WRITE(map->dpll, temp); | |
231 | REG_READ(map->dpll); | |
232 | /* Wait for the clocks to stabilize. */ | |
233 | udelay(150); | |
234 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); | |
235 | REG_READ(map->dpll); | |
236 | /* Wait for the clocks to stabilize. */ | |
237 | udelay(150); | |
238 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); | |
239 | REG_READ(map->dpll); | |
240 | /* Wait for the clocks to stabilize. */ | |
241 | udelay(150); | |
242 | } | |
243 | ||
244 | /* Enable the plane */ | |
245 | temp = REG_READ(map->cntr); | |
246 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { | |
247 | REG_WRITE(map->cntr, | |
248 | temp | DISPLAY_PLANE_ENABLE); | |
249 | /* Flush the plane changes */ | |
250 | REG_WRITE(map->base, REG_READ(map->base)); | |
251 | } | |
252 | ||
253 | udelay(150); | |
254 | ||
255 | /* Enable the pipe */ | |
256 | temp = REG_READ(map->conf); | |
257 | if ((temp & PIPEACONF_ENABLE) == 0) | |
258 | REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); | |
259 | ||
260 | temp = REG_READ(map->status); | |
261 | temp &= ~(0xFFFF); | |
262 | temp |= PIPE_FIFO_UNDERRUN; | |
263 | REG_WRITE(map->status, temp); | |
264 | REG_READ(map->status); | |
265 | ||
266 | gma_crtc_load_lut(crtc); | |
267 | ||
268 | /* Give the overlay scaler a chance to enable | |
269 | * if it's on this pipe */ | |
270 | /* psb_intel_crtc_dpms_video(crtc, true); TODO */ | |
271 | break; | |
272 | case DRM_MODE_DPMS_OFF: | |
6306865d | 273 | if (!gma_crtc->active) |
2eff0b33 PJ |
274 | break; |
275 | ||
6306865d | 276 | gma_crtc->active = false; |
2eff0b33 PJ |
277 | |
278 | /* Give the overlay scaler a chance to disable | |
279 | * if it's on this pipe */ | |
280 | /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ | |
281 | ||
282 | /* Disable the VGA plane that we never use */ | |
283 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | |
284 | ||
285 | /* Turn off vblank interrupts */ | |
c02726ff | 286 | drm_crtc_vblank_off(crtc); |
2eff0b33 PJ |
287 | |
288 | /* Wait for vblank for the disable to take effect */ | |
289 | gma_wait_for_vblank(dev); | |
290 | ||
291 | /* Disable plane */ | |
292 | temp = REG_READ(map->cntr); | |
293 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { | |
294 | REG_WRITE(map->cntr, | |
295 | temp & ~DISPLAY_PLANE_ENABLE); | |
296 | /* Flush the plane changes */ | |
297 | REG_WRITE(map->base, REG_READ(map->base)); | |
298 | REG_READ(map->base); | |
299 | } | |
300 | ||
301 | /* Disable pipe */ | |
302 | temp = REG_READ(map->conf); | |
303 | if ((temp & PIPEACONF_ENABLE) != 0) { | |
304 | REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); | |
305 | REG_READ(map->conf); | |
306 | } | |
307 | ||
308 | /* Wait for vblank for the disable to take effect. */ | |
309 | gma_wait_for_vblank(dev); | |
310 | ||
311 | udelay(150); | |
312 | ||
313 | /* Disable DPLL */ | |
314 | temp = REG_READ(map->dpll); | |
315 | if ((temp & DPLL_VCO_ENABLE) != 0) { | |
316 | REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); | |
317 | REG_READ(map->dpll); | |
318 | } | |
319 | ||
320 | /* Wait for the clocks to turn off. */ | |
321 | udelay(150); | |
322 | break; | |
323 | } | |
324 | ||
2eff0b33 | 325 | if (IS_CDV(dev)) |
28a8194c | 326 | dev_priv->ops->update_wm(dev, crtc); |
2eff0b33 PJ |
327 | |
328 | /* Set FIFO watermarks */ | |
329 | REG_WRITE(DSPARB, 0x3F3E); | |
330 | } | |
331 | ||
38945be6 PJ |
332 | int gma_crtc_cursor_set(struct drm_crtc *crtc, |
333 | struct drm_file *file_priv, | |
334 | uint32_t handle, | |
335 | uint32_t width, uint32_t height) | |
336 | { | |
337 | struct drm_device *dev = crtc->dev; | |
338 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d PJ |
339 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
340 | int pipe = gma_crtc->pipe; | |
38945be6 PJ |
341 | uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; |
342 | uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; | |
343 | uint32_t temp; | |
344 | size_t addr = 0; | |
345 | struct gtt_range *gt; | |
6306865d | 346 | struct gtt_range *cursor_gt = gma_crtc->cursor_gt; |
38945be6 PJ |
347 | struct drm_gem_object *obj; |
348 | void *tmp_dst, *tmp_src; | |
349 | int ret = 0, i, cursor_pages; | |
350 | ||
351 | /* If we didn't get a handle then turn the cursor off */ | |
352 | if (!handle) { | |
353 | temp = CURSOR_MODE_DISABLE; | |
38945be6 PJ |
354 | if (gma_power_begin(dev, false)) { |
355 | REG_WRITE(control, temp); | |
356 | REG_WRITE(base, 0); | |
357 | gma_power_end(dev); | |
358 | } | |
359 | ||
360 | /* Unpin the old GEM object */ | |
6306865d PJ |
361 | if (gma_crtc->cursor_obj) { |
362 | gt = container_of(gma_crtc->cursor_obj, | |
38945be6 PJ |
363 | struct gtt_range, gem); |
364 | psb_gtt_unpin(gt); | |
663ab9c4 | 365 | drm_gem_object_unreference_unlocked(gma_crtc->cursor_obj); |
6306865d | 366 | gma_crtc->cursor_obj = NULL; |
38945be6 | 367 | } |
38945be6 PJ |
368 | return 0; |
369 | } | |
370 | ||
371 | /* Currently we only support 64x64 cursors */ | |
372 | if (width != 64 || height != 64) { | |
373 | dev_dbg(dev->dev, "We currently only support 64x64 cursors\n"); | |
374 | return -EINVAL; | |
375 | } | |
376 | ||
a8ad0bd8 | 377 | obj = drm_gem_object_lookup(file_priv, handle); |
631794b4 PJ |
378 | if (!obj) { |
379 | ret = -ENOENT; | |
380 | goto unlock; | |
381 | } | |
38945be6 PJ |
382 | |
383 | if (obj->size < width * height * 4) { | |
384 | dev_dbg(dev->dev, "Buffer is too small\n"); | |
385 | ret = -ENOMEM; | |
386 | goto unref_cursor; | |
387 | } | |
388 | ||
389 | gt = container_of(obj, struct gtt_range, gem); | |
390 | ||
391 | /* Pin the memory into the GTT */ | |
392 | ret = psb_gtt_pin(gt); | |
393 | if (ret) { | |
394 | dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); | |
395 | goto unref_cursor; | |
396 | } | |
397 | ||
398 | if (dev_priv->ops->cursor_needs_phys) { | |
399 | if (cursor_gt == NULL) { | |
400 | dev_err(dev->dev, "No hardware cursor mem available"); | |
401 | ret = -ENOMEM; | |
402 | goto unref_cursor; | |
403 | } | |
404 | ||
405 | /* Prevent overflow */ | |
406 | if (gt->npage > 4) | |
407 | cursor_pages = 4; | |
408 | else | |
409 | cursor_pages = gt->npage; | |
410 | ||
411 | /* Copy the cursor to cursor mem */ | |
412 | tmp_dst = dev_priv->vram_addr + cursor_gt->offset; | |
413 | for (i = 0; i < cursor_pages; i++) { | |
414 | tmp_src = kmap(gt->pages[i]); | |
415 | memcpy(tmp_dst, tmp_src, PAGE_SIZE); | |
416 | kunmap(gt->pages[i]); | |
417 | tmp_dst += PAGE_SIZE; | |
418 | } | |
419 | ||
6306865d | 420 | addr = gma_crtc->cursor_addr; |
38945be6 PJ |
421 | } else { |
422 | addr = gt->offset; | |
6306865d | 423 | gma_crtc->cursor_addr = addr; |
38945be6 PJ |
424 | } |
425 | ||
426 | temp = 0; | |
427 | /* set the pipe for the cursor */ | |
428 | temp |= (pipe << 28); | |
429 | temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; | |
430 | ||
431 | if (gma_power_begin(dev, false)) { | |
432 | REG_WRITE(control, temp); | |
433 | REG_WRITE(base, addr); | |
434 | gma_power_end(dev); | |
435 | } | |
436 | ||
437 | /* unpin the old bo */ | |
6306865d PJ |
438 | if (gma_crtc->cursor_obj) { |
439 | gt = container_of(gma_crtc->cursor_obj, struct gtt_range, gem); | |
38945be6 | 440 | psb_gtt_unpin(gt); |
663ab9c4 | 441 | drm_gem_object_unreference_unlocked(gma_crtc->cursor_obj); |
38945be6 PJ |
442 | } |
443 | ||
6306865d | 444 | gma_crtc->cursor_obj = obj; |
631794b4 | 445 | unlock: |
38945be6 PJ |
446 | return ret; |
447 | ||
448 | unref_cursor: | |
663ab9c4 | 449 | drm_gem_object_unreference_unlocked(obj); |
38945be6 PJ |
450 | return ret; |
451 | } | |
452 | ||
453 | int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | |
454 | { | |
455 | struct drm_device *dev = crtc->dev; | |
6306865d PJ |
456 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
457 | int pipe = gma_crtc->pipe; | |
38945be6 PJ |
458 | uint32_t temp = 0; |
459 | uint32_t addr; | |
460 | ||
461 | if (x < 0) { | |
462 | temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); | |
463 | x = -x; | |
464 | } | |
465 | if (y < 0) { | |
466 | temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); | |
467 | y = -y; | |
468 | } | |
469 | ||
470 | temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); | |
471 | temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); | |
472 | ||
6306865d | 473 | addr = gma_crtc->cursor_addr; |
38945be6 PJ |
474 | |
475 | if (gma_power_begin(dev, false)) { | |
476 | REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); | |
477 | REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, addr); | |
478 | gma_power_end(dev); | |
479 | } | |
480 | return 0; | |
481 | } | |
482 | ||
2eff0b33 PJ |
483 | void gma_crtc_prepare(struct drm_crtc *crtc) |
484 | { | |
45fe734c | 485 | const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
2eff0b33 PJ |
486 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
487 | } | |
488 | ||
489 | void gma_crtc_commit(struct drm_crtc *crtc) | |
490 | { | |
45fe734c | 491 | const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
2eff0b33 PJ |
492 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); |
493 | } | |
494 | ||
495 | void gma_crtc_disable(struct drm_crtc *crtc) | |
496 | { | |
497 | struct gtt_range *gt; | |
45fe734c | 498 | const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
2eff0b33 PJ |
499 | |
500 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | |
501 | ||
f4510a27 MR |
502 | if (crtc->primary->fb) { |
503 | gt = to_psb_fb(crtc->primary->fb)->gtt; | |
2eff0b33 PJ |
504 | psb_gtt_unpin(gt); |
505 | } | |
506 | } | |
507 | ||
508 | void gma_crtc_destroy(struct drm_crtc *crtc) | |
509 | { | |
6306865d | 510 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
2eff0b33 | 511 | |
6306865d | 512 | kfree(gma_crtc->crtc_state); |
2eff0b33 | 513 | drm_crtc_cleanup(crtc); |
6306865d | 514 | kfree(gma_crtc); |
2eff0b33 PJ |
515 | } |
516 | ||
a4eff9aa DV |
517 | int gma_crtc_set_config(struct drm_mode_set *set, |
518 | struct drm_modeset_acquire_ctx *ctx) | |
924cb5ff PJ |
519 | { |
520 | struct drm_device *dev = set->crtc->dev; | |
521 | struct drm_psb_private *dev_priv = dev->dev_private; | |
522 | int ret; | |
523 | ||
524 | if (!dev_priv->rpm_enabled) | |
a4eff9aa | 525 | return drm_crtc_helper_set_config(set, ctx); |
924cb5ff PJ |
526 | |
527 | pm_runtime_forbid(&dev->pdev->dev); | |
a4eff9aa | 528 | ret = drm_crtc_helper_set_config(set, ctx); |
924cb5ff PJ |
529 | pm_runtime_allow(&dev->pdev->dev); |
530 | ||
531 | return ret; | |
532 | } | |
533 | ||
2e775700 PJ |
534 | /** |
535 | * Save HW states of given crtc | |
536 | */ | |
537 | void gma_crtc_save(struct drm_crtc *crtc) | |
538 | { | |
539 | struct drm_device *dev = crtc->dev; | |
540 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d PJ |
541 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
542 | struct psb_intel_crtc_state *crtc_state = gma_crtc->crtc_state; | |
543 | const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; | |
2e775700 PJ |
544 | uint32_t palette_reg; |
545 | int i; | |
546 | ||
547 | if (!crtc_state) { | |
548 | dev_err(dev->dev, "No CRTC state found\n"); | |
549 | return; | |
550 | } | |
551 | ||
552 | crtc_state->saveDSPCNTR = REG_READ(map->cntr); | |
553 | crtc_state->savePIPECONF = REG_READ(map->conf); | |
554 | crtc_state->savePIPESRC = REG_READ(map->src); | |
555 | crtc_state->saveFP0 = REG_READ(map->fp0); | |
556 | crtc_state->saveFP1 = REG_READ(map->fp1); | |
557 | crtc_state->saveDPLL = REG_READ(map->dpll); | |
558 | crtc_state->saveHTOTAL = REG_READ(map->htotal); | |
559 | crtc_state->saveHBLANK = REG_READ(map->hblank); | |
560 | crtc_state->saveHSYNC = REG_READ(map->hsync); | |
561 | crtc_state->saveVTOTAL = REG_READ(map->vtotal); | |
562 | crtc_state->saveVBLANK = REG_READ(map->vblank); | |
563 | crtc_state->saveVSYNC = REG_READ(map->vsync); | |
564 | crtc_state->saveDSPSTRIDE = REG_READ(map->stride); | |
565 | ||
566 | /* NOTE: DSPSIZE DSPPOS only for psb */ | |
567 | crtc_state->saveDSPSIZE = REG_READ(map->size); | |
568 | crtc_state->saveDSPPOS = REG_READ(map->pos); | |
569 | ||
570 | crtc_state->saveDSPBASE = REG_READ(map->base); | |
571 | ||
572 | palette_reg = map->palette; | |
573 | for (i = 0; i < 256; ++i) | |
574 | crtc_state->savePalette[i] = REG_READ(palette_reg + (i << 2)); | |
575 | } | |
576 | ||
577 | /** | |
578 | * Restore HW states of given crtc | |
579 | */ | |
580 | void gma_crtc_restore(struct drm_crtc *crtc) | |
581 | { | |
582 | struct drm_device *dev = crtc->dev; | |
583 | struct drm_psb_private *dev_priv = dev->dev_private; | |
6306865d PJ |
584 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
585 | struct psb_intel_crtc_state *crtc_state = gma_crtc->crtc_state; | |
586 | const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; | |
2e775700 PJ |
587 | uint32_t palette_reg; |
588 | int i; | |
589 | ||
590 | if (!crtc_state) { | |
591 | dev_err(dev->dev, "No crtc state\n"); | |
592 | return; | |
593 | } | |
594 | ||
595 | if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { | |
596 | REG_WRITE(map->dpll, | |
597 | crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); | |
598 | REG_READ(map->dpll); | |
599 | udelay(150); | |
600 | } | |
601 | ||
602 | REG_WRITE(map->fp0, crtc_state->saveFP0); | |
603 | REG_READ(map->fp0); | |
604 | ||
605 | REG_WRITE(map->fp1, crtc_state->saveFP1); | |
606 | REG_READ(map->fp1); | |
607 | ||
608 | REG_WRITE(map->dpll, crtc_state->saveDPLL); | |
609 | REG_READ(map->dpll); | |
610 | udelay(150); | |
611 | ||
612 | REG_WRITE(map->htotal, crtc_state->saveHTOTAL); | |
613 | REG_WRITE(map->hblank, crtc_state->saveHBLANK); | |
614 | REG_WRITE(map->hsync, crtc_state->saveHSYNC); | |
615 | REG_WRITE(map->vtotal, crtc_state->saveVTOTAL); | |
616 | REG_WRITE(map->vblank, crtc_state->saveVBLANK); | |
617 | REG_WRITE(map->vsync, crtc_state->saveVSYNC); | |
618 | REG_WRITE(map->stride, crtc_state->saveDSPSTRIDE); | |
619 | ||
620 | REG_WRITE(map->size, crtc_state->saveDSPSIZE); | |
621 | REG_WRITE(map->pos, crtc_state->saveDSPPOS); | |
622 | ||
623 | REG_WRITE(map->src, crtc_state->savePIPESRC); | |
624 | REG_WRITE(map->base, crtc_state->saveDSPBASE); | |
625 | REG_WRITE(map->conf, crtc_state->savePIPECONF); | |
626 | ||
627 | gma_wait_for_vblank(dev); | |
628 | ||
629 | REG_WRITE(map->cntr, crtc_state->saveDSPCNTR); | |
630 | REG_WRITE(map->base, crtc_state->saveDSPBASE); | |
631 | ||
632 | gma_wait_for_vblank(dev); | |
633 | ||
634 | palette_reg = map->palette; | |
635 | for (i = 0; i < 256; ++i) | |
636 | REG_WRITE(palette_reg + (i << 2), crtc_state->savePalette[i]); | |
637 | } | |
638 | ||
59345847 PJ |
639 | void gma_encoder_prepare(struct drm_encoder *encoder) |
640 | { | |
45fe734c | 641 | const struct drm_encoder_helper_funcs *encoder_funcs = |
59345847 PJ |
642 | encoder->helper_private; |
643 | /* lvds has its own version of prepare see psb_intel_lvds_prepare */ | |
644 | encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); | |
645 | } | |
646 | ||
647 | void gma_encoder_commit(struct drm_encoder *encoder) | |
648 | { | |
45fe734c | 649 | const struct drm_encoder_helper_funcs *encoder_funcs = |
59345847 PJ |
650 | encoder->helper_private; |
651 | /* lvds has its own version of commit see psb_intel_lvds_commit */ | |
652 | encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); | |
653 | } | |
654 | ||
655 | void gma_encoder_destroy(struct drm_encoder *encoder) | |
656 | { | |
367e4408 | 657 | struct gma_encoder *intel_encoder = to_gma_encoder(encoder); |
59345847 PJ |
658 | |
659 | drm_encoder_cleanup(encoder); | |
660 | kfree(intel_encoder); | |
661 | } | |
662 | ||
663 | /* Currently there is only a 1:1 mapping of encoders and connectors */ | |
664 | struct drm_encoder *gma_best_encoder(struct drm_connector *connector) | |
665 | { | |
367e4408 | 666 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
59345847 | 667 | |
367e4408 | 668 | return &gma_encoder->base; |
59345847 PJ |
669 | } |
670 | ||
a3d5d75f | 671 | void gma_connector_attach_encoder(struct gma_connector *connector, |
367e4408 | 672 | struct gma_encoder *encoder) |
59345847 PJ |
673 | { |
674 | connector->encoder = encoder; | |
675 | drm_mode_connector_attach_encoder(&connector->base, | |
676 | &encoder->base); | |
677 | } | |
678 | ||
5ea75e0f PJ |
679 | #define GMA_PLL_INVALID(s) { /* DRM_ERROR(s); */ return false; } |
680 | ||
681 | bool gma_pll_is_valid(struct drm_crtc *crtc, | |
682 | const struct gma_limit_t *limit, | |
683 | struct gma_clock_t *clock) | |
684 | { | |
685 | if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) | |
686 | GMA_PLL_INVALID("p1 out of range"); | |
687 | if (clock->p < limit->p.min || limit->p.max < clock->p) | |
688 | GMA_PLL_INVALID("p out of range"); | |
689 | if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) | |
690 | GMA_PLL_INVALID("m2 out of range"); | |
691 | if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) | |
692 | GMA_PLL_INVALID("m1 out of range"); | |
693 | /* On CDV m1 is always 0 */ | |
694 | if (clock->m1 <= clock->m2 && clock->m1 != 0) | |
695 | GMA_PLL_INVALID("m1 <= m2 && m1 != 0"); | |
696 | if (clock->m < limit->m.min || limit->m.max < clock->m) | |
697 | GMA_PLL_INVALID("m out of range"); | |
698 | if (clock->n < limit->n.min || limit->n.max < clock->n) | |
699 | GMA_PLL_INVALID("n out of range"); | |
700 | if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) | |
701 | GMA_PLL_INVALID("vco out of range"); | |
702 | /* XXX: We may need to be checking "Dot clock" | |
703 | * depending on the multiplier, connector, etc., | |
704 | * rather than just a single range. | |
705 | */ | |
706 | if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) | |
707 | GMA_PLL_INVALID("dot out of range"); | |
708 | ||
709 | return true; | |
710 | } | |
711 | ||
712 | bool gma_find_best_pll(const struct gma_limit_t *limit, | |
713 | struct drm_crtc *crtc, int target, int refclk, | |
714 | struct gma_clock_t *best_clock) | |
715 | { | |
716 | struct drm_device *dev = crtc->dev; | |
717 | const struct gma_clock_funcs *clock_funcs = | |
6306865d | 718 | to_gma_crtc(crtc)->clock_funcs; |
5ea75e0f PJ |
719 | struct gma_clock_t clock; |
720 | int err = target; | |
721 | ||
722 | if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && | |
723 | (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { | |
724 | /* | |
725 | * For LVDS, if the panel is on, just rely on its current | |
726 | * settings for dual-channel. We haven't figured out how to | |
727 | * reliably set up different single/dual channel state, if we | |
728 | * even can. | |
729 | */ | |
730 | if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == | |
731 | LVDS_CLKB_POWER_UP) | |
732 | clock.p2 = limit->p2.p2_fast; | |
733 | else | |
734 | clock.p2 = limit->p2.p2_slow; | |
735 | } else { | |
736 | if (target < limit->p2.dot_limit) | |
737 | clock.p2 = limit->p2.p2_slow; | |
738 | else | |
739 | clock.p2 = limit->p2.p2_fast; | |
740 | } | |
741 | ||
742 | memset(best_clock, 0, sizeof(*best_clock)); | |
743 | ||
744 | /* m1 is always 0 on CDV so the outmost loop will run just once */ | |
745 | for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { | |
746 | for (clock.m2 = limit->m2.min; | |
747 | (clock.m2 < clock.m1 || clock.m1 == 0) && | |
748 | clock.m2 <= limit->m2.max; clock.m2++) { | |
749 | for (clock.n = limit->n.min; | |
750 | clock.n <= limit->n.max; clock.n++) { | |
751 | for (clock.p1 = limit->p1.min; | |
752 | clock.p1 <= limit->p1.max; | |
753 | clock.p1++) { | |
754 | int this_err; | |
755 | ||
756 | clock_funcs->clock(refclk, &clock); | |
757 | ||
758 | if (!clock_funcs->pll_is_valid(crtc, | |
759 | limit, &clock)) | |
760 | continue; | |
761 | ||
762 | this_err = abs(clock.dot - target); | |
763 | if (this_err < err) { | |
764 | *best_clock = clock; | |
765 | err = this_err; | |
766 | } | |
767 | } | |
768 | } | |
769 | } | |
770 | } | |
771 | ||
772 | return err != target; | |
773 | } |