diff options
author | Lespiau, Damien <damien.lespiau@intel.com> | 2013-08-19 11:58:54 -0400 |
---|---|---|
committer | Dave Airlie <airlied@gmail.com> | 2013-08-29 18:40:06 -0400 |
commit | 7ebe1963a063daf30f95752c35244c5d49550aa9 (patch) | |
tree | 8e74c2a950e3858c0a21c80acb30bd080150596c /drivers/gpu/drm/drm_edid.c | |
parent | 13ac3f5593cf0964cdb239864829e57cc6981dac (diff) |
drm/edid: Parse the HDMI CEA block and look for 4k modes
HDMI 1.4 adds 4 "4k x 2k" modes in the the CEA vendor specific block.
With this commit, we now parse this block and expose the 4k modes that
we find there.
v2: Fix the "4096x2160" string (nice catch!), add comments about
do_hdmi_vsdb_modes() arguments and make it clearer that offset is
relative to the end of the required fields of the HDMI VSDB
(Ville Syrjälä)
v3: Fix 'Unknow' typo (Simon Farnsworth)
Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
Tested-by: Cancan Feng <cancan.feng@intel.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=67030
Reviewed-by: Simon Farnsworth <simon.farnsworth@onelan.co.uk>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Dave Airlie <airlied@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 124 |
1 files changed, 109 insertions, 15 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bb25ee2f9f25..9de573cd3683 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -931,6 +931,36 @@ static const struct drm_display_mode edid_cea_modes[] = { | |||
931 | .vrefresh = 100, }, | 931 | .vrefresh = 100, }, |
932 | }; | 932 | }; |
933 | 933 | ||
934 | /* | ||
935 | * HDMI 1.4 4k modes. | ||
936 | */ | ||
937 | static const struct drm_display_mode edid_4k_modes[] = { | ||
938 | /* 1 - 3840x2160@30Hz */ | ||
939 | { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, | ||
940 | 3840, 4016, 4104, 4400, 0, | ||
941 | 2160, 2168, 2178, 2250, 0, | ||
942 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
943 | .vrefresh = 30, }, | ||
944 | /* 2 - 3840x2160@25Hz */ | ||
945 | { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, | ||
946 | 3840, 4896, 4984, 5280, 0, | ||
947 | 2160, 2168, 2178, 2250, 0, | ||
948 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
949 | .vrefresh = 25, }, | ||
950 | /* 3 - 3840x2160@24Hz */ | ||
951 | { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, | ||
952 | 3840, 5116, 5204, 5500, 0, | ||
953 | 2160, 2168, 2178, 2250, 0, | ||
954 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
955 | .vrefresh = 24, }, | ||
956 | /* 4 - 4096x2160@24Hz (SMPTE) */ | ||
957 | { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, | ||
958 | 4096, 5116, 5204, 5500, 0, | ||
959 | 2160, 2168, 2178, 2250, 0, | ||
960 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
961 | .vrefresh = 24, }, | ||
962 | }; | ||
963 | |||
934 | /*** DDC fetch and block validation ***/ | 964 | /*** DDC fetch and block validation ***/ |
935 | 965 | ||
936 | static const u8 edid_header[] = { | 966 | static const u8 edid_header[] = { |
@@ -2465,6 +2495,68 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) | |||
2465 | return modes; | 2495 | return modes; |
2466 | } | 2496 | } |
2467 | 2497 | ||
2498 | /* | ||
2499 | * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block | ||
2500 | * @connector: connector corresponding to the HDMI sink | ||
2501 | * @db: start of the CEA vendor specific block | ||
2502 | * @len: length of the CEA block payload, ie. one can access up to db[len] | ||
2503 | * | ||
2504 | * Parses the HDMI VSDB looking for modes to add to @connector. | ||
2505 | */ | ||
2506 | static int | ||
2507 | do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) | ||
2508 | { | ||
2509 | struct drm_device *dev = connector->dev; | ||
2510 | int modes = 0, offset = 0, i; | ||
2511 | u8 vic_len; | ||
2512 | |||
2513 | if (len < 8) | ||
2514 | goto out; | ||
2515 | |||
2516 | /* no HDMI_Video_Present */ | ||
2517 | if (!(db[8] & (1 << 5))) | ||
2518 | goto out; | ||
2519 | |||
2520 | /* Latency_Fields_Present */ | ||
2521 | if (db[8] & (1 << 7)) | ||
2522 | offset += 2; | ||
2523 | |||
2524 | /* I_Latency_Fields_Present */ | ||
2525 | if (db[8] & (1 << 6)) | ||
2526 | offset += 2; | ||
2527 | |||
2528 | /* the declared length is not long enough for the 2 first bytes | ||
2529 | * of additional video format capabilities */ | ||
2530 | offset += 2; | ||
2531 | if (len < (8 + offset)) | ||
2532 | goto out; | ||
2533 | |||
2534 | vic_len = db[8 + offset] >> 5; | ||
2535 | |||
2536 | for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { | ||
2537 | struct drm_display_mode *newmode; | ||
2538 | u8 vic; | ||
2539 | |||
2540 | vic = db[9 + offset + i]; | ||
2541 | |||
2542 | vic--; /* VICs start at 1 */ | ||
2543 | if (vic >= ARRAY_SIZE(edid_4k_modes)) { | ||
2544 | DRM_ERROR("Unknown HDMI VIC: %d\n", vic); | ||
2545 | continue; | ||
2546 | } | ||
2547 | |||
2548 | newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); | ||
2549 | if (!newmode) | ||
2550 | continue; | ||
2551 | |||
2552 | drm_mode_probed_add(connector, newmode); | ||
2553 | modes++; | ||
2554 | } | ||
2555 | |||
2556 | out: | ||
2557 | return modes; | ||
2558 | } | ||
2559 | |||
2468 | static int | 2560 | static int |
2469 | cea_db_payload_len(const u8 *db) | 2561 | cea_db_payload_len(const u8 *db) |
2470 | { | 2562 | { |
@@ -2496,6 +2588,21 @@ cea_db_offsets(const u8 *cea, int *start, int *end) | |||
2496 | return 0; | 2588 | return 0; |
2497 | } | 2589 | } |
2498 | 2590 | ||
2591 | static bool cea_db_is_hdmi_vsdb(const u8 *db) | ||
2592 | { | ||
2593 | int hdmi_id; | ||
2594 | |||
2595 | if (cea_db_tag(db) != VENDOR_BLOCK) | ||
2596 | return false; | ||
2597 | |||
2598 | if (cea_db_payload_len(db) < 5) | ||
2599 | return false; | ||
2600 | |||
2601 | hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); | ||
2602 | |||
2603 | return hdmi_id == HDMI_IDENTIFIER; | ||
2604 | } | ||
2605 | |||
2499 | #define for_each_cea_db(cea, i, start, end) \ | 2606 | #define for_each_cea_db(cea, i, start, end) \ |
2500 | for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) | 2607 | for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) |
2501 | 2608 | ||
@@ -2519,6 +2626,8 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid) | |||
2519 | 2626 | ||
2520 | if (cea_db_tag(db) == VIDEO_BLOCK) | 2627 | if (cea_db_tag(db) == VIDEO_BLOCK) |
2521 | modes += do_cea_modes(connector, db + 1, dbl); | 2628 | modes += do_cea_modes(connector, db + 1, dbl); |
2629 | else if (cea_db_is_hdmi_vsdb(db)) | ||
2630 | modes += do_hdmi_vsdb_modes(connector, db, dbl); | ||
2522 | } | 2631 | } |
2523 | } | 2632 | } |
2524 | 2633 | ||
@@ -2571,21 +2680,6 @@ monitor_name(struct detailed_timing *t, void *data) | |||
2571 | *(u8 **)data = t->data.other_data.data.str.str; | 2680 | *(u8 **)data = t->data.other_data.data.str.str; |
2572 | } | 2681 | } |
2573 | 2682 | ||
2574 | static bool cea_db_is_hdmi_vsdb(const u8 *db) | ||
2575 | { | ||
2576 | int hdmi_id; | ||
2577 | |||
2578 | if (cea_db_tag(db) != VENDOR_BLOCK) | ||
2579 | return false; | ||
2580 | |||
2581 | if (cea_db_payload_len(db) < 5) | ||
2582 | return false; | ||
2583 | |||
2584 | hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); | ||
2585 | |||
2586 | return hdmi_id == HDMI_IDENTIFIER; | ||
2587 | } | ||
2588 | |||
2589 | /** | 2683 | /** |
2590 | * drm_edid_to_eld - build ELD from EDID | 2684 | * drm_edid_to_eld - build ELD from EDID |
2591 | * @connector: connector corresponding to the HDMI/DP sink | 2685 | * @connector: connector corresponding to the HDMI/DP sink |