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