Rev 1125 | Rev 1179 | 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 | /* |
||
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 | |||
1126 | serge | 285 | ENTRY(); |
286 | |||
1123 | serge | 287 | list_for_each_entry(mode, &connector->modes, head) { |
288 | if (drm_mode_width(mode) > width || |
||
289 | drm_mode_height(mode) > height) |
||
290 | continue; |
||
291 | if (mode->type & DRM_MODE_TYPE_PREFERRED) |
||
292 | return mode; |
||
293 | } |
||
294 | return NULL; |
||
295 | } |
||
296 | |||
297 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
||
298 | { |
||
299 | bool enable; |
||
300 | |||
301 | if (strict) { |
||
302 | enable = connector->status == connector_status_connected; |
||
303 | } else { |
||
304 | enable = connector->status != connector_status_disconnected; |
||
305 | } |
||
306 | return enable; |
||
307 | } |
||
308 | |||
309 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
||
310 | { |
||
311 | bool any_enabled = false; |
||
312 | struct drm_connector *connector; |
||
313 | int i = 0; |
||
314 | |||
315 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
316 | enabled[i] = drm_connector_enabled(connector, true); |
||
1126 | serge | 317 | DRM_DEBUG("connector %d enabled ? %s\n", connector->base.id, |
1123 | serge | 318 | enabled[i] ? "yes" : "no"); |
319 | any_enabled |= enabled[i]; |
||
320 | i++; |
||
321 | } |
||
322 | |||
323 | if (any_enabled) |
||
324 | return; |
||
325 | |||
326 | i = 0; |
||
327 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
328 | enabled[i] = drm_connector_enabled(connector, false); |
||
329 | i++; |
||
330 | } |
||
331 | } |
||
332 | |||
333 | static bool drm_target_preferred(struct drm_device *dev, |
||
334 | struct drm_display_mode **modes, |
||
335 | bool *enabled, int width, int height) |
||
336 | { |
||
337 | struct drm_connector *connector; |
||
338 | int i = 0; |
||
339 | |||
340 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
341 | |||
342 | if (enabled[i] == false) { |
||
343 | i++; |
||
344 | continue; |
||
345 | } |
||
346 | |||
347 | DRM_DEBUG("looking for preferred mode on connector %d\n", |
||
348 | connector->base.id); |
||
349 | |||
350 | modes[i] = drm_has_preferred_mode(connector, width, height); |
||
351 | /* No preferred modes, pick one off the list */ |
||
352 | if (!modes[i] && !list_empty(&connector->modes)) { |
||
353 | list_for_each_entry(modes[i], &connector->modes, head) |
||
354 | break; |
||
355 | } |
||
356 | DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name : |
||
357 | "none"); |
||
358 | i++; |
||
359 | } |
||
360 | return true; |
||
361 | } |
||
362 | |||
363 | static int drm_pick_crtcs(struct drm_device *dev, |
||
364 | struct drm_crtc **best_crtcs, |
||
365 | struct drm_display_mode **modes, |
||
366 | int n, int width, int height) |
||
367 | { |
||
368 | int c, o; |
||
369 | struct drm_connector *connector; |
||
370 | struct drm_connector_helper_funcs *connector_funcs; |
||
371 | struct drm_encoder *encoder; |
||
372 | struct drm_crtc *best_crtc; |
||
373 | int my_score, best_score, score; |
||
374 | struct drm_crtc **crtcs, *crtc; |
||
375 | |||
376 | if (n == dev->mode_config.num_connector) |
||
377 | return 0; |
||
378 | c = 0; |
||
379 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
380 | if (c == n) |
||
381 | break; |
||
382 | c++; |
||
383 | } |
||
384 | |||
1126 | serge | 385 | dbgprintf("n= %d\n", n); |
386 | |||
1123 | serge | 387 | best_crtcs[n] = NULL; |
388 | best_crtc = NULL; |
||
389 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
||
390 | if (modes[n] == NULL) |
||
391 | return best_score; |
||
392 | |||
393 | crtcs = kmalloc(dev->mode_config.num_connector * |
||
394 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
395 | if (!crtcs) |
||
396 | return best_score; |
||
397 | |||
1126 | serge | 398 | dbgprintf("crtcs = %x\n", crtcs); |
399 | |||
1123 | serge | 400 | my_score = 1; |
401 | if (connector->status == connector_status_connected) |
||
402 | my_score++; |
||
403 | if (drm_has_preferred_mode(connector, width, height)) |
||
404 | my_score++; |
||
405 | |||
406 | connector_funcs = connector->helper_private; |
||
407 | encoder = connector_funcs->best_encoder(connector); |
||
1126 | serge | 408 | |
409 | dbgprintf("encoder = %x\n", encoder); |
||
410 | |||
1123 | serge | 411 | if (!encoder) |
412 | goto out; |
||
413 | |||
414 | connector->encoder = encoder; |
||
415 | |||
416 | /* select a crtc for this connector and then attempt to configure |
||
417 | remaining connectors */ |
||
418 | c = 0; |
||
419 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
||
420 | |||
421 | if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { |
||
422 | c++; |
||
423 | continue; |
||
424 | } |
||
425 | |||
426 | for (o = 0; o < n; o++) |
||
427 | if (best_crtcs[o] == crtc) |
||
428 | break; |
||
429 | |||
430 | if (o < n) { |
||
431 | /* ignore cloning for now */ |
||
432 | c++; |
||
433 | continue; |
||
434 | } |
||
435 | |||
436 | crtcs[n] = crtc; |
||
437 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
||
438 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
||
439 | width, height); |
||
440 | if (score > best_score) { |
||
441 | best_crtc = crtc; |
||
442 | best_score = score; |
||
443 | memcpy(best_crtcs, crtcs, |
||
444 | dev->mode_config.num_connector * |
||
445 | sizeof(struct drm_crtc *)); |
||
446 | } |
||
447 | c++; |
||
448 | } |
||
449 | out: |
||
450 | kfree(crtcs); |
||
1126 | serge | 451 | |
452 | dbgprintf("best_score= %x\n", best_score); |
||
453 | |||
454 | LEAVE(); |
||
455 | |||
1123 | serge | 456 | return best_score; |
457 | } |
||
458 | |||
459 | static void drm_setup_crtcs(struct drm_device *dev) |
||
460 | { |
||
461 | struct drm_crtc **crtcs; |
||
462 | struct drm_display_mode **modes; |
||
463 | struct drm_encoder *encoder; |
||
464 | struct drm_connector *connector; |
||
465 | bool *enabled; |
||
466 | int width, height; |
||
467 | int i, ret; |
||
468 | |||
469 | DRM_DEBUG("\n"); |
||
470 | |||
1126 | serge | 471 | width = 1280; //dev->mode_config.max_width; |
472 | height = 1024; //dev->mode_config.max_height; |
||
1123 | serge | 473 | |
474 | /* clean out all the encoder/crtc combos */ |
||
475 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
476 | encoder->crtc = NULL; |
||
477 | } |
||
478 | |||
479 | crtcs = kcalloc(dev->mode_config.num_connector, |
||
480 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
481 | modes = kcalloc(dev->mode_config.num_connector, |
||
482 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
||
483 | enabled = kcalloc(dev->mode_config.num_connector, |
||
484 | sizeof(bool), GFP_KERNEL); |
||
485 | |||
486 | drm_enable_connectors(dev, enabled); |
||
487 | |||
488 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
||
489 | if (!ret) |
||
490 | DRM_ERROR("Unable to find initial modes\n"); |
||
491 | |||
492 | DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height); |
||
493 | |||
494 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
||
495 | |||
496 | i = 0; |
||
497 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
498 | struct drm_display_mode *mode = modes[i]; |
||
499 | struct drm_crtc *crtc = crtcs[i]; |
||
500 | |||
501 | if (connector->encoder == NULL) { |
||
502 | i++; |
||
503 | continue; |
||
504 | } |
||
505 | |||
506 | if (mode && crtc) { |
||
507 | DRM_DEBUG("desired mode %s set on crtc %d\n", |
||
508 | mode->name, crtc->base.id); |
||
509 | crtc->desired_mode = mode; |
||
1126 | serge | 510 | // crtc->mode = *mode; |
511 | crtc->enabled = true; |
||
1123 | serge | 512 | connector->encoder->crtc = crtc; |
513 | } else |
||
514 | connector->encoder->crtc = NULL; |
||
515 | i++; |
||
516 | } |
||
517 | |||
518 | kfree(crtcs); |
||
519 | kfree(modes); |
||
520 | kfree(enabled); |
||
1125 | serge | 521 | |
522 | LEAVE(); |
||
1123 | serge | 523 | } |
524 | |||
525 | /** |
||
526 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
||
527 | * @encoder: encoder to test |
||
528 | * @crtc: crtc to test |
||
529 | * |
||
530 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
||
531 | */ |
||
532 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
||
533 | struct drm_crtc *crtc) |
||
534 | { |
||
535 | struct drm_device *dev; |
||
536 | struct drm_crtc *tmp; |
||
537 | int crtc_mask = 1; |
||
538 | |||
1125 | serge | 539 | // WARN(!crtc, "checking null crtc?"); |
1123 | serge | 540 | |
541 | dev = crtc->dev; |
||
542 | |||
543 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
||
544 | if (tmp == crtc) |
||
545 | break; |
||
546 | crtc_mask <<= 1; |
||
547 | } |
||
548 | |||
549 | if (encoder->possible_crtcs & crtc_mask) |
||
550 | return true; |
||
551 | return false; |
||
552 | } |
||
553 | |||
554 | /* |
||
555 | * Check the CRTC we're going to map each output to vs. its current |
||
556 | * CRTC. If they don't match, we have to disable the output and the CRTC |
||
557 | * since the driver will have to re-route things. |
||
558 | */ |
||
559 | static void |
||
560 | drm_crtc_prepare_encoders(struct drm_device *dev) |
||
561 | { |
||
562 | struct drm_encoder_helper_funcs *encoder_funcs; |
||
563 | struct drm_encoder *encoder; |
||
564 | |||
565 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
566 | encoder_funcs = encoder->helper_private; |
||
567 | /* Disable unused encoders */ |
||
568 | if (encoder->crtc == NULL) |
||
569 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
||
570 | /* Disable encoders whose CRTC is about to change */ |
||
571 | if (encoder_funcs->get_crtc && |
||
572 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
||
573 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
||
574 | } |
||
575 | } |
||
576 | |||
577 | /** |
||
578 | * drm_crtc_set_mode - set a mode |
||
579 | * @crtc: CRTC to program |
||
580 | * @mode: mode to use |
||
581 | * @x: width of mode |
||
582 | * @y: height of mode |
||
583 | * |
||
584 | * LOCKING: |
||
585 | * Caller must hold mode config lock. |
||
586 | * |
||
587 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
||
588 | * to fixup or reject the mode prior to trying to set it. |
||
589 | * |
||
590 | * RETURNS: |
||
591 | * True if the mode was set successfully, or false otherwise. |
||
592 | */ |
||
593 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
||
594 | struct drm_display_mode *mode, |
||
595 | int x, int y, |
||
596 | struct drm_framebuffer *old_fb) |
||
597 | { |
||
598 | struct drm_device *dev = crtc->dev; |
||
599 | struct drm_display_mode *adjusted_mode, saved_mode; |
||
600 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
601 | struct drm_encoder_helper_funcs *encoder_funcs; |
||
602 | int saved_x, saved_y; |
||
603 | struct drm_encoder *encoder; |
||
604 | bool ret = true; |
||
605 | |||
1126 | serge | 606 | ENTRY(); |
607 | |||
1123 | serge | 608 | adjusted_mode = drm_mode_duplicate(dev, mode); |
609 | |||
610 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
||
611 | |||
612 | if (!crtc->enabled) |
||
613 | return true; |
||
614 | |||
615 | saved_mode = crtc->mode; |
||
616 | saved_x = crtc->x; |
||
617 | saved_y = crtc->y; |
||
618 | |||
619 | /* Update crtc values up front so the driver can rely on them for mode |
||
620 | * setting. |
||
621 | */ |
||
622 | crtc->mode = *mode; |
||
623 | crtc->x = x; |
||
624 | crtc->y = y; |
||
625 | |||
626 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
||
627 | * adjust it according to limitations or connector properties, and also |
||
628 | * a chance to reject the mode entirely. |
||
629 | */ |
||
630 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
631 | |||
632 | if (encoder->crtc != crtc) |
||
633 | continue; |
||
634 | encoder_funcs = encoder->helper_private; |
||
635 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
||
636 | adjusted_mode))) { |
||
637 | goto done; |
||
638 | } |
||
639 | } |
||
640 | |||
641 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
||
642 | goto done; |
||
643 | } |
||
644 | |||
645 | /* Prepare the encoders and CRTCs before setting the mode. */ |
||
646 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
647 | |||
648 | if (encoder->crtc != crtc) |
||
649 | continue; |
||
650 | encoder_funcs = encoder->helper_private; |
||
651 | /* Disable the encoders as the first thing we do. */ |
||
652 | encoder_funcs->prepare(encoder); |
||
653 | } |
||
654 | |||
655 | drm_crtc_prepare_encoders(dev); |
||
656 | |||
657 | crtc_funcs->prepare(crtc); |
||
658 | |||
659 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
||
660 | * on the DPLL. |
||
661 | */ |
||
662 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
||
663 | if (!ret) |
||
664 | goto done; |
||
665 | |||
666 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
667 | |||
668 | if (encoder->crtc != crtc) |
||
669 | continue; |
||
670 | |||
671 | DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
||
672 | mode->name, mode->base.id); |
||
673 | encoder_funcs = encoder->helper_private; |
||
674 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
||
675 | } |
||
676 | |||
677 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
||
678 | crtc_funcs->commit(crtc); |
||
679 | |||
680 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
||
681 | |||
682 | if (encoder->crtc != crtc) |
||
683 | continue; |
||
684 | |||
685 | encoder_funcs = encoder->helper_private; |
||
686 | encoder_funcs->commit(encoder); |
||
687 | |||
688 | } |
||
689 | |||
690 | /* XXX free adjustedmode */ |
||
691 | drm_mode_destroy(dev, adjusted_mode); |
||
692 | /* FIXME: add subpixel order */ |
||
693 | done: |
||
694 | if (!ret) { |
||
695 | crtc->mode = saved_mode; |
||
696 | crtc->x = saved_x; |
||
697 | crtc->y = saved_y; |
||
698 | } |
||
1126 | serge | 699 | LEAVE(); |
1123 | serge | 700 | return ret; |
701 | } |
||
702 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
||
703 | |||
704 | |||
705 | /** |
||
706 | * drm_crtc_helper_set_config - set a new config from userspace |
||
707 | * @crtc: CRTC to setup |
||
708 | * @crtc_info: user provided configuration |
||
709 | * @new_mode: new mode to set |
||
710 | * @connector_set: set of connectors for the new config |
||
711 | * @fb: new framebuffer |
||
712 | * |
||
713 | * LOCKING: |
||
714 | * Caller must hold mode config lock. |
||
715 | * |
||
716 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
||
717 | * it. |
||
718 | * |
||
719 | * RETURNS: |
||
720 | * Zero. (FIXME) |
||
721 | */ |
||
722 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
||
723 | { |
||
724 | struct drm_device *dev; |
||
725 | struct drm_crtc **save_crtcs, *new_crtc; |
||
726 | struct drm_encoder **save_encoders, *new_encoder; |
||
727 | struct drm_framebuffer *old_fb = NULL; |
||
728 | bool save_enabled; |
||
729 | bool mode_changed = false; |
||
730 | bool fb_changed = false; |
||
731 | struct drm_connector *connector; |
||
732 | int count = 0, ro, fail = 0; |
||
733 | struct drm_crtc_helper_funcs *crtc_funcs; |
||
734 | int ret = 0; |
||
735 | |||
736 | DRM_DEBUG("\n"); |
||
737 | |||
738 | if (!set) |
||
739 | return -EINVAL; |
||
740 | |||
741 | if (!set->crtc) |
||
742 | return -EINVAL; |
||
743 | |||
744 | if (!set->crtc->helper_private) |
||
745 | return -EINVAL; |
||
746 | |||
747 | crtc_funcs = set->crtc->helper_private; |
||
748 | |||
749 | DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n", |
||
750 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
||
751 | (int)set->num_connectors, set->x, set->y); |
||
752 | |||
753 | dev = set->crtc->dev; |
||
754 | |||
755 | /* save previous config */ |
||
756 | save_enabled = set->crtc->enabled; |
||
757 | |||
758 | /* |
||
759 | * We do mode_config.num_connectors here since we'll look at the |
||
760 | * CRTC and encoder associated with each connector later. |
||
761 | */ |
||
762 | save_crtcs = kzalloc(dev->mode_config.num_connector * |
||
763 | sizeof(struct drm_crtc *), GFP_KERNEL); |
||
764 | if (!save_crtcs) |
||
765 | return -ENOMEM; |
||
766 | |||
767 | save_encoders = kzalloc(dev->mode_config.num_connector * |
||
768 | sizeof(struct drm_encoders *), GFP_KERNEL); |
||
769 | if (!save_encoders) { |
||
770 | kfree(save_crtcs); |
||
771 | return -ENOMEM; |
||
772 | } |
||
773 | |||
774 | /* We should be able to check here if the fb has the same properties |
||
775 | * and then just flip_or_move it */ |
||
776 | if (set->crtc->fb != set->fb) { |
||
777 | /* If we have no fb then treat it as a full mode set */ |
||
778 | if (set->crtc->fb == NULL) { |
||
779 | DRM_DEBUG("crtc has no fb, full mode set\n"); |
||
780 | mode_changed = true; |
||
781 | } else if ((set->fb->bits_per_pixel != |
||
782 | set->crtc->fb->bits_per_pixel) || |
||
783 | set->fb->depth != set->crtc->fb->depth) |
||
784 | fb_changed = true; |
||
785 | else |
||
786 | fb_changed = true; |
||
787 | } |
||
788 | |||
789 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
||
790 | fb_changed = true; |
||
791 | |||
792 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
||
793 | DRM_DEBUG("modes are different, full mode set\n"); |
||
794 | drm_mode_debug_printmodeline(&set->crtc->mode); |
||
795 | drm_mode_debug_printmodeline(set->mode); |
||
796 | mode_changed = true; |
||
797 | } |
||
798 | |||
799 | /* a) traverse passed in connector list and get encoders for them */ |
||
800 | count = 0; |
||
801 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
802 | struct drm_connector_helper_funcs *connector_funcs = |
||
803 | connector->helper_private; |
||
804 | save_encoders[count++] = connector->encoder; |
||
805 | new_encoder = connector->encoder; |
||
806 | for (ro = 0; ro < set->num_connectors; ro++) { |
||
807 | if (set->connectors[ro] == connector) { |
||
808 | new_encoder = connector_funcs->best_encoder(connector); |
||
809 | /* if we can't get an encoder for a connector |
||
810 | we are setting now - then fail */ |
||
811 | if (new_encoder == NULL) |
||
812 | /* don't break so fail path works correct */ |
||
813 | fail = 1; |
||
814 | break; |
||
815 | } |
||
816 | } |
||
817 | |||
818 | if (new_encoder != connector->encoder) { |
||
819 | DRM_DEBUG("encoder changed, full mode switch\n"); |
||
820 | mode_changed = true; |
||
821 | connector->encoder = new_encoder; |
||
822 | } |
||
823 | } |
||
824 | |||
825 | if (fail) { |
||
826 | ret = -EINVAL; |
||
827 | goto fail_no_encoder; |
||
828 | } |
||
829 | |||
830 | count = 0; |
||
831 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
832 | if (!connector->encoder) |
||
833 | continue; |
||
834 | |||
835 | save_crtcs[count++] = connector->encoder->crtc; |
||
836 | |||
837 | if (connector->encoder->crtc == set->crtc) |
||
838 | new_crtc = NULL; |
||
839 | else |
||
840 | new_crtc = connector->encoder->crtc; |
||
841 | |||
842 | for (ro = 0; ro < set->num_connectors; ro++) { |
||
843 | if (set->connectors[ro] == connector) |
||
844 | new_crtc = set->crtc; |
||
845 | } |
||
846 | |||
847 | /* Make sure the new CRTC will work with the encoder */ |
||
848 | if (new_crtc && |
||
849 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
||
850 | ret = -EINVAL; |
||
851 | goto fail_set_mode; |
||
852 | } |
||
853 | if (new_crtc != connector->encoder->crtc) { |
||
854 | DRM_DEBUG("crtc changed, full mode switch\n"); |
||
855 | mode_changed = true; |
||
856 | connector->encoder->crtc = new_crtc; |
||
857 | } |
||
858 | DRM_DEBUG("setting connector %d crtc to %p\n", |
||
859 | connector->base.id, new_crtc); |
||
860 | } |
||
861 | |||
862 | /* mode_set_base is not a required function */ |
||
863 | if (fb_changed && !crtc_funcs->mode_set_base) |
||
864 | mode_changed = true; |
||
865 | |||
866 | if (mode_changed) { |
||
867 | old_fb = set->crtc->fb; |
||
868 | set->crtc->fb = set->fb; |
||
869 | set->crtc->enabled = (set->mode != NULL); |
||
870 | if (set->mode != NULL) { |
||
871 | DRM_DEBUG("attempting to set mode from userspace\n"); |
||
872 | drm_mode_debug_printmodeline(set->mode); |
||
873 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
||
874 | set->x, set->y, |
||
875 | old_fb)) { |
||
876 | DRM_ERROR("failed to set mode on crtc %p\n", |
||
877 | set->crtc); |
||
878 | ret = -EINVAL; |
||
879 | goto fail_set_mode; |
||
880 | } |
||
881 | /* TODO are these needed? */ |
||
882 | set->crtc->desired_x = set->x; |
||
883 | set->crtc->desired_y = set->y; |
||
884 | set->crtc->desired_mode = set->mode; |
||
885 | } |
||
886 | drm_helper_disable_unused_functions(dev); |
||
887 | } else if (fb_changed) { |
||
888 | old_fb = set->crtc->fb; |
||
889 | if (set->crtc->fb != set->fb) |
||
890 | set->crtc->fb = set->fb; |
||
891 | ret = crtc_funcs->mode_set_base(set->crtc, |
||
892 | set->x, set->y, old_fb); |
||
893 | if (ret != 0) |
||
894 | goto fail_set_mode; |
||
895 | } |
||
896 | |||
897 | kfree(save_encoders); |
||
898 | kfree(save_crtcs); |
||
899 | return 0; |
||
900 | |||
901 | fail_set_mode: |
||
902 | set->crtc->enabled = save_enabled; |
||
903 | set->crtc->fb = old_fb; |
||
904 | count = 0; |
||
905 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
906 | if (!connector->encoder) |
||
907 | continue; |
||
908 | |||
909 | connector->encoder->crtc = save_crtcs[count++]; |
||
910 | } |
||
911 | fail_no_encoder: |
||
912 | kfree(save_crtcs); |
||
913 | count = 0; |
||
914 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
||
915 | connector->encoder = save_encoders[count++]; |
||
916 | } |
||
917 | kfree(save_encoders); |
||
918 | return ret; |
||
919 | } |
||
920 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
||
921 | |||
922 | bool drm_helper_plugged_event(struct drm_device *dev) |
||
923 | { |
||
924 | DRM_DEBUG("\n"); |
||
925 | |||
926 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
||
927 | dev->mode_config.max_height); |
||
928 | |||
929 | drm_setup_crtcs(dev); |
||
930 | |||
931 | /* alert the driver fb layer */ |
||
1126 | serge | 932 | dev->mode_config.funcs->fb_changed(dev); |
1123 | serge | 933 | |
934 | /* FIXME: send hotplug event */ |
||
935 | return true; |
||
936 | } |
||
937 | /** |
||
938 | * drm_initial_config - setup a sane initial connector configuration |
||
939 | * @dev: DRM device |
||
940 | * |
||
941 | * LOCKING: |
||
942 | * Called at init time, must take mode config lock. |
||
943 | * |
||
944 | * Scan the CRTCs and connectors and try to put together an initial setup. |
||
945 | * At the moment, this is a cloned configuration across all heads with |
||
946 | * a new framebuffer object as the backing store. |
||
947 | * |
||
948 | * RETURNS: |
||
949 | * Zero if everything went ok, nonzero otherwise. |
||
950 | */ |
||
951 | bool drm_helper_initial_config(struct drm_device *dev) |
||
952 | { |
||
953 | struct drm_connector *connector; |
||
954 | int count = 0; |
||
955 | |||
1125 | serge | 956 | ENTRY(); |
957 | |||
1123 | serge | 958 | count = drm_helper_probe_connector_modes(dev, |
959 | dev->mode_config.max_width, |
||
960 | dev->mode_config.max_height); |
||
961 | |||
962 | /* |
||
963 | * None of the available connectors had any modes, so add some |
||
964 | * and try to light them up anyway |
||
965 | */ |
||
966 | if (!count) { |
||
967 | DRM_ERROR("connectors have no modes, using standard modes\n"); |
||
968 | list_for_each_entry(connector, |
||
969 | &dev->mode_config.connector_list, |
||
970 | head) |
||
971 | drm_helper_add_std_modes(dev, connector); |
||
972 | } |
||
973 | |||
974 | drm_setup_crtcs(dev); |
||
975 | |||
1126 | serge | 976 | radeonfb_create(dev->dev_private, 1280, 1024, 1280, 1024, NULL); |
1123 | serge | 977 | |
1126 | serge | 978 | // /* alert the driver fb layer */ |
979 | dev->mode_config.funcs->fb_changed(dev); |
||
980 | |||
1125 | serge | 981 | LEAVE(); |
982 | |||
1123 | serge | 983 | return 0; |
984 | } |
||
985 | EXPORT_SYMBOL(drm_helper_initial_config); |
||
986 | |||
987 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
||
988 | { |
||
989 | int dpms = DRM_MODE_DPMS_OFF; |
||
990 | struct drm_connector *connector; |
||
991 | struct drm_device *dev = encoder->dev; |
||
992 | |||
993 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
||
994 | if (connector->encoder == encoder) |
||
995 | if (connector->dpms < dpms) |
||
996 | dpms = connector->dpms; |
||
997 | return dpms; |
||
998 | } |
||
999 | |||
1000 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
||
1001 | { |
||
1002 | int dpms = DRM_MODE_DPMS_OFF; |
||
1003 | struct drm_connector *connector; |
||
1004 | struct drm_device *dev = crtc->dev; |
||
1005 | |||
1006 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
||
1007 | if (connector->encoder && connector->encoder->crtc == crtc) |
||
1008 | if (connector->dpms < dpms) |
||
1009 | dpms = connector->dpms; |
||
1010 | return dpms; |
||
1011 | } |
||
1012 | |||
1013 | /** |
||
1014 | * drm_helper_connector_dpms |
||
1015 | * @connector affected connector |
||
1016 | * @mode DPMS mode |
||
1017 | * |
||
1018 | * Calls the low-level connector DPMS function, then |
||
1019 | * calls appropriate encoder and crtc DPMS functions as well |
||
1020 | */ |
||
1021 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
||
1022 | { |
||
1023 | struct drm_encoder *encoder = connector->encoder; |
||
1024 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
||
1025 | int old_dpms; |
||
1026 | |||
1027 | if (mode == connector->dpms) |
||
1028 | return; |
||
1029 | |||
1030 | old_dpms = connector->dpms; |
||
1031 | connector->dpms = mode; |
||
1032 | |||
1033 | /* from off to on, do crtc then encoder */ |
||
1034 | if (mode < old_dpms) { |
||
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 | if (encoder) { |
||
1042 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
||
1043 | if (encoder_funcs->dpms) |
||
1044 | (*encoder_funcs->dpms) (encoder, |
||
1045 | drm_helper_choose_encoder_dpms(encoder)); |
||
1046 | } |
||
1047 | } |
||
1048 | |||
1049 | /* from on to off, do encoder then crtc */ |
||
1050 | if (mode > old_dpms) { |
||
1051 | if (encoder) { |
||
1052 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
||
1053 | if (encoder_funcs->dpms) |
||
1054 | (*encoder_funcs->dpms) (encoder, |
||
1055 | drm_helper_choose_encoder_dpms(encoder)); |
||
1056 | } |
||
1057 | if (crtc) { |
||
1058 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
||
1059 | if (crtc_funcs->dpms) |
||
1060 | (*crtc_funcs->dpms) (crtc, |
||
1061 | drm_helper_choose_crtc_dpms(crtc)); |
||
1062 | } |
||
1063 | } |
||
1064 | |||
1065 | return; |
||
1066 | } |
||
1067 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
||
1068 | |||
1069 | /** |
||
1070 | * drm_hotplug_stage_two |
||
1071 | * @dev DRM device |
||
1072 | * @connector hotpluged connector |
||
1073 | * |
||
1074 | * LOCKING. |
||
1075 | * Caller must hold mode config lock, function might grab struct lock. |
||
1076 | * |
||
1077 | * Stage two of a hotplug. |
||
1078 | * |
||
1079 | * RETURNS: |
||
1080 | * Zero on success, errno on failure. |
||
1081 | */ |
||
1082 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
||
1083 | { |
||
1084 | drm_helper_plugged_event(dev); |
||
1085 | |||
1086 | return 0; |
||
1087 | } |
||
1088 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
||
1089 | |||
1090 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
||
1091 | struct drm_mode_fb_cmd *mode_cmd) |
||
1092 | { |
||
1093 | fb->width = mode_cmd->width; |
||
1094 | fb->height = mode_cmd->height; |
||
1095 | fb->pitch = mode_cmd->pitch; |
||
1096 | fb->bits_per_pixel = mode_cmd->bpp; |
||
1097 | fb->depth = mode_cmd->depth; |
||
1098 | |||
1099 | return 0; |
||
1100 | } |
||
1101 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
||
1102 | |||
1126 | serge | 1103 | void sysSetScreen(int width, int height) |
1104 | { |
||
1105 | asm __volatile__ |
||
1106 | ( |
||
1107 | "decl %%eax \n\t" |
||
1108 | "dec %%edx \n\t" |
||
1109 | "call *__imp__SetScreen" |
||
1110 | : |
||
1111 | :"a" (width),"d"(height) |
||
1112 | :"memory","cc" |
||
1113 | ); |
||
1114 | } |
||
1115 | |||
1116 | |||
1123 | serge | 1117 | int drm_helper_resume_force_mode(struct drm_device *dev) |
1118 | { |
||
1119 | struct drm_crtc *crtc; |
||
1126 | serge | 1120 | struct drm_framebuffer *fb; |
1121 | |||
1123 | serge | 1122 | int ret; |
1123 | |||
1126 | serge | 1124 | ENTRY(); |
1125 | |||
1123 | serge | 1126 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1127 | |||
1128 | if (!crtc->enabled) |
||
1129 | continue; |
||
1126 | serge | 1130 | dbgprintf("mode %x x %x y %x fb %x\n", |
1131 | crtc->x, crtc->y, crtc->fb, crtc->mode); |
||
1123 | serge | 1132 | |
1126 | serge | 1133 | fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); |
1134 | |||
1135 | crtc->fb = fb; |
||
1136 | |||
1137 | ret = drm_crtc_helper_set_mode(crtc, crtc->desired_mode, |
||
1123 | serge | 1138 | crtc->x, crtc->y, crtc->fb); |
1139 | |||
1140 | if (ret == false) |
||
1141 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
||
1126 | serge | 1142 | |
1143 | sysSetScreen(1280,1024); |
||
1144 | |||
1123 | serge | 1145 | } |
1126 | serge | 1146 | LEAVE(); |
1123 | serge | 1147 | return 0; |
1148 | } |
||
1149 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><>> |