diff options
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 121 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 3 |
2 files changed, 95 insertions, 29 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a839a28d8ee6..fab2bdf9c423 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -550,11 +550,20 @@ static int add_detailed_info(struct drm_connector *connector, | |||
550 | } | 550 | } |
551 | 551 | ||
552 | #define DDC_ADDR 0x50 | 552 | #define DDC_ADDR 0x50 |
553 | 553 | /** | |
554 | unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) | 554 | * Get EDID information via I2C. |
555 | * | ||
556 | * \param adapter : i2c device adaptor | ||
557 | * \param buf : EDID data buffer to be filled | ||
558 | * \param len : EDID data buffer length | ||
559 | * \return 0 on success or -1 on failure. | ||
560 | * | ||
561 | * Try to fetch EDID information by calling i2c driver function. | ||
562 | */ | ||
563 | int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, | ||
564 | unsigned char *buf, int len) | ||
555 | { | 565 | { |
556 | unsigned char start = 0x0; | 566 | unsigned char start = 0x0; |
557 | unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); | ||
558 | struct i2c_msg msgs[] = { | 567 | struct i2c_msg msgs[] = { |
559 | { | 568 | { |
560 | .addr = DDC_ADDR, | 569 | .addr = DDC_ADDR, |
@@ -564,31 +573,36 @@ unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) | |||
564 | }, { | 573 | }, { |
565 | .addr = DDC_ADDR, | 574 | .addr = DDC_ADDR, |
566 | .flags = I2C_M_RD, | 575 | .flags = I2C_M_RD, |
567 | .len = EDID_LENGTH, | 576 | .len = len, |
568 | .buf = buf, | 577 | .buf = buf, |
569 | } | 578 | } |
570 | }; | 579 | }; |
571 | 580 | ||
572 | if (!buf) { | ||
573 | dev_warn(&adapter->dev, "unable to allocate memory for EDID " | ||
574 | "block.\n"); | ||
575 | return NULL; | ||
576 | } | ||
577 | |||
578 | if (i2c_transfer(adapter, msgs, 2) == 2) | 581 | if (i2c_transfer(adapter, msgs, 2) == 2) |
579 | return buf; | 582 | return 0; |
580 | 583 | ||
581 | dev_info(&adapter->dev, "unable to read EDID block.\n"); | 584 | dev_info(&adapter->dev, "unable to read EDID block.\n"); |
582 | kfree(buf); | 585 | return -1; |
583 | return NULL; | ||
584 | } | 586 | } |
585 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); | 587 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); |
586 | 588 | ||
587 | static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | 589 | /** |
590 | * Get EDID information. | ||
591 | * | ||
592 | * \param adapter : i2c device adaptor. | ||
593 | * \param buf : EDID data buffer to be filled | ||
594 | * \param len : EDID data buffer length | ||
595 | * \return 0 on success or -1 on failure. | ||
596 | * | ||
597 | * Initialize DDC, then fetch EDID information | ||
598 | * by calling drm_do_probe_ddc_edid function. | ||
599 | */ | ||
600 | static int drm_ddc_read(struct i2c_adapter *adapter, | ||
601 | unsigned char *buf, int len) | ||
588 | { | 602 | { |
589 | struct i2c_algo_bit_data *algo_data = adapter->algo_data; | 603 | struct i2c_algo_bit_data *algo_data = adapter->algo_data; |
590 | unsigned char *edid = NULL; | ||
591 | int i, j; | 604 | int i, j; |
605 | int ret = -1; | ||
592 | 606 | ||
593 | algo_data->setscl(algo_data->data, 1); | 607 | algo_data->setscl(algo_data->data, 1); |
594 | 608 | ||
@@ -616,7 +630,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | |||
616 | msleep(15); | 630 | msleep(15); |
617 | 631 | ||
618 | /* Do the real work */ | 632 | /* Do the real work */ |
619 | edid = drm_do_probe_ddc_edid(adapter); | 633 | ret = drm_do_probe_ddc_edid(adapter, buf, len); |
620 | algo_data->setsda(algo_data->data, 0); | 634 | algo_data->setsda(algo_data->data, 0); |
621 | algo_data->setscl(algo_data->data, 0); | 635 | algo_data->setscl(algo_data->data, 0); |
622 | msleep(15); | 636 | msleep(15); |
@@ -632,7 +646,7 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | |||
632 | msleep(15); | 646 | msleep(15); |
633 | algo_data->setscl(algo_data->data, 0); | 647 | algo_data->setscl(algo_data->data, 0); |
634 | algo_data->setsda(algo_data->data, 0); | 648 | algo_data->setsda(algo_data->data, 0); |
635 | if (edid) | 649 | if (ret == 0) |
636 | break; | 650 | break; |
637 | } | 651 | } |
638 | /* Release the DDC lines when done or the Apple Cinema HD display | 652 | /* Release the DDC lines when done or the Apple Cinema HD display |
@@ -641,9 +655,31 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | |||
641 | algo_data->setsda(algo_data->data, 1); | 655 | algo_data->setsda(algo_data->data, 1); |
642 | algo_data->setscl(algo_data->data, 1); | 656 | algo_data->setscl(algo_data->data, 1); |
643 | 657 | ||
644 | return edid; | 658 | return ret; |
659 | } | ||
660 | |||
661 | static int drm_ddc_read_edid(struct drm_connector *connector, | ||
662 | struct i2c_adapter *adapter, | ||
663 | char *buf, int len) | ||
664 | { | ||
665 | int ret; | ||
666 | |||
667 | ret = drm_ddc_read(adapter, buf, len); | ||
668 | if (ret != 0) { | ||
669 | dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", | ||
670 | drm_get_connector_name(connector)); | ||
671 | goto end; | ||
672 | } | ||
673 | if (!edid_is_valid((struct edid *)buf)) { | ||
674 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | ||
675 | drm_get_connector_name(connector)); | ||
676 | ret = -1; | ||
677 | } | ||
678 | end: | ||
679 | return ret; | ||
645 | } | 680 | } |
646 | 681 | ||
682 | #define MAX_EDID_EXT_NUM 4 | ||
647 | /** | 683 | /** |
648 | * drm_get_edid - get EDID data, if available | 684 | * drm_get_edid - get EDID data, if available |
649 | * @connector: connector we're probing | 685 | * @connector: connector we're probing |
@@ -656,24 +692,53 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) | |||
656 | struct edid *drm_get_edid(struct drm_connector *connector, | 692 | struct edid *drm_get_edid(struct drm_connector *connector, |
657 | struct i2c_adapter *adapter) | 693 | struct i2c_adapter *adapter) |
658 | { | 694 | { |
695 | int ret; | ||
659 | struct edid *edid; | 696 | struct edid *edid; |
660 | 697 | ||
661 | edid = (struct edid *)drm_ddc_read(adapter); | 698 | edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1), |
662 | if (!edid) { | 699 | GFP_KERNEL); |
663 | dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", | 700 | if (edid == NULL) { |
664 | drm_get_connector_name(connector)); | 701 | dev_warn(&connector->dev->pdev->dev, |
665 | return NULL; | 702 | "Failed to allocate EDID\n"); |
703 | goto end; | ||
666 | } | 704 | } |
667 | if (!edid_is_valid(edid)) { | 705 | |
668 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | 706 | /* Read first EDID block */ |
669 | drm_get_connector_name(connector)); | 707 | ret = drm_ddc_read_edid(connector, adapter, |
670 | kfree(edid); | 708 | (unsigned char *)edid, EDID_LENGTH); |
671 | return NULL; | 709 | if (ret != 0) |
710 | goto clean_up; | ||
711 | |||
712 | /* There are EDID extensions to be read */ | ||
713 | if (edid->extensions != 0) { | ||
714 | int edid_ext_num = edid->extensions; | ||
715 | |||
716 | if (edid_ext_num > MAX_EDID_EXT_NUM) { | ||
717 | dev_warn(&connector->dev->pdev->dev, | ||
718 | "The number of extension(%d) is " | ||
719 | "over max (%d), actually read number (%d)\n", | ||
720 | edid_ext_num, MAX_EDID_EXT_NUM, | ||
721 | MAX_EDID_EXT_NUM); | ||
722 | /* Reset EDID extension number to be read */ | ||
723 | edid_ext_num = MAX_EDID_EXT_NUM; | ||
724 | } | ||
725 | /* Read EDID including extensions too */ | ||
726 | ret = drm_ddc_read_edid(connector, adapter, (char *)edid, | ||
727 | EDID_LENGTH * (edid_ext_num + 1)); | ||
728 | if (ret != 0) | ||
729 | goto clean_up; | ||
730 | |||
672 | } | 731 | } |
673 | 732 | ||
674 | connector->display_info.raw_edid = (char *)edid; | 733 | connector->display_info.raw_edid = (char *)edid; |
734 | goto end; | ||
675 | 735 | ||
736 | clean_up: | ||
737 | kfree(edid); | ||
738 | edid = NULL; | ||
739 | end: | ||
676 | return edid; | 740 | return edid; |
741 | |||
677 | } | 742 | } |
678 | EXPORT_SYMBOL(drm_get_edid); | 743 | EXPORT_SYMBOL(drm_get_edid); |
679 | 744 | ||
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 33ae98ced80e..9022b2468182 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -613,7 +613,8 @@ extern void drm_fb_release(struct drm_file *file_priv); | |||
613 | extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); | 613 | extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); |
614 | extern struct edid *drm_get_edid(struct drm_connector *connector, | 614 | extern struct edid *drm_get_edid(struct drm_connector *connector, |
615 | struct i2c_adapter *adapter); | 615 | struct i2c_adapter *adapter); |
616 | extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter); | 616 | extern int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, |
617 | unsigned char *buf, int len); | ||
617 | extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); | 618 | extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); |
618 | extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); | 619 | extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); |
619 | extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); | 620 | extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); |