diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 391 |
1 files changed, 1 insertions, 390 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 51103aa469f8..9d23f54673d3 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c | |||
@@ -55,7 +55,7 @@ static void drm_mode_validate_flag(struct drm_connector *connector, | |||
55 | } | 55 | } |
56 | 56 | ||
57 | /** | 57 | /** |
58 | * drm_helper_probe_connector_modes - get complete set of display modes | 58 | * drm_helper_probe_single_connector_modes - get complete set of display modes |
59 | * @dev: DRM device | 59 | * @dev: DRM device |
60 | * @maxX: max width for modes | 60 | * @maxX: max width for modes |
61 | * @maxY: max height for modes | 61 | * @maxY: max height for modes |
@@ -154,21 +154,6 @@ prune: | |||
154 | } | 154 | } |
155 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); | 155 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); |
156 | 156 | ||
157 | int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, | ||
158 | uint32_t maxY) | ||
159 | { | ||
160 | struct drm_connector *connector; | ||
161 | int count = 0; | ||
162 | |||
163 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
164 | count += drm_helper_probe_single_connector_modes(connector, | ||
165 | maxX, maxY); | ||
166 | } | ||
167 | |||
168 | return count; | ||
169 | } | ||
170 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); | ||
171 | |||
172 | /** | 157 | /** |
173 | * drm_helper_encoder_in_use - check if a given encoder is in use | 158 | * drm_helper_encoder_in_use - check if a given encoder is in use |
174 | * @encoder: encoder to check | 159 | * @encoder: encoder to check |
@@ -263,302 +248,6 @@ void drm_helper_disable_unused_functions(struct drm_device *dev) | |||
263 | } | 248 | } |
264 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); | 249 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); |
265 | 250 | ||
266 | static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) | ||
267 | { | ||
268 | struct drm_display_mode *mode; | ||
269 | |||
270 | list_for_each_entry(mode, &connector->modes, head) { | ||
271 | if (drm_mode_width(mode) > width || | ||
272 | drm_mode_height(mode) > height) | ||
273 | continue; | ||
274 | if (mode->type & DRM_MODE_TYPE_PREFERRED) | ||
275 | return mode; | ||
276 | } | ||
277 | return NULL; | ||
278 | } | ||
279 | |||
280 | static bool drm_has_cmdline_mode(struct drm_connector *connector) | ||
281 | { | ||
282 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; | ||
283 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | ||
284 | |||
285 | if (!fb_help_conn) | ||
286 | return false; | ||
287 | |||
288 | cmdline_mode = &fb_help_conn->cmdline_mode; | ||
289 | return cmdline_mode->specified; | ||
290 | } | ||
291 | |||
292 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height) | ||
293 | { | ||
294 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; | ||
295 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | ||
296 | struct drm_display_mode *mode = NULL; | ||
297 | |||
298 | if (!fb_help_conn) | ||
299 | return mode; | ||
300 | |||
301 | cmdline_mode = &fb_help_conn->cmdline_mode; | ||
302 | if (cmdline_mode->specified == false) | ||
303 | return mode; | ||
304 | |||
305 | /* attempt to find a matching mode in the list of modes | ||
306 | * we have gotten so far, if not add a CVT mode that conforms | ||
307 | */ | ||
308 | if (cmdline_mode->rb || cmdline_mode->margins) | ||
309 | goto create_mode; | ||
310 | |||
311 | list_for_each_entry(mode, &connector->modes, head) { | ||
312 | /* check width/height */ | ||
313 | if (mode->hdisplay != cmdline_mode->xres || | ||
314 | mode->vdisplay != cmdline_mode->yres) | ||
315 | continue; | ||
316 | |||
317 | if (cmdline_mode->refresh_specified) { | ||
318 | if (mode->vrefresh != cmdline_mode->refresh) | ||
319 | continue; | ||
320 | } | ||
321 | |||
322 | if (cmdline_mode->interlace) { | ||
323 | if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) | ||
324 | continue; | ||
325 | } | ||
326 | return mode; | ||
327 | } | ||
328 | |||
329 | create_mode: | ||
330 | mode = drm_cvt_mode(connector->dev, cmdline_mode->xres, | ||
331 | cmdline_mode->yres, | ||
332 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, | ||
333 | cmdline_mode->rb, cmdline_mode->interlace, | ||
334 | cmdline_mode->margins); | ||
335 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
336 | list_add(&mode->head, &connector->modes); | ||
337 | return mode; | ||
338 | } | ||
339 | |||
340 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) | ||
341 | { | ||
342 | bool enable; | ||
343 | |||
344 | if (strict) { | ||
345 | enable = connector->status == connector_status_connected; | ||
346 | } else { | ||
347 | enable = connector->status != connector_status_disconnected; | ||
348 | } | ||
349 | return enable; | ||
350 | } | ||
351 | |||
352 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) | ||
353 | { | ||
354 | bool any_enabled = false; | ||
355 | struct drm_connector *connector; | ||
356 | int i = 0; | ||
357 | |||
358 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
359 | enabled[i] = drm_connector_enabled(connector, true); | ||
360 | DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, | ||
361 | enabled[i] ? "yes" : "no"); | ||
362 | any_enabled |= enabled[i]; | ||
363 | i++; | ||
364 | } | ||
365 | |||
366 | if (any_enabled) | ||
367 | return; | ||
368 | |||
369 | i = 0; | ||
370 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
371 | enabled[i] = drm_connector_enabled(connector, false); | ||
372 | i++; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | static bool drm_target_preferred(struct drm_device *dev, | ||
377 | struct drm_display_mode **modes, | ||
378 | bool *enabled, int width, int height) | ||
379 | { | ||
380 | struct drm_connector *connector; | ||
381 | int i = 0; | ||
382 | |||
383 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
384 | |||
385 | if (enabled[i] == false) { | ||
386 | i++; | ||
387 | continue; | ||
388 | } | ||
389 | |||
390 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", | ||
391 | connector->base.id); | ||
392 | |||
393 | /* got for command line mode first */ | ||
394 | modes[i] = drm_pick_cmdline_mode(connector, width, height); | ||
395 | if (!modes[i]) { | ||
396 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", | ||
397 | connector->base.id); | ||
398 | modes[i] = drm_has_preferred_mode(connector, width, height); | ||
399 | } | ||
400 | /* No preferred modes, pick one off the list */ | ||
401 | if (!modes[i] && !list_empty(&connector->modes)) { | ||
402 | list_for_each_entry(modes[i], &connector->modes, head) | ||
403 | break; | ||
404 | } | ||
405 | DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : | ||
406 | "none"); | ||
407 | i++; | ||
408 | } | ||
409 | return true; | ||
410 | } | ||
411 | |||
412 | static int drm_pick_crtcs(struct drm_device *dev, | ||
413 | struct drm_crtc **best_crtcs, | ||
414 | struct drm_display_mode **modes, | ||
415 | int n, int width, int height) | ||
416 | { | ||
417 | int c, o; | ||
418 | struct drm_connector *connector; | ||
419 | struct drm_connector_helper_funcs *connector_funcs; | ||
420 | struct drm_encoder *encoder; | ||
421 | struct drm_crtc *best_crtc; | ||
422 | int my_score, best_score, score; | ||
423 | struct drm_crtc **crtcs, *crtc; | ||
424 | |||
425 | if (n == dev->mode_config.num_connector) | ||
426 | return 0; | ||
427 | c = 0; | ||
428 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
429 | if (c == n) | ||
430 | break; | ||
431 | c++; | ||
432 | } | ||
433 | |||
434 | best_crtcs[n] = NULL; | ||
435 | best_crtc = NULL; | ||
436 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); | ||
437 | if (modes[n] == NULL) | ||
438 | return best_score; | ||
439 | |||
440 | crtcs = kmalloc(dev->mode_config.num_connector * | ||
441 | sizeof(struct drm_crtc *), GFP_KERNEL); | ||
442 | if (!crtcs) | ||
443 | return best_score; | ||
444 | |||
445 | my_score = 1; | ||
446 | if (connector->status == connector_status_connected) | ||
447 | my_score++; | ||
448 | if (drm_has_cmdline_mode(connector)) | ||
449 | my_score++; | ||
450 | if (drm_has_preferred_mode(connector, width, height)) | ||
451 | my_score++; | ||
452 | |||
453 | connector_funcs = connector->helper_private; | ||
454 | encoder = connector_funcs->best_encoder(connector); | ||
455 | if (!encoder) | ||
456 | goto out; | ||
457 | |||
458 | connector->encoder = encoder; | ||
459 | |||
460 | /* select a crtc for this connector and then attempt to configure | ||
461 | remaining connectors */ | ||
462 | c = 0; | ||
463 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
464 | |||
465 | if ((encoder->possible_crtcs & (1 << c)) == 0) { | ||
466 | c++; | ||
467 | continue; | ||
468 | } | ||
469 | |||
470 | for (o = 0; o < n; o++) | ||
471 | if (best_crtcs[o] == crtc) | ||
472 | break; | ||
473 | |||
474 | if (o < n) { | ||
475 | /* ignore cloning for now */ | ||
476 | c++; | ||
477 | continue; | ||
478 | } | ||
479 | |||
480 | crtcs[n] = crtc; | ||
481 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); | ||
482 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, | ||
483 | width, height); | ||
484 | if (score > best_score) { | ||
485 | best_crtc = crtc; | ||
486 | best_score = score; | ||
487 | memcpy(best_crtcs, crtcs, | ||
488 | dev->mode_config.num_connector * | ||
489 | sizeof(struct drm_crtc *)); | ||
490 | } | ||
491 | c++; | ||
492 | } | ||
493 | out: | ||
494 | kfree(crtcs); | ||
495 | return best_score; | ||
496 | } | ||
497 | |||
498 | static void drm_setup_crtcs(struct drm_device *dev) | ||
499 | { | ||
500 | struct drm_crtc **crtcs; | ||
501 | struct drm_display_mode **modes; | ||
502 | struct drm_encoder *encoder; | ||
503 | struct drm_connector *connector; | ||
504 | bool *enabled; | ||
505 | int width, height; | ||
506 | int i, ret; | ||
507 | |||
508 | DRM_DEBUG_KMS("\n"); | ||
509 | |||
510 | width = dev->mode_config.max_width; | ||
511 | height = dev->mode_config.max_height; | ||
512 | |||
513 | /* clean out all the encoder/crtc combos */ | ||
514 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
515 | encoder->crtc = NULL; | ||
516 | } | ||
517 | |||
518 | crtcs = kcalloc(dev->mode_config.num_connector, | ||
519 | sizeof(struct drm_crtc *), GFP_KERNEL); | ||
520 | modes = kcalloc(dev->mode_config.num_connector, | ||
521 | sizeof(struct drm_display_mode *), GFP_KERNEL); | ||
522 | enabled = kcalloc(dev->mode_config.num_connector, | ||
523 | sizeof(bool), GFP_KERNEL); | ||
524 | |||
525 | drm_enable_connectors(dev, enabled); | ||
526 | |||
527 | ret = drm_target_preferred(dev, modes, enabled, width, height); | ||
528 | if (!ret) | ||
529 | DRM_ERROR("Unable to find initial modes\n"); | ||
530 | |||
531 | DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); | ||
532 | |||
533 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); | ||
534 | |||
535 | i = 0; | ||
536 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
537 | struct drm_display_mode *mode = modes[i]; | ||
538 | struct drm_crtc *crtc = crtcs[i]; | ||
539 | |||
540 | if (connector->encoder == NULL) { | ||
541 | i++; | ||
542 | continue; | ||
543 | } | ||
544 | |||
545 | if (mode && crtc) { | ||
546 | DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", | ||
547 | mode->name, crtc->base.id); | ||
548 | crtc->desired_mode = mode; | ||
549 | connector->encoder->crtc = crtc; | ||
550 | } else { | ||
551 | connector->encoder->crtc = NULL; | ||
552 | connector->encoder = NULL; | ||
553 | } | ||
554 | i++; | ||
555 | } | ||
556 | |||
557 | kfree(crtcs); | ||
558 | kfree(modes); | ||
559 | kfree(enabled); | ||
560 | } | ||
561 | |||
562 | /** | 251 | /** |
563 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? | 252 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
564 | * @encoder: encoder to test | 253 | * @encoder: encoder to test |
@@ -984,63 +673,6 @@ fail: | |||
984 | } | 673 | } |
985 | EXPORT_SYMBOL(drm_crtc_helper_set_config); | 674 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
986 | 675 | ||
987 | bool drm_helper_plugged_event(struct drm_device *dev) | ||
988 | { | ||
989 | DRM_DEBUG_KMS("\n"); | ||
990 | |||
991 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, | ||
992 | dev->mode_config.max_height); | ||
993 | |||
994 | drm_setup_crtcs(dev); | ||
995 | |||
996 | /* alert the driver fb layer */ | ||
997 | dev->mode_config.funcs->fb_changed(dev); | ||
998 | |||
999 | /* FIXME: send hotplug event */ | ||
1000 | return true; | ||
1001 | } | ||
1002 | /** | ||
1003 | * drm_initial_config - setup a sane initial connector configuration | ||
1004 | * @dev: DRM device | ||
1005 | * | ||
1006 | * LOCKING: | ||
1007 | * Called at init time, must take mode config lock. | ||
1008 | * | ||
1009 | * Scan the CRTCs and connectors and try to put together an initial setup. | ||
1010 | * At the moment, this is a cloned configuration across all heads with | ||
1011 | * a new framebuffer object as the backing store. | ||
1012 | * | ||
1013 | * RETURNS: | ||
1014 | * Zero if everything went ok, nonzero otherwise. | ||
1015 | */ | ||
1016 | bool drm_helper_initial_config(struct drm_device *dev) | ||
1017 | { | ||
1018 | int count = 0; | ||
1019 | |||
1020 | /* disable all the possible outputs/crtcs before entering KMS mode */ | ||
1021 | drm_helper_disable_unused_functions(dev); | ||
1022 | |||
1023 | drm_fb_helper_parse_command_line(dev); | ||
1024 | |||
1025 | count = drm_helper_probe_connector_modes(dev, | ||
1026 | dev->mode_config.max_width, | ||
1027 | dev->mode_config.max_height); | ||
1028 | |||
1029 | /* | ||
1030 | * we shouldn't end up with no modes here. | ||
1031 | */ | ||
1032 | if (count == 0) | ||
1033 | printk(KERN_INFO "No connectors reported connected with modes\n"); | ||
1034 | |||
1035 | drm_setup_crtcs(dev); | ||
1036 | |||
1037 | /* alert the driver fb layer */ | ||
1038 | dev->mode_config.funcs->fb_changed(dev); | ||
1039 | |||
1040 | return 0; | ||
1041 | } | ||
1042 | EXPORT_SYMBOL(drm_helper_initial_config); | ||
1043 | |||
1044 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) | 676 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
1045 | { | 677 | { |
1046 | int dpms = DRM_MODE_DPMS_OFF; | 678 | int dpms = DRM_MODE_DPMS_OFF; |
@@ -1123,27 +755,6 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode) | |||
1123 | } | 755 | } |
1124 | EXPORT_SYMBOL(drm_helper_connector_dpms); | 756 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
1125 | 757 | ||
1126 | /** | ||
1127 | * drm_hotplug_stage_two | ||
1128 | * @dev DRM device | ||
1129 | * @connector hotpluged connector | ||
1130 | * | ||
1131 | * LOCKING. | ||
1132 | * Caller must hold mode config lock, function might grab struct lock. | ||
1133 | * | ||
1134 | * Stage two of a hotplug. | ||
1135 | * | ||
1136 | * RETURNS: | ||
1137 | * Zero on success, errno on failure. | ||
1138 | */ | ||
1139 | int drm_helper_hotplug_stage_two(struct drm_device *dev) | ||
1140 | { | ||
1141 | drm_helper_plugged_event(dev); | ||
1142 | |||
1143 | return 0; | ||
1144 | } | ||
1145 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); | ||
1146 | |||
1147 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, | 758 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
1148 | struct drm_mode_fb_cmd *mode_cmd) | 759 | struct drm_mode_fb_cmd *mode_cmd) |
1149 | { | 760 | { |