diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2010-05-31 23:32:42 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-07-12 19:41:51 -0400 |
commit | d17f395cdcec39033a481f96d75e8b3d3c41d43a (patch) | |
tree | e95a22e58d680ed7cb25c3bec70282df5da31c38 | |
parent | 2dfe36b1b6cb51ddfd2358774fd8f097528cfdd0 (diff) |
drm/nouveau: move LVDS detection back to connector detect() time
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 202 |
1 files changed, 87 insertions, 115 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 149ed224c3cb..a8c44c9eedf6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
@@ -236,20 +236,6 @@ nouveau_connector_detect(struct drm_connector *connector) | |||
236 | struct nouveau_i2c_chan *i2c; | 236 | struct nouveau_i2c_chan *i2c; |
237 | int type, flags; | 237 | int type, flags; |
238 | 238 | ||
239 | if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS) | ||
240 | nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); | ||
241 | if (nv_encoder && nv_connector->native_mode) { | ||
242 | unsigned status = connector_status_connected; | ||
243 | |||
244 | #if defined(CONFIG_ACPI_BUTTON) || \ | ||
245 | (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE)) | ||
246 | if (!nouveau_ignorelid && !acpi_lid_open()) | ||
247 | status = connector_status_unknown; | ||
248 | #endif | ||
249 | nouveau_connector_set_encoder(connector, nv_encoder); | ||
250 | return status; | ||
251 | } | ||
252 | |||
253 | /* Cleanup the previous EDID block. */ | 239 | /* Cleanup the previous EDID block. */ |
254 | if (nv_connector->edid) { | 240 | if (nv_connector->edid) { |
255 | drm_mode_connector_update_edid_property(connector, NULL); | 241 | drm_mode_connector_update_edid_property(connector, NULL); |
@@ -321,6 +307,67 @@ detect_analog: | |||
321 | return connector_status_disconnected; | 307 | return connector_status_disconnected; |
322 | } | 308 | } |
323 | 309 | ||
310 | static enum drm_connector_status | ||
311 | nouveau_connector_detect_lvds(struct drm_connector *connector) | ||
312 | { | ||
313 | struct drm_device *dev = connector->dev; | ||
314 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
315 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
316 | struct nouveau_encoder *nv_encoder = NULL; | ||
317 | enum drm_connector_status status = connector_status_disconnected; | ||
318 | |||
319 | /* Cleanup the previous EDID block. */ | ||
320 | if (nv_connector->edid) { | ||
321 | drm_mode_connector_update_edid_property(connector, NULL); | ||
322 | kfree(nv_connector->edid); | ||
323 | nv_connector->edid = NULL; | ||
324 | } | ||
325 | |||
326 | nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); | ||
327 | if (!nv_encoder) | ||
328 | return connector_status_disconnected; | ||
329 | |||
330 | if (!dev_priv->vbios.fp_no_ddc) { | ||
331 | status = nouveau_connector_detect(connector); | ||
332 | if (status == connector_status_connected) | ||
333 | goto out; | ||
334 | } | ||
335 | |||
336 | /* If no EDID found above, and the VBIOS indicates a hardcoded | ||
337 | * modeline is avalilable for the panel, set it as the panel's | ||
338 | * native mode and exit. | ||
339 | */ | ||
340 | if (nouveau_bios_fp_mode(dev, NULL) && (dev_priv->vbios.fp_no_ddc || | ||
341 | nv_encoder->dcb->lvdsconf.use_straps_for_mode)) { | ||
342 | status = connector_status_connected; | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | /* Still nothing, some VBIOS images have a hardcoded EDID block | ||
347 | * stored for the panel stored in them. | ||
348 | */ | ||
349 | if (!dev_priv->vbios.fp_no_ddc) { | ||
350 | struct edid *edid = | ||
351 | (struct edid *)nouveau_bios_embedded_edid(dev); | ||
352 | if (edid) { | ||
353 | nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL); | ||
354 | *(nv_connector->edid) = *edid; | ||
355 | status = connector_status_connected; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | out: | ||
360 | #if defined(CONFIG_ACPI_BUTTON) || \ | ||
361 | (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE)) | ||
362 | if (status == connector_status_connected && | ||
363 | !nouveau_ignorelid && !acpi_lid_open()) | ||
364 | status = connector_status_unknown; | ||
365 | #endif | ||
366 | |||
367 | drm_mode_connector_update_edid_property(connector, nv_connector->edid); | ||
368 | return status; | ||
369 | } | ||
370 | |||
324 | static void | 371 | static void |
325 | nouveau_connector_force(struct drm_connector *connector) | 372 | nouveau_connector_force(struct drm_connector *connector) |
326 | { | 373 | { |
@@ -534,21 +581,28 @@ static int | |||
534 | nouveau_connector_get_modes(struct drm_connector *connector) | 581 | nouveau_connector_get_modes(struct drm_connector *connector) |
535 | { | 582 | { |
536 | struct drm_device *dev = connector->dev; | 583 | struct drm_device *dev = connector->dev; |
584 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
537 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | 585 | struct nouveau_connector *nv_connector = nouveau_connector(connector); |
538 | struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; | 586 | struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; |
587 | struct drm_display_mode mode; | ||
539 | int ret = 0; | 588 | int ret = 0; |
540 | 589 | ||
541 | /* If we're not LVDS, destroy the previous native mode, the attached | 590 | /* destroy the native mode, the attached monitor could have changed. |
542 | * monitor could have changed. | ||
543 | */ | 591 | */ |
544 | if (nv_connector->dcb->type != DCB_CONNECTOR_LVDS && | 592 | if (nv_connector->native_mode) { |
545 | nv_connector->native_mode) { | ||
546 | drm_mode_destroy(dev, nv_connector->native_mode); | 593 | drm_mode_destroy(dev, nv_connector->native_mode); |
547 | nv_connector->native_mode = NULL; | 594 | nv_connector->native_mode = NULL; |
548 | } | 595 | } |
549 | 596 | ||
550 | if (nv_connector->edid) | 597 | if (nv_connector->edid) |
551 | ret = drm_add_edid_modes(connector, nv_connector->edid); | 598 | ret = drm_add_edid_modes(connector, nv_connector->edid); |
599 | else | ||
600 | if (nv_encoder->dcb->type == OUTPUT_LVDS && | ||
601 | (nv_encoder->dcb->lvdsconf.use_straps_for_mode || | ||
602 | dev_priv->vbios.fp_no_ddc) && | ||
603 | nouveau_bios_fp_mode(dev, &mode)) { | ||
604 | nv_connector->native_mode = drm_mode_duplicate(dev, &mode); | ||
605 | } | ||
552 | 606 | ||
553 | /* Find the native mode if this is a digital panel, if we didn't | 607 | /* Find the native mode if this is a digital panel, if we didn't |
554 | * find any modes through DDC previously add the native mode to | 608 | * find any modes through DDC previously add the native mode to |
@@ -662,99 +716,28 @@ nouveau_connector_funcs = { | |||
662 | .force = nouveau_connector_force | 716 | .force = nouveau_connector_force |
663 | }; | 717 | }; |
664 | 718 | ||
665 | static int | 719 | static const struct drm_connector_funcs |
666 | nouveau_connector_create_lvds(struct drm_device *dev, | 720 | nouveau_connector_funcs_lvds = { |
667 | struct drm_connector *connector) | 721 | .dpms = drm_helper_connector_dpms, |
668 | { | 722 | .save = NULL, |
669 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | 723 | .restore = NULL, |
670 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 724 | .detect = nouveau_connector_detect_lvds, |
671 | struct nouveau_i2c_chan *i2c = NULL; | 725 | .destroy = nouveau_connector_destroy, |
672 | struct nouveau_encoder *nv_encoder; | 726 | .fill_modes = drm_helper_probe_single_connector_modes, |
673 | struct drm_display_mode native, *mode, *temp; | 727 | .set_property = nouveau_connector_set_property, |
674 | bool dummy, if_is_24bit = false; | 728 | .force = nouveau_connector_force |
675 | int ret, flags; | 729 | }; |
676 | |||
677 | nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); | ||
678 | if (!nv_encoder) | ||
679 | return -ENODEV; | ||
680 | |||
681 | ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit); | ||
682 | if (ret) { | ||
683 | NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n"); | ||
684 | return ret; | ||
685 | } | ||
686 | nv_connector->use_dithering = !if_is_24bit; | ||
687 | |||
688 | /* Firstly try getting EDID over DDC, if allowed and I2C channel | ||
689 | * is available. | ||
690 | */ | ||
691 | if (!dev_priv->vbios.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf) | ||
692 | i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
693 | |||
694 | if (i2c) { | ||
695 | nouveau_connector_ddc_prepare(connector, &flags); | ||
696 | nv_connector->edid = drm_get_edid(connector, &i2c->adapter); | ||
697 | nouveau_connector_ddc_finish(connector, flags); | ||
698 | } | ||
699 | |||
700 | /* If no EDID found above, and the VBIOS indicates a hardcoded | ||
701 | * modeline is avalilable for the panel, set it as the panel's | ||
702 | * native mode and exit. | ||
703 | */ | ||
704 | if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) && | ||
705 | (nv_encoder->dcb->lvdsconf.use_straps_for_mode || | ||
706 | dev_priv->vbios.fp_no_ddc)) { | ||
707 | nv_connector->native_mode = drm_mode_duplicate(dev, &native); | ||
708 | goto out; | ||
709 | } | ||
710 | |||
711 | /* Still nothing, some VBIOS images have a hardcoded EDID block | ||
712 | * stored for the panel stored in them. | ||
713 | */ | ||
714 | if (!nv_connector->edid && !nv_connector->native_mode && | ||
715 | !dev_priv->vbios.fp_no_ddc) { | ||
716 | struct edid *edid = | ||
717 | (struct edid *)nouveau_bios_embedded_edid(dev); | ||
718 | if (edid) { | ||
719 | nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL); | ||
720 | *(nv_connector->edid) = *edid; | ||
721 | } | ||
722 | } | ||
723 | |||
724 | if (!nv_connector->edid) | ||
725 | goto out; | ||
726 | |||
727 | /* We didn't find/use a panel mode from the VBIOS, so parse the EDID | ||
728 | * block and look for the preferred mode there. | ||
729 | */ | ||
730 | ret = drm_add_edid_modes(connector, nv_connector->edid); | ||
731 | if (ret == 0) | ||
732 | goto out; | ||
733 | nv_connector->detected_encoder = nv_encoder; | ||
734 | nv_connector->native_mode = nouveau_connector_native_mode(connector); | ||
735 | list_for_each_entry_safe(mode, temp, &connector->probed_modes, head) | ||
736 | drm_mode_remove(connector, mode); | ||
737 | |||
738 | out: | ||
739 | if (!nv_connector->native_mode) { | ||
740 | NV_ERROR(dev, "LVDS present in DCB table, but couldn't " | ||
741 | "determine its native mode. Disabling.\n"); | ||
742 | return -ENODEV; | ||
743 | } | ||
744 | |||
745 | drm_mode_connector_update_edid_property(connector, nv_connector->edid); | ||
746 | return 0; | ||
747 | } | ||
748 | 730 | ||
749 | int | 731 | int |
750 | nouveau_connector_create(struct drm_device *dev, | 732 | nouveau_connector_create(struct drm_device *dev, |
751 | struct dcb_connector_table_entry *dcb) | 733 | struct dcb_connector_table_entry *dcb) |
752 | { | 734 | { |
735 | const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; | ||
753 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 736 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
754 | struct nouveau_connector *nv_connector = NULL; | 737 | struct nouveau_connector *nv_connector = NULL; |
755 | struct drm_connector *connector; | 738 | struct drm_connector *connector; |
756 | struct drm_encoder *encoder; | 739 | struct drm_encoder *encoder; |
757 | int ret, type; | 740 | int type; |
758 | 741 | ||
759 | NV_DEBUG_KMS(dev, "\n"); | 742 | NV_DEBUG_KMS(dev, "\n"); |
760 | 743 | ||
@@ -787,6 +770,7 @@ nouveau_connector_create(struct drm_device *dev, | |||
787 | case DCB_CONNECTOR_LVDS: | 770 | case DCB_CONNECTOR_LVDS: |
788 | NV_INFO(dev, "Detected a LVDS connector\n"); | 771 | NV_INFO(dev, "Detected a LVDS connector\n"); |
789 | type = DRM_MODE_CONNECTOR_LVDS; | 772 | type = DRM_MODE_CONNECTOR_LVDS; |
773 | funcs = &nouveau_connector_funcs_lvds; | ||
790 | break; | 774 | break; |
791 | case DCB_CONNECTOR_DP: | 775 | case DCB_CONNECTOR_DP: |
792 | NV_INFO(dev, "Detected a DisplayPort connector\n"); | 776 | NV_INFO(dev, "Detected a DisplayPort connector\n"); |
@@ -811,7 +795,7 @@ nouveau_connector_create(struct drm_device *dev, | |||
811 | connector->interlace_allowed = false; | 795 | connector->interlace_allowed = false; |
812 | connector->doublescan_allowed = false; | 796 | connector->doublescan_allowed = false; |
813 | 797 | ||
814 | drm_connector_init(dev, connector, &nouveau_connector_funcs, type); | 798 | drm_connector_init(dev, connector, funcs, type); |
815 | drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); | 799 | drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); |
816 | 800 | ||
817 | /* attach encoders */ | 801 | /* attach encoders */ |
@@ -841,9 +825,6 @@ nouveau_connector_create(struct drm_device *dev, | |||
841 | drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); | 825 | drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); |
842 | } | 826 | } |
843 | 827 | ||
844 | if (dcb->type != DCB_CONNECTOR_LVDS) | ||
845 | nv_connector->use_dithering = false; | ||
846 | |||
847 | switch (dcb->type) { | 828 | switch (dcb->type) { |
848 | case DCB_CONNECTOR_VGA: | 829 | case DCB_CONNECTOR_VGA: |
849 | connector->polled = DRM_CONNECTOR_POLL_CONNECT; | 830 | connector->polled = DRM_CONNECTOR_POLL_CONNECT; |
@@ -883,14 +864,5 @@ nouveau_connector_create(struct drm_device *dev, | |||
883 | } | 864 | } |
884 | 865 | ||
885 | drm_sysfs_connector_add(connector); | 866 | drm_sysfs_connector_add(connector); |
886 | |||
887 | if (dcb->type == DCB_CONNECTOR_LVDS) { | ||
888 | ret = nouveau_connector_create_lvds(dev, connector); | ||
889 | if (ret) { | ||
890 | connector->funcs->destroy(connector); | ||
891 | return ret; | ||
892 | } | ||
893 | } | ||
894 | |||
895 | return 0; | 867 | return 0; |
896 | } | 868 | } |