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