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