Rev 1125 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1123 | serge | 1 | /* |
2 | * Copyright (c) 2006-2008 Intel Corporation |
||
3 | * Copyright (c) 2007 Dave Airlie |
||
4 | * |
||
5 | * DRM core CRTC related functions |
||
6 | * |
||
7 | * Permission to use, copy, modify, distribute, and sell this software and its |
||
8 | * documentation for any purpose is hereby granted without fee, provided that |
||
9 | * the above copyright notice appear in all copies and that both that copyright |
||
10 | * notice and this permission notice appear in supporting documentation, and |
||
11 | * that the name of the copyright holders not be used in advertising or |
||
12 | * publicity pertaining to distribution of the software without specific, |
||
13 | * written prior permission. The copyright holders make no representations |
||
14 | * about the suitability of this software for any purpose. It is provided "as |
||
15 | * is" without express or implied warranty. |
||
16 | * |
||
17 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
||
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
||
19 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
||
20 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
||
21 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
||
22 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
||
23 | * OF THIS SOFTWARE. |
||
24 | * |
||
25 | * Authors: |
||
26 | * Keith Packard |
||
27 | * Eric Anholt |
||
28 | * Dave Airlie |
||
29 | * Jesse Barnes |
||
30 | */ |
||
31 | |||
32 | #include "drmP.h" |
||
33 | #include "drm_crtc.h" |
||
34 | #include "drm_crtc_helper.h" |
||
35 | |||
36 | /* |
||
37 | * Detailed mode info for 800x600@60Hz |
||
38 | */ |
||
39 | static struct drm_display_mode std_modes[] = { |
||
40 | { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840, |
||
41 | 968, 1056, 0, 600, 601, 605, 628, 0, |
||
42 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
||
43 | }; |
||
44 | |||
45 | static void drm_mode_validate_flag(struct drm_connector *connector, |
||
46 | int flags) |
||
47 | { |
||
48 | struct drm_display_mode *mode, *t; |
||
49 | |||
50 | if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) |
||
51 | return; |
||
52 | |||
53 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
||
54 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && |
||
55 | !(flags & DRM_MODE_FLAG_INTERLACE)) |
||
56 | mode->status = MODE_NO_INTERLACE; |
||
57 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && |
||
58 | !(flags & DRM_MODE_FLAG_DBLSCAN)) |
||
59 | mode->status = MODE_NO_DBLESCAN; |
||
60 | } |
||
61 | |||
62 | return; |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * drm_helper_probe_connector_modes - get complete set of display modes |
||
67 | * @dev: DRM device |
||
68 | * @maxX: max width for modes |
||
69 | * @maxY: max height for modes |
||
70 | * |
||
71 | * LOCKING: |
||
72 | * Caller must hold mode config lock. |
||
73 | * |
||
74 | * Based on @dev's mode_config layout, scan all the connectors and try to detect |
||
75 | * modes on them. Modes will first be added to the connector's probed_modes |
||
76 | * list, then culled (based on validity and the @maxX, @maxY parameters) and |
||
77 | * put into the normal modes list. |
||
78 | * |
||
79 | * Intended to be used either at bootup time or when major configuration |
||
80 | * changes have occurred. |
||
81 | * |
||
82 | * FIXME: take into account monitor limits |
||
83 | * |
||
84 | * RETURNS: |
||
85 | * Number of modes found on @connector. |
||
86 | */ |
||
87 | int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
||
88 | uint32_t maxX, uint32_t maxY) |
||
89 | { |
||
90 | struct drm_device *dev = connector->dev; |
||
91 | struct drm_display_mode *mode, *t; |
||
92 | struct drm_connector_helper_funcs *connector_funcs = |
||
93 | connector->helper_private; |
||
94 | int count = 0; |
||
95 | int mode_flags = 0; |
||
96 | |||
97 | DRM_DEBUG("%s\n", drm_get_connector_name(connector)); |
||
98 | /* set all modes to the unverified state */ |
||
99 | list_for_each_entry_safe(mode, t, &connector->modes, head) |
||
100 | mode->status = MODE_UNVERIFIED; |
||
101 | |||
102 | connector->status = connector->funcs->detect(connector); |
||
103 | |||
104 | if (connector->status == connector_status_disconnected) { |
||
105 | DRM_DEBUG("%s is disconnected\n", |
||
106 | drm_get_connector_name(connector)); |
||
107 | /* TODO set EDID to NULL */ |
||
108 | return 0; |
||
109 | } |
||
110 | |||
111 | count = (*connector_funcs->get_modes)(connector); |
||
112 | if (!count) |
||
113 | return 0; |
||
114 | |||
115 | drm_mode_connector_list_update(connector); |
||
116 | |||
117 | if (maxX && maxY) |
||
118 | drm_mode_validate_size(dev, &connector->modes, maxX, |
||
119 | maxY, 0); |
||
120 | |||
121 | if (connector->interlace_allowed) |
||
122 | mode_flags |= DRM_MODE_FLAG_INTERLACE; |
||
123 | if (connector->doublescan_allowed) |
||
124 | mode_flags |= DRM_MODE_FLAG_DBLSCAN; |
||
125 | drm_mode_validate_flag(connector, mode_flags); |
||
126 | |||
127 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
||
128 | if (mode->status == MODE_OK) |
||
129 | mode->status = connector_funcs->mode_valid(connector, |
||
130 | mode); |
||
131 | } |
||
132 | |||
133 | |||
134 | drm_mode_prune_invalid(dev, &connector->modes, true); |
||
135 | |||
136 | if (list_empty(&connector->modes)) |
||
137 | return 0; |
||
138 | |||
139 | drm_mode_sort(&connector->modes); |
||
140 | |||
141 | DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); |
||
142 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
||
143 | mode->vrefresh = drm_mode_vrefresh(mode); |
||
144 | |||
145 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
||
146 | drm_mode_debug_printmodeline(mode); |
||
147 | } |
||
148 | |||
149 | return count; |
||
150 | } |
||
151 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); |
||
152 | |||
153 | int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, |
||
154 | uint32_t maxY) |
||
155 | { |
||
156 | struct drm_connector *connector; |
||
157 | int count = 0; |
||
158 | |||
159 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
160 | count += drm_helper_probe_single_connector_modes(connector, |
||
161 | maxX, maxY); |
||
162 | } |
||
163 | |||
164 | return count; |
||
165 | } |
||
166 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); |
||
167 | |||
168 | static void drm_helper_add_std_modes(struct drm_device *dev, |
||
169 | struct drm_connector *connector) |
||
170 | { |
||
171 | struct drm_display_mode *mode, *t; |
||
172 | int i; |
||
173 | |||
174 | for (i = 0; i < ARRAY_SIZE(std_modes); i++) { |
||
175 | struct drm_display_mode *stdmode; |
||
176 | |||
177 | /* |
||
178 | * When no valid EDID modes are available we end up |
||
179 | * here and bailed in the past, now we add some standard |
||
180 | * modes and move on. |
||
181 | */ |
||
182 | stdmode = drm_mode_duplicate(dev, &std_modes[i]); |
||
183 | drm_mode_probed_add(connector, stdmode); |
||
184 | drm_mode_list_concat(&connector->probed_modes, |
||
185 | &connector->modes); |
||
186 | |||
187 | DRM_DEBUG("Adding mode %s to %s\n", stdmode->name, |
||
188 | drm_get_connector_name(connector)); |
||
189 | } |
||
190 | drm_mode_sort(&connector->modes); |
||
191 | |||
192 | DRM_DEBUG("Added std modes on %s\n", drm_get_connector_name(connector)); |
||
193 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
||
194 | mode->vrefresh = drm_mode_vrefresh(mode); |
||
195 | |||
196 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
||
197 | drm_mode_debug_printmodeline(mode); |
||
198 | } |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * drm_helper_encoder_in_use - check if a given encoder is in use |
||
203 | * @encoder: encoder to check |
||
204 | * |
||
205 | * LOCKING: |
||
206 | * Caller must hold mode config lock. |
||
207 | * |
||
208 | * Walk @encoders's DRM device's mode_config and see if it's in use. |
||
209 | * |
||
210 | * RETURNS: |
||
211 | * True if @encoder is part of the mode_config, false otherwise. |
||
212 | */ |
||
213 | bool drm_helper_encoder_in_use(struct drm_encoder *encoder) |
||
214 | { |
||
215 | struct drm_connector *connector; |
||
216 | struct drm_device *dev = encoder->dev; |
||
217 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
||
218 | if (connector->encoder == encoder) |
||
219 | return true; |
||
220 | return false; |
||
221 | } |
||
222 | EXPORT_SYMBOL(drm_helper_encoder_in_use); |
||
223 | |||
224 | /** |
||
225 | * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config |
||
226 | * @crtc: CRTC to check |
||
227 | * |
||
228 | * LOCKING: |
||
229 | * Caller must hold mode config lock. |
||
230 | * |
||
231 | * Walk @crtc's DRM device's mode_config and see if it's in use. |
||
232 | * |
||
233 | * RETURNS: |
||
234 | * True if @crtc is part of the mode_config, false otherwise. |
||
235 | */ |
||
236 | bool drm_helper_crtc_in_use(struct drm_crtc *crtc) |
||
237 | { |
||
238 | struct drm_encoder *encoder; |
||
239 | struct drm_device *dev = crtc->dev; |
||
240 | /* FIXME: Locking around list access? */ |
||
241 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
||
242 | if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) |
||
243 | return true; |
||
244 | return false; |
||
245 | } |
||
246 | EXPORT_SYMBOL(drm_helper_crtc_in_use); |
||
247 | |||
248 | /** |
||
249 | * drm_disable_unused_functions - disable unused objects |
||
250 | * @dev: DRM device |
||
251 | * |
||
252 | * LOCKING: |
||
253 | * Caller must hold mode config lock. |
||
254 | * |
||
255 | * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled |
||
256 | * by calling its dpms function, which should power it off. |
||
257 | */ |
||
258 | void drm_helper_disable_unused_functions(struct drm_device *dev) |
||
259 | { |
||
260 | struct drm_encoder *encoder; |
||
261 | struct drm_encoder_helper_funcs *encoder_funcs; |
||
262 | struct drm_crtc *crtc; |
||
263 | |||
264 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
265 | encoder_funcs = encoder->helper_private; |
||
266 | if (!drm_helper_encoder_in_use(encoder)) |
||
267 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
||
268 | } |
||
269 | |||
270 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
||
271 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
272 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
||
273 | if (!crtc->enabled) { |
||
274 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
||
275 | crtc->fb = NULL; |
||
276 | } |
||
277 | } |
||
278 | } |
||
279 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); |
||
280 | |||
281 | static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) |
||
282 | { |
||
283 | struct drm_display_mode *mode; |
||
284 | |||
285 | list_for_each_entry(mode, &connector->modes, head) { |
||
286 | if (drm_mode_width(mode) > width || |
||
287 | drm_mode_height(mode) > height) |
||
288 | continue; |
||
289 | if (mode->type & DRM_MODE_TYPE_PREFERRED) |
||
290 | return mode; |
||
291 | } |
||
292 | return NULL; |
||
293 | } |
||
294 | |||
295 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
||
296 | { |
||
297 | bool enable; |
||
298 | |||
299 | if (strict) { |
||
300 | enable = connector->status == connector_status_connected; |
||
301 | } else { |
||
302 | enable = connector->status != connector_status_disconnected; |
||
303 | } |
||
304 | return enable; |
||
305 | } |
||
306 | |||
307 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
||
308 | { |
||
309 | bool any_enabled = false; |
||
310 | struct drm_connector *connector; |
||
311 | int i = 0; |
||
312 | |||
313 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
314 | enabled[i] = drm_connector_enabled(connector, true); |
||
315 | DRM_DEBUG("connector %d enabled? %s\n", connector->base.id, |
||
316 | enabled[i] ? "yes" : "no"); |
||
317 | any_enabled |= enabled[i]; |
||
318 | i++; |
||
319 | } |
||
320 | |||
321 | if (any_enabled) |
||
322 | return; |
||
323 | |||
324 | i = 0; |
||
325 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
326 | enabled[i] = drm_connector_enabled(connector, false); |
||
327 | i++; |
||
328 | } |
||
329 | } |
||
330 | |||
331 | static bool drm_target_preferred(struct drm_device *dev, |
||
332 | struct drm_display_mode **modes, |
||
333 | bool *enabled, int width, int height) |
||
334 | { |
||
335 | struct drm_connector *connector; |
||
336 | int i = 0; |
||
337 | |||
338 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
339 | |||
340 | if (enabled[i] == false) { |
||
341 | i++; |
||
342 | continue; |
||
343 | } |
||
344 | |||
345 | DRM_DEBUG("looking for preferred mode on connector %d\n", |
||
346 | connector->base.id); |
||
347 | |||
348 | modes[i] = drm_has_preferred_mode(connector, width, height); |
||
349 | /* No preferred modes, pick one off the list */ |
||
350 | if (!modes[i] && !list_empty(&connector->modes)) { |
||
351 | list_for_each_entry(modes[i], &connector->modes, head) |
||
352 | break; |
||
353 | } |
||
354 | DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name : |
||
355 | "none"); |
||
356 | i++; |
||
357 | } |
||
358 | return true; |
||
359 | } |
||
360 | |||
361 | static int drm_pick_crtcs(struct drm_device *dev, |
||
362 | struct drm_crtc **best_crtcs, |
||
363 | struct drm_display_mode **modes, |
||
364 | int n, int width, int height) |
||
365 | { |
||
366 | int c, o; |
||
367 | struct drm_connector *connector; |
||
368 | struct drm_connector_helper_funcs *connector_funcs; |
||
369 | struct drm_encoder *encoder; |
||
370 | struct drm_crtc *best_crtc; |
||
371 | int my_score, best_score, score; |
||
372 | struct drm_crtc **crtcs, *crtc; |
||
373 | |||
374 | if (n == dev->mode_config.num_connector) |
||
375 | return 0; |
||
376 | c = 0; |
||
377 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
378 | if (c == n) |
||
379 | break; |
||
380 | c++; |
||
381 | } |
||
382 | |||
383 | best_crtcs[n] = NULL; |
||
384 | best_crtc = NULL; |
||
385 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
||
386 | if (modes[n] == NULL) |
||
387 | return best_score; |
||
388 | |||
389 | crtcs = kmalloc(dev->mode_config.num_connector * |
||
390 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
391 | if (!crtcs) |
||
392 | return best_score; |
||
393 | |||
394 | my_score = 1; |
||
395 | if (connector->status == connector_status_connected) |
||
396 | my_score++; |
||
397 | if (drm_has_preferred_mode(connector, width, height)) |
||
398 | my_score++; |
||
399 | |||
400 | connector_funcs = connector->helper_private; |
||
401 | encoder = connector_funcs->best_encoder(connector); |
||
402 | if (!encoder) |
||
403 | goto out; |
||
404 | |||
405 | connector->encoder = encoder; |
||
406 | |||
407 | /* select a crtc for this connector and then attempt to configure |
||
408 | remaining connectors */ |
||
409 | c = 0; |
||
410 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
||
411 | |||
412 | if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { |
||
413 | c++; |
||
414 | continue; |
||
415 | } |
||
416 | |||
417 | for (o = 0; o < n; o++) |
||
418 | if (best_crtcs[o] == crtc) |
||
419 | break; |
||
420 | |||
421 | if (o < n) { |
||
422 | /* ignore cloning for now */ |
||
423 | c++; |
||
424 | continue; |
||
425 | } |
||
426 | |||
427 | crtcs[n] = crtc; |
||
428 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
||
429 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
||
430 | width, height); |
||
431 | if (score > best_score) { |
||
432 | best_crtc = crtc; |
||
433 | best_score = score; |
||
434 | memcpy(best_crtcs, crtcs, |
||
435 | dev->mode_config.num_connector * |
||
436 | sizeof(struct drm_crtc *)); |
||
437 | } |
||
438 | c++; |
||
439 | } |
||
440 | out: |
||
441 | kfree(crtcs); |
||
442 | return best_score; |
||
443 | } |
||
444 | |||
445 | static void drm_setup_crtcs(struct drm_device *dev) |
||
446 | { |
||
447 | struct drm_crtc **crtcs; |
||
448 | struct drm_display_mode **modes; |
||
449 | struct drm_encoder *encoder; |
||
450 | struct drm_connector *connector; |
||
451 | bool *enabled; |
||
452 | int width, height; |
||
453 | int i, ret; |
||
454 | |||
455 | DRM_DEBUG("\n"); |
||
456 | |||
457 | width = dev->mode_config.max_width; |
||
458 | height = dev->mode_config.max_height; |
||
459 | |||
460 | /* clean out all the encoder/crtc combos */ |
||
461 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
462 | encoder->crtc = NULL; |
||
463 | } |
||
464 | |||
465 | crtcs = kcalloc(dev->mode_config.num_connector, |
||
466 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
467 | modes = kcalloc(dev->mode_config.num_connector, |
||
468 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
||
469 | enabled = kcalloc(dev->mode_config.num_connector, |
||
470 | sizeof(bool), GFP_KERNEL); |
||
471 | |||
472 | drm_enable_connectors(dev, enabled); |
||
473 | |||
474 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
||
475 | if (!ret) |
||
476 | DRM_ERROR("Unable to find initial modes\n"); |
||
477 | |||
478 | DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height); |
||
479 | |||
480 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
||
481 | |||
482 | i = 0; |
||
483 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
484 | struct drm_display_mode *mode = modes[i]; |
||
485 | struct drm_crtc *crtc = crtcs[i]; |
||
486 | |||
487 | if (connector->encoder == NULL) { |
||
488 | i++; |
||
489 | continue; |
||
490 | } |
||
491 | |||
492 | if (mode && crtc) { |
||
493 | DRM_DEBUG("desired mode %s set on crtc %d\n", |
||
494 | mode->name, crtc->base.id); |
||
495 | crtc->desired_mode = mode; |
||
496 | connector->encoder->crtc = crtc; |
||
497 | } else |
||
498 | connector->encoder->crtc = NULL; |
||
499 | i++; |
||
500 | } |
||
501 | |||
502 | kfree(crtcs); |
||
503 | kfree(modes); |
||
504 | kfree(enabled); |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
||
509 | * @encoder: encoder to test |
||
510 | * @crtc: crtc to test |
||
511 | * |
||
512 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
||
513 | */ |
||
514 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
||
515 | struct drm_crtc *crtc) |
||
516 | { |
||
517 | struct drm_device *dev; |
||
518 | struct drm_crtc *tmp; |
||
519 | int crtc_mask = 1; |
||
520 | |||
521 | WARN(!crtc, "checking null crtc?"); |
||
522 | |||
523 | dev = crtc->dev; |
||
524 | |||
525 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
||
526 | if (tmp == crtc) |
||
527 | break; |
||
528 | crtc_mask <<= 1; |
||
529 | } |
||
530 | |||
531 | if (encoder->possible_crtcs & crtc_mask) |
||
532 | return true; |
||
533 | return false; |
||
534 | } |
||
535 | |||
536 | /* |
||
537 | * Check the CRTC we're going to map each output to vs. its current |
||
538 | * CRTC. If they don't match, we have to disable the output and the CRTC |
||
539 | * since the driver will have to re-route things. |
||
540 | */ |
||
541 | static void |
||
542 | drm_crtc_prepare_encoders(struct drm_device *dev) |
||
543 | { |
||
544 | struct drm_encoder_helper_funcs *encoder_funcs; |
||
545 | struct drm_encoder *encoder; |
||
546 | |||
547 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
548 | encoder_funcs = encoder->helper_private; |
||
549 | /* Disable unused encoders */ |
||
550 | if (encoder->crtc == NULL) |
||
551 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
||
552 | /* Disable encoders whose CRTC is about to change */ |
||
553 | if (encoder_funcs->get_crtc && |
||
554 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
||
555 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
||
556 | } |
||
557 | } |
||
558 | |||
559 | /** |
||
560 | * drm_crtc_set_mode - set a mode |
||
561 | * @crtc: CRTC to program |
||
562 | * @mode: mode to use |
||
563 | * @x: width of mode |
||
564 | * @y: height of mode |
||
565 | * |
||
566 | * LOCKING: |
||
567 | * Caller must hold mode config lock. |
||
568 | * |
||
569 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
||
570 | * to fixup or reject the mode prior to trying to set it. |
||
571 | * |
||
572 | * RETURNS: |
||
573 | * True if the mode was set successfully, or false otherwise. |
||
574 | */ |
||
575 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
||
576 | struct drm_display_mode *mode, |
||
577 | int x, int y, |
||
578 | struct drm_framebuffer *old_fb) |
||
579 | { |
||
580 | struct drm_device *dev = crtc->dev; |
||
581 | struct drm_display_mode *adjusted_mode, saved_mode; |
||
582 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
583 | struct drm_encoder_helper_funcs *encoder_funcs; |
||
584 | int saved_x, saved_y; |
||
585 | struct drm_encoder *encoder; |
||
586 | bool ret = true; |
||
587 | |||
588 | adjusted_mode = drm_mode_duplicate(dev, mode); |
||
589 | |||
590 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
||
591 | |||
592 | if (!crtc->enabled) |
||
593 | return true; |
||
594 | |||
595 | saved_mode = crtc->mode; |
||
596 | saved_x = crtc->x; |
||
597 | saved_y = crtc->y; |
||
598 | |||
599 | /* Update crtc values up front so the driver can rely on them for mode |
||
600 | * setting. |
||
601 | */ |
||
602 | crtc->mode = *mode; |
||
603 | crtc->x = x; |
||
604 | crtc->y = y; |
||
605 | |||
606 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
||
607 | * adjust it according to limitations or connector properties, and also |
||
608 | * a chance to reject the mode entirely. |
||
609 | */ |
||
610 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
611 | |||
612 | if (encoder->crtc != crtc) |
||
613 | continue; |
||
614 | encoder_funcs = encoder->helper_private; |
||
615 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
||
616 | adjusted_mode))) { |
||
617 | goto done; |
||
618 | } |
||
619 | } |
||
620 | |||
621 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
||
622 | goto done; |
||
623 | } |
||
624 | |||
625 | /* Prepare the encoders and CRTCs before setting the mode. */ |
||
626 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
627 | |||
628 | if (encoder->crtc != crtc) |
||
629 | continue; |
||
630 | encoder_funcs = encoder->helper_private; |
||
631 | /* Disable the encoders as the first thing we do. */ |
||
632 | encoder_funcs->prepare(encoder); |
||
633 | } |
||
634 | |||
635 | drm_crtc_prepare_encoders(dev); |
||
636 | |||
637 | crtc_funcs->prepare(crtc); |
||
638 | |||
639 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
||
640 | * on the DPLL. |
||
641 | */ |
||
642 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
||
643 | if (!ret) |
||
644 | goto done; |
||
645 | |||
646 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
647 | |||
648 | if (encoder->crtc != crtc) |
||
649 | continue; |
||
650 | |||
651 | DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
||
652 | mode->name, mode->base.id); |
||
653 | encoder_funcs = encoder->helper_private; |
||
654 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
||
655 | } |
||
656 | |||
657 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
||
658 | crtc_funcs->commit(crtc); |
||
659 | |||
660 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
661 | |||
662 | if (encoder->crtc != crtc) |
||
663 | continue; |
||
664 | |||
665 | encoder_funcs = encoder->helper_private; |
||
666 | encoder_funcs->commit(encoder); |
||
667 | |||
668 | } |
||
669 | |||
670 | /* XXX free adjustedmode */ |
||
671 | drm_mode_destroy(dev, adjusted_mode); |
||
672 | /* FIXME: add subpixel order */ |
||
673 | done: |
||
674 | if (!ret) { |
||
675 | crtc->mode = saved_mode; |
||
676 | crtc->x = saved_x; |
||
677 | crtc->y = saved_y; |
||
678 | } |
||
679 | |||
680 | return ret; |
||
681 | } |
||
682 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
||
683 | |||
684 | |||
685 | /** |
||
686 | * drm_crtc_helper_set_config - set a new config from userspace |
||
687 | * @crtc: CRTC to setup |
||
688 | * @crtc_info: user provided configuration |
||
689 | * @new_mode: new mode to set |
||
690 | * @connector_set: set of connectors for the new config |
||
691 | * @fb: new framebuffer |
||
692 | * |
||
693 | * LOCKING: |
||
694 | * Caller must hold mode config lock. |
||
695 | * |
||
696 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
||
697 | * it. |
||
698 | * |
||
699 | * RETURNS: |
||
700 | * Zero. (FIXME) |
||
701 | */ |
||
702 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
||
703 | { |
||
704 | struct drm_device *dev; |
||
705 | struct drm_crtc **save_crtcs, *new_crtc; |
||
706 | struct drm_encoder **save_encoders, *new_encoder; |
||
707 | struct drm_framebuffer *old_fb = NULL; |
||
708 | bool save_enabled; |
||
709 | bool mode_changed = false; |
||
710 | bool fb_changed = false; |
||
711 | struct drm_connector *connector; |
||
712 | int count = 0, ro, fail = 0; |
||
713 | struct drm_crtc_helper_funcs *crtc_funcs; |
||
714 | int ret = 0; |
||
715 | |||
716 | DRM_DEBUG("\n"); |
||
717 | |||
718 | if (!set) |
||
719 | return -EINVAL; |
||
720 | |||
721 | if (!set->crtc) |
||
722 | return -EINVAL; |
||
723 | |||
724 | if (!set->crtc->helper_private) |
||
725 | return -EINVAL; |
||
726 | |||
727 | crtc_funcs = set->crtc->helper_private; |
||
728 | |||
729 | DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n", |
||
730 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
||
731 | (int)set->num_connectors, set->x, set->y); |
||
732 | |||
733 | dev = set->crtc->dev; |
||
734 | |||
735 | /* save previous config */ |
||
736 | save_enabled = set->crtc->enabled; |
||
737 | |||
738 | /* |
||
739 | * We do mode_config.num_connectors here since we'll look at the |
||
740 | * CRTC and encoder associated with each connector later. |
||
741 | */ |
||
742 | save_crtcs = kzalloc(dev->mode_config.num_connector * |
||
743 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
744 | if (!save_crtcs) |
||
745 | return -ENOMEM; |
||
746 | |||
747 | save_encoders = kzalloc(dev->mode_config.num_connector * |
||
748 | sizeof(struct drm_encoders *), GFP_KERNEL); |
||
749 | if (!save_encoders) { |
||
750 | kfree(save_crtcs); |
||
751 | return -ENOMEM; |
||
752 | } |
||
753 | |||
754 | /* We should be able to check here if the fb has the same properties |
||
755 | * and then just flip_or_move it */ |
||
756 | if (set->crtc->fb != set->fb) { |
||
757 | /* If we have no fb then treat it as a full mode set */ |
||
758 | if (set->crtc->fb == NULL) { |
||
759 | DRM_DEBUG("crtc has no fb, full mode set\n"); |
||
760 | mode_changed = true; |
||
761 | } else if ((set->fb->bits_per_pixel != |
||
762 | set->crtc->fb->bits_per_pixel) || |
||
763 | set->fb->depth != set->crtc->fb->depth) |
||
764 | fb_changed = true; |
||
765 | else |
||
766 | fb_changed = true; |
||
767 | } |
||
768 | |||
769 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
||
770 | fb_changed = true; |
||
771 | |||
772 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
||
773 | DRM_DEBUG("modes are different, full mode set\n"); |
||
774 | drm_mode_debug_printmodeline(&set->crtc->mode); |
||
775 | drm_mode_debug_printmodeline(set->mode); |
||
776 | mode_changed = true; |
||
777 | } |
||
778 | |||
779 | /* a) traverse passed in connector list and get encoders for them */ |
||
780 | count = 0; |
||
781 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
782 | struct drm_connector_helper_funcs *connector_funcs = |
||
783 | connector->helper_private; |
||
784 | save_encoders[count++] = connector->encoder; |
||
785 | new_encoder = connector->encoder; |
||
786 | for (ro = 0; ro < set->num_connectors; ro++) { |
||
787 | if (set->connectors[ro] == connector) { |
||
788 | new_encoder = connector_funcs->best_encoder(connector); |
||
789 | /* if we can't get an encoder for a connector |
||
790 | we are setting now - then fail */ |
||
791 | if (new_encoder == NULL) |
||
792 | /* don't break so fail path works correct */ |
||
793 | fail = 1; |
||
794 | break; |
||
795 | } |
||
796 | } |
||
797 | |||
798 | if (new_encoder != connector->encoder) { |
||
799 | DRM_DEBUG("encoder changed, full mode switch\n"); |
||
800 | mode_changed = true; |
||
801 | connector->encoder = new_encoder; |
||
802 | } |
||
803 | } |
||
804 | |||
805 | if (fail) { |
||
806 | ret = -EINVAL; |
||
807 | goto fail_no_encoder; |
||
808 | } |
||
809 | |||
810 | count = 0; |
||
811 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
812 | if (!connector->encoder) |
||
813 | continue; |
||
814 | |||
815 | save_crtcs[count++] = connector->encoder->crtc; |
||
816 | |||
817 | if (connector->encoder->crtc == set->crtc) |
||
818 | new_crtc = NULL; |
||
819 | else |
||
820 | new_crtc = connector->encoder->crtc; |
||
821 | |||
822 | for (ro = 0; ro < set->num_connectors; ro++) { |
||
823 | if (set->connectors[ro] == connector) |
||
824 | new_crtc = set->crtc; |
||
825 | } |
||
826 | |||
827 | /* Make sure the new CRTC will work with the encoder */ |
||
828 | if (new_crtc && |
||
829 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
||
830 | ret = -EINVAL; |
||
831 | goto fail_set_mode; |
||
832 | } |
||
833 | if (new_crtc != connector->encoder->crtc) { |
||
834 | DRM_DEBUG("crtc changed, full mode switch\n"); |
||
835 | mode_changed = true; |
||
836 | connector->encoder->crtc = new_crtc; |
||
837 | } |
||
838 | DRM_DEBUG("setting connector %d crtc to %p\n", |
||
839 | connector->base.id, new_crtc); |
||
840 | } |
||
841 | |||
842 | /* mode_set_base is not a required function */ |
||
843 | if (fb_changed && !crtc_funcs->mode_set_base) |
||
844 | mode_changed = true; |
||
845 | |||
846 | if (mode_changed) { |
||
847 | old_fb = set->crtc->fb; |
||
848 | set->crtc->fb = set->fb; |
||
849 | set->crtc->enabled = (set->mode != NULL); |
||
850 | if (set->mode != NULL) { |
||
851 | DRM_DEBUG("attempting to set mode from userspace\n"); |
||
852 | drm_mode_debug_printmodeline(set->mode); |
||
853 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
||
854 | set->x, set->y, |
||
855 | old_fb)) { |
||
856 | DRM_ERROR("failed to set mode on crtc %p\n", |
||
857 | set->crtc); |
||
858 | ret = -EINVAL; |
||
859 | goto fail_set_mode; |
||
860 | } |
||
861 | /* TODO are these needed? */ |
||
862 | set->crtc->desired_x = set->x; |
||
863 | set->crtc->desired_y = set->y; |
||
864 | set->crtc->desired_mode = set->mode; |
||
865 | } |
||
866 | drm_helper_disable_unused_functions(dev); |
||
867 | } else if (fb_changed) { |
||
868 | old_fb = set->crtc->fb; |
||
869 | if (set->crtc->fb != set->fb) |
||
870 | set->crtc->fb = set->fb; |
||
871 | ret = crtc_funcs->mode_set_base(set->crtc, |
||
872 | set->x, set->y, old_fb); |
||
873 | if (ret != 0) |
||
874 | goto fail_set_mode; |
||
875 | } |
||
876 | |||
877 | kfree(save_encoders); |
||
878 | kfree(save_crtcs); |
||
879 | return 0; |
||
880 | |||
881 | fail_set_mode: |
||
882 | set->crtc->enabled = save_enabled; |
||
883 | set->crtc->fb = old_fb; |
||
884 | count = 0; |
||
885 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
886 | if (!connector->encoder) |
||
887 | continue; |
||
888 | |||
889 | connector->encoder->crtc = save_crtcs[count++]; |
||
890 | } |
||
891 | fail_no_encoder: |
||
892 | kfree(save_crtcs); |
||
893 | count = 0; |
||
894 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
895 | connector->encoder = save_encoders[count++]; |
||
896 | } |
||
897 | kfree(save_encoders); |
||
898 | return ret; |
||
899 | } |
||
900 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
||
901 | |||
902 | bool drm_helper_plugged_event(struct drm_device *dev) |
||
903 | { |
||
904 | DRM_DEBUG("\n"); |
||
905 | |||
906 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
||
907 | dev->mode_config.max_height); |
||
908 | |||
909 | drm_setup_crtcs(dev); |
||
910 | |||
911 | /* alert the driver fb layer */ |
||
912 | dev->mode_config.funcs->fb_changed(dev); |
||
913 | |||
914 | /* FIXME: send hotplug event */ |
||
915 | return true; |
||
916 | } |
||
917 | /** |
||
918 | * drm_initial_config - setup a sane initial connector configuration |
||
919 | * @dev: DRM device |
||
920 | * |
||
921 | * LOCKING: |
||
922 | * Called at init time, must take mode config lock. |
||
923 | * |
||
924 | * Scan the CRTCs and connectors and try to put together an initial setup. |
||
925 | * At the moment, this is a cloned configuration across all heads with |
||
926 | * a new framebuffer object as the backing store. |
||
927 | * |
||
928 | * RETURNS: |
||
929 | * Zero if everything went ok, nonzero otherwise. |
||
930 | */ |
||
931 | bool drm_helper_initial_config(struct drm_device *dev) |
||
932 | { |
||
933 | struct drm_connector *connector; |
||
934 | int count = 0; |
||
935 | |||
936 | count = drm_helper_probe_connector_modes(dev, |
||
937 | dev->mode_config.max_width, |
||
938 | dev->mode_config.max_height); |
||
939 | |||
940 | /* |
||
941 | * None of the available connectors had any modes, so add some |
||
942 | * and try to light them up anyway |
||
943 | */ |
||
944 | if (!count) { |
||
945 | DRM_ERROR("connectors have no modes, using standard modes\n"); |
||
946 | list_for_each_entry(connector, |
||
947 | &dev->mode_config.connector_list, |
||
948 | head) |
||
949 | drm_helper_add_std_modes(dev, connector); |
||
950 | } |
||
951 | |||
952 | drm_setup_crtcs(dev); |
||
953 | |||
954 | /* alert the driver fb layer */ |
||
955 | dev->mode_config.funcs->fb_changed(dev); |
||
956 | |||
957 | return 0; |
||
958 | } |
||
959 | EXPORT_SYMBOL(drm_helper_initial_config); |
||
960 | |||
961 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
||
962 | { |
||
963 | int dpms = DRM_MODE_DPMS_OFF; |
||
964 | struct drm_connector *connector; |
||
965 | struct drm_device *dev = encoder->dev; |
||
966 | |||
967 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
||
968 | if (connector->encoder == encoder) |
||
969 | if (connector->dpms < dpms) |
||
970 | dpms = connector->dpms; |
||
971 | return dpms; |
||
972 | } |
||
973 | |||
974 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
||
975 | { |
||
976 | int dpms = DRM_MODE_DPMS_OFF; |
||
977 | struct drm_connector *connector; |
||
978 | struct drm_device *dev = crtc->dev; |
||
979 | |||
980 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
||
981 | if (connector->encoder && connector->encoder->crtc == crtc) |
||
982 | if (connector->dpms < dpms) |
||
983 | dpms = connector->dpms; |
||
984 | return dpms; |
||
985 | } |
||
986 | |||
987 | /** |
||
988 | * drm_helper_connector_dpms |
||
989 | * @connector affected connector |
||
990 | * @mode DPMS mode |
||
991 | * |
||
992 | * Calls the low-level connector DPMS function, then |
||
993 | * calls appropriate encoder and crtc DPMS functions as well |
||
994 | */ |
||
995 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
||
996 | { |
||
997 | struct drm_encoder *encoder = connector->encoder; |
||
998 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
||
999 | int old_dpms; |
||
1000 | |||
1001 | if (mode == connector->dpms) |
||
1002 | return; |
||
1003 | |||
1004 | old_dpms = connector->dpms; |
||
1005 | connector->dpms = mode; |
||
1006 | |||
1007 | /* from off to on, do crtc then encoder */ |
||
1008 | if (mode < old_dpms) { |
||
1009 | if (crtc) { |
||
1010 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
1011 | if (crtc_funcs->dpms) |
||
1012 | (*crtc_funcs->dpms) (crtc, |
||
1013 | drm_helper_choose_crtc_dpms(crtc)); |
||
1014 | } |
||
1015 | if (encoder) { |
||
1016 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
||
1017 | if (encoder_funcs->dpms) |
||
1018 | (*encoder_funcs->dpms) (encoder, |
||
1019 | drm_helper_choose_encoder_dpms(encoder)); |
||
1020 | } |
||
1021 | } |
||
1022 | |||
1023 | /* from on to off, do encoder then crtc */ |
||
1024 | if (mode > old_dpms) { |
||
1025 | if (encoder) { |
||
1026 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
||
1027 | if (encoder_funcs->dpms) |
||
1028 | (*encoder_funcs->dpms) (encoder, |
||
1029 | drm_helper_choose_encoder_dpms(encoder)); |
||
1030 | } |
||
1031 | if (crtc) { |
||
1032 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
1033 | if (crtc_funcs->dpms) |
||
1034 | (*crtc_funcs->dpms) (crtc, |
||
1035 | drm_helper_choose_crtc_dpms(crtc)); |
||
1036 | } |
||
1037 | } |
||
1038 | |||
1039 | return; |
||
1040 | } |
||
1041 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
||
1042 | |||
1043 | /** |
||
1044 | * drm_hotplug_stage_two |
||
1045 | * @dev DRM device |
||
1046 | * @connector hotpluged connector |
||
1047 | * |
||
1048 | * LOCKING. |
||
1049 | * Caller must hold mode config lock, function might grab struct lock. |
||
1050 | * |
||
1051 | * Stage two of a hotplug. |
||
1052 | * |
||
1053 | * RETURNS: |
||
1054 | * Zero on success, errno on failure. |
||
1055 | */ |
||
1056 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
||
1057 | { |
||
1058 | drm_helper_plugged_event(dev); |
||
1059 | |||
1060 | return 0; |
||
1061 | } |
||
1062 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
||
1063 | |||
1064 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
||
1065 | struct drm_mode_fb_cmd *mode_cmd) |
||
1066 | { |
||
1067 | fb->width = mode_cmd->width; |
||
1068 | fb->height = mode_cmd->height; |
||
1069 | fb->pitch = mode_cmd->pitch; |
||
1070 | fb->bits_per_pixel = mode_cmd->bpp; |
||
1071 | fb->depth = mode_cmd->depth; |
||
1072 | |||
1073 | return 0; |
||
1074 | } |
||
1075 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
||
1076 | |||
1077 | int drm_helper_resume_force_mode(struct drm_device *dev) |
||
1078 | { |
||
1079 | struct drm_crtc *crtc; |
||
1080 | int ret; |
||
1081 | |||
1082 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
||
1083 | |||
1084 | if (!crtc->enabled) |
||
1085 | continue; |
||
1086 | |||
1087 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
||
1088 | crtc->x, crtc->y, crtc->fb); |
||
1089 | |||
1090 | if (ret == false) |
||
1091 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
||
1092 | } |
||
1093 | return 0; |
||
1094 | } |
||
1095 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><>> |