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 | |
| 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>
| -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 |
