diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2009-10-15 16:16:35 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-10-15 18:46:52 -0400 |
commit | 0294cf4f7f895ddcf284b1ce7cf27a2c9d7106f7 (patch) | |
tree | 7c69f11e946519457c08e8106c05df1a850b77fd | |
parent | 1b4d7d75ccff38008ccd40f8e2d74e33a087caaa (diff) |
drm/radeon/kms: fix connector edid handling
Based partly on a patch from
Christian Koenig <deathsimple@vodafone.de>
- fix several memory leaks in radeon_connector->edid handling
- store edid in radeon_connector->edid in detect() or get_modes()
- switch hdmi detect code to use radeon_connector->edid
- add support for oem boards multiple connectors that share
a ddc line.
- short circuit lvds_detect() if have a stored edid
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 75 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_encoders.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_mode.h | 2 |
4 files changed, 73 insertions, 32 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 729207a1e83e..5b6067aa08bd 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c | |||
@@ -400,7 +400,6 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec | |||
400 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | 400 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
401 | struct drm_encoder *encoder = radeon_best_single_encoder(connector); | 401 | struct drm_encoder *encoder = radeon_best_single_encoder(connector); |
402 | enum drm_connector_status ret = connector_status_disconnected; | 402 | enum drm_connector_status ret = connector_status_disconnected; |
403 | bool dret; | ||
404 | 403 | ||
405 | if (encoder) { | 404 | if (encoder) { |
406 | struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); | 405 | struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
@@ -413,12 +412,17 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec | |||
413 | } | 412 | } |
414 | 413 | ||
415 | /* check for edid as well */ | 414 | /* check for edid as well */ |
416 | if (radeon_connector->ddc_bus) { | 415 | if (radeon_connector->edid) |
417 | radeon_i2c_do_lock(radeon_connector, 1); | 416 | ret = connector_status_connected; |
418 | dret = radeon_ddc_probe(radeon_connector); | 417 | else { |
419 | radeon_i2c_do_lock(radeon_connector, 0); | 418 | if (radeon_connector->ddc_bus) { |
420 | if (dret) | 419 | radeon_i2c_do_lock(radeon_connector, 1); |
421 | ret = connector_status_connected; | 420 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, |
421 | &radeon_connector->ddc_bus->adapter); | ||
422 | radeon_i2c_do_lock(radeon_connector, 0); | ||
423 | if (radeon_connector->edid) | ||
424 | ret = connector_status_connected; | ||
425 | } | ||
422 | } | 426 | } |
423 | /* check acpi lid status ??? */ | 427 | /* check acpi lid status ??? */ |
424 | 428 | ||
@@ -432,6 +436,8 @@ static void radeon_connector_destroy(struct drm_connector *connector) | |||
432 | 436 | ||
433 | if (radeon_connector->ddc_bus) | 437 | if (radeon_connector->ddc_bus) |
434 | radeon_i2c_destroy(radeon_connector->ddc_bus); | 438 | radeon_i2c_destroy(radeon_connector->ddc_bus); |
439 | if (radeon_connector->edid) | ||
440 | kfree(radeon_connector->edid); | ||
435 | kfree(radeon_connector->con_priv); | 441 | kfree(radeon_connector->con_priv); |
436 | drm_sysfs_connector_remove(connector); | 442 | drm_sysfs_connector_remove(connector); |
437 | drm_connector_cleanup(connector); | 443 | drm_connector_cleanup(connector); |
@@ -519,9 +525,32 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect | |||
519 | radeon_i2c_do_lock(radeon_connector, 1); | 525 | radeon_i2c_do_lock(radeon_connector, 1); |
520 | dret = radeon_ddc_probe(radeon_connector); | 526 | dret = radeon_ddc_probe(radeon_connector); |
521 | radeon_i2c_do_lock(radeon_connector, 0); | 527 | radeon_i2c_do_lock(radeon_connector, 0); |
522 | if (dret) | 528 | if (dret) { |
523 | ret = connector_status_connected; | 529 | if (radeon_connector->edid) { |
524 | else { | 530 | kfree(radeon_connector->edid); |
531 | radeon_connector->edid = NULL; | ||
532 | } | ||
533 | radeon_i2c_do_lock(radeon_connector, 1); | ||
534 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); | ||
535 | radeon_i2c_do_lock(radeon_connector, 0); | ||
536 | |||
537 | if (!radeon_connector->edid) { | ||
538 | DRM_ERROR("DDC responded but not EDID found for %s\n", | ||
539 | drm_get_connector_name(connector)); | ||
540 | } else { | ||
541 | radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); | ||
542 | |||
543 | /* some oems have boards with separate digital and analog connectors | ||
544 | * with a shared ddc line (often vga + hdmi) | ||
545 | */ | ||
546 | if (radeon_connector->use_digital && radeon_connector->shared_ddc) { | ||
547 | kfree(radeon_connector->edid); | ||
548 | radeon_connector->edid = NULL; | ||
549 | ret = connector_status_disconnected; | ||
550 | } else | ||
551 | ret = connector_status_connected; | ||
552 | } | ||
553 | } else { | ||
525 | if (radeon_connector->dac_load_detect) { | 554 | if (radeon_connector->dac_load_detect) { |
526 | encoder_funcs = encoder->helper_private; | 555 | encoder_funcs = encoder->helper_private; |
527 | ret = encoder_funcs->detect(encoder, connector); | 556 | ret = encoder_funcs->detect(encoder, connector); |
@@ -649,6 +678,10 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect | |||
649 | dret = radeon_ddc_probe(radeon_connector); | 678 | dret = radeon_ddc_probe(radeon_connector); |
650 | radeon_i2c_do_lock(radeon_connector, 0); | 679 | radeon_i2c_do_lock(radeon_connector, 0); |
651 | if (dret) { | 680 | if (dret) { |
681 | if (radeon_connector->edid) { | ||
682 | kfree(radeon_connector->edid); | ||
683 | radeon_connector->edid = NULL; | ||
684 | } | ||
652 | radeon_i2c_do_lock(radeon_connector, 1); | 685 | radeon_i2c_do_lock(radeon_connector, 1); |
653 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); | 686 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); |
654 | radeon_i2c_do_lock(radeon_connector, 0); | 687 | radeon_i2c_do_lock(radeon_connector, 0); |
@@ -659,10 +692,15 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect | |||
659 | } else { | 692 | } else { |
660 | radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); | 693 | radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); |
661 | 694 | ||
662 | /* if this isn't a digital monitor | 695 | /* some oems have boards with separate digital and analog connectors |
663 | then we need to make sure we don't have any | 696 | * with a shared ddc line (often vga + hdmi) |
664 | TV conflicts */ | 697 | */ |
665 | ret = connector_status_connected; | 698 | if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) { |
699 | kfree(radeon_connector->edid); | ||
700 | radeon_connector->edid = NULL; | ||
701 | ret = connector_status_disconnected; | ||
702 | } else | ||
703 | ret = connector_status_connected; | ||
666 | } | 704 | } |
667 | } | 705 | } |
668 | 706 | ||
@@ -787,6 +825,7 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
787 | struct radeon_connector *radeon_connector; | 825 | struct radeon_connector *radeon_connector; |
788 | struct radeon_connector_atom_dig *radeon_dig_connector; | 826 | struct radeon_connector_atom_dig *radeon_dig_connector; |
789 | uint32_t subpixel_order = SubPixelNone; | 827 | uint32_t subpixel_order = SubPixelNone; |
828 | bool shared_ddc = false; | ||
790 | int ret; | 829 | int ret; |
791 | 830 | ||
792 | /* fixme - tv/cv/din */ | 831 | /* fixme - tv/cv/din */ |
@@ -800,6 +839,13 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
800 | radeon_connector->devices |= supported_device; | 839 | radeon_connector->devices |= supported_device; |
801 | return; | 840 | return; |
802 | } | 841 | } |
842 | if (radeon_connector->ddc_bus && i2c_bus->valid) { | ||
843 | if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus, | ||
844 | sizeof(struct radeon_i2c_bus_rec)) == 0) { | ||
845 | radeon_connector->shared_ddc = true; | ||
846 | shared_ddc = true; | ||
847 | } | ||
848 | } | ||
803 | } | 849 | } |
804 | 850 | ||
805 | radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); | 851 | radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); |
@@ -810,6 +856,7 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
810 | 856 | ||
811 | radeon_connector->connector_id = connector_id; | 857 | radeon_connector->connector_id = connector_id; |
812 | radeon_connector->devices = supported_device; | 858 | radeon_connector->devices = supported_device; |
859 | radeon_connector->shared_ddc = shared_ddc; | ||
813 | switch (connector_type) { | 860 | switch (connector_type) { |
814 | case DRM_MODE_CONNECTOR_VGA: | 861 | case DRM_MODE_CONNECTOR_VGA: |
815 | drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); | 862 | drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); |
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index a20bf43389b3..c85df4afcb7a 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c | |||
@@ -334,27 +334,19 @@ static bool radeon_setup_enc_conn(struct drm_device *dev) | |||
334 | 334 | ||
335 | int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) | 335 | int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) |
336 | { | 336 | { |
337 | struct edid *edid; | ||
338 | int ret = 0; | 337 | int ret = 0; |
339 | 338 | ||
340 | if (!radeon_connector->ddc_bus) | 339 | if (!radeon_connector->ddc_bus) |
341 | return -1; | 340 | return -1; |
342 | if (!radeon_connector->edid) { | 341 | if (!radeon_connector->edid) { |
343 | radeon_i2c_do_lock(radeon_connector, 1); | 342 | radeon_i2c_do_lock(radeon_connector, 1); |
344 | edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); | 343 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); |
345 | radeon_i2c_do_lock(radeon_connector, 0); | 344 | radeon_i2c_do_lock(radeon_connector, 0); |
346 | } else | 345 | } |
347 | edid = radeon_connector->edid; | ||
348 | 346 | ||
349 | if (edid) { | 347 | if (radeon_connector->edid) { |
350 | /* update digital bits here */ | 348 | drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); |
351 | if (edid->input & DRM_EDID_INPUT_DIGITAL) | 349 | ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); |
352 | radeon_connector->use_digital = 1; | ||
353 | else | ||
354 | radeon_connector->use_digital = 0; | ||
355 | drm_mode_connector_update_edid_property(&radeon_connector->base, edid); | ||
356 | ret = drm_add_edid_modes(&radeon_connector->base, edid); | ||
357 | kfree(edid); | ||
358 | return ret; | 350 | return ret; |
359 | } | 351 | } |
360 | drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); | 352 | drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); |
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 6cdef27be99d..b70440e8d179 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c | |||
@@ -449,7 +449,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) | |||
449 | case 1: | 449 | case 1: |
450 | args.v1.ucMisc = 0; | 450 | args.v1.ucMisc = 0; |
451 | args.v1.ucAction = action; | 451 | args.v1.ucAction = action; |
452 | if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) | 452 | if (drm_detect_hdmi_monitor(radeon_connector->edid)) |
453 | args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; | 453 | args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; |
454 | args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); | 454 | args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); |
455 | if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { | 455 | if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { |
@@ -474,7 +474,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) | |||
474 | if (dig->coherent_mode) | 474 | if (dig->coherent_mode) |
475 | args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; | 475 | args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; |
476 | } | 476 | } |
477 | if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) | 477 | if (drm_detect_hdmi_monitor(radeon_connector->edid)) |
478 | args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; | 478 | args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; |
479 | args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); | 479 | args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); |
480 | args.v2.ucTruncate = 0; | 480 | args.v2.ucTruncate = 0; |
@@ -532,7 +532,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) | |||
532 | switch (connector->connector_type) { | 532 | switch (connector->connector_type) { |
533 | case DRM_MODE_CONNECTOR_DVII: | 533 | case DRM_MODE_CONNECTOR_DVII: |
534 | case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ | 534 | case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ |
535 | if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) | 535 | if (drm_detect_hdmi_monitor(radeon_connector->edid)) |
536 | return ATOM_ENCODER_MODE_HDMI; | 536 | return ATOM_ENCODER_MODE_HDMI; |
537 | else if (radeon_connector->use_digital) | 537 | else if (radeon_connector->use_digital) |
538 | return ATOM_ENCODER_MODE_DVI; | 538 | return ATOM_ENCODER_MODE_DVI; |
@@ -542,7 +542,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) | |||
542 | case DRM_MODE_CONNECTOR_DVID: | 542 | case DRM_MODE_CONNECTOR_DVID: |
543 | case DRM_MODE_CONNECTOR_HDMIA: | 543 | case DRM_MODE_CONNECTOR_HDMIA: |
544 | default: | 544 | default: |
545 | if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) | 545 | if (drm_detect_hdmi_monitor(radeon_connector->edid)) |
546 | return ATOM_ENCODER_MODE_HDMI; | 546 | return ATOM_ENCODER_MODE_HDMI; |
547 | else | 547 | else |
548 | return ATOM_ENCODER_MODE_DVI; | 548 | return ATOM_ENCODER_MODE_DVI; |
@@ -554,7 +554,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) | |||
554 | /*if (radeon_output->MonType == MT_DP) | 554 | /*if (radeon_output->MonType == MT_DP) |
555 | return ATOM_ENCODER_MODE_DP; | 555 | return ATOM_ENCODER_MODE_DP; |
556 | else*/ | 556 | else*/ |
557 | if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) | 557 | if (drm_detect_hdmi_monitor(radeon_connector->edid)) |
558 | return ATOM_ENCODER_MODE_HDMI; | 558 | return ATOM_ENCODER_MODE_HDMI; |
559 | else | 559 | else |
560 | return ATOM_ENCODER_MODE_DVI; | 560 | return ATOM_ENCODER_MODE_DVI; |
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 3d2631be073c..b6868fbe7346 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h | |||
@@ -297,6 +297,8 @@ struct radeon_connector { | |||
297 | uint32_t connector_id; | 297 | uint32_t connector_id; |
298 | uint32_t devices; | 298 | uint32_t devices; |
299 | struct radeon_i2c_chan *ddc_bus; | 299 | struct radeon_i2c_chan *ddc_bus; |
300 | /* some systems have a an hdmi and vga port with a shared ddc line */ | ||
301 | bool shared_ddc; | ||
300 | bool use_digital; | 302 | bool use_digital; |
301 | /* we need to mind the EDID between detect | 303 | /* we need to mind the EDID between detect |
302 | and get modes due to analog/digital/tvencoder */ | 304 | and get modes due to analog/digital/tvencoder */ |