From patchwork Mon Aug 19 15:58:54 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Lespiau, Damien" X-Patchwork-Id: 2846572 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 953F19F2F4 for ; Mon, 19 Aug 2013 16:08:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4C71520379 for ; Mon, 19 Aug 2013 16:08:41 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id B212120371 for ; Mon, 19 Aug 2013 16:08:39 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 93369E72B6 for ; Mon, 19 Aug 2013 09:08:39 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga14.intel.com (mga14.intel.com [143.182.124.37]) by gabe.freedesktop.org (Postfix) with ESMTP id 075D6E723E; Mon, 19 Aug 2013 08:59:19 -0700 (PDT) Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga102.ch.intel.com with ESMTP; 19 Aug 2013 08:59:19 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.89,914,1367996400"; d="scan'208";a="348428935" Received: from unknown (HELO strange.amr.corp.intel.com) ([10.255.14.123]) by azsmga001.ch.intel.com with ESMTP; 19 Aug 2013 08:59:17 -0700 From: Damien Lespiau To: dri-devel@lists.freedesktop.org Date: Mon, 19 Aug 2013 16:58:54 +0100 Message-Id: <1376927945-26635-4-git-send-email-damien.lespiau@intel.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1376927945-26635-1-git-send-email-damien.lespiau@intel.com> References: <1376927945-26635-1-git-send-email-damien.lespiau@intel.com> MIME-Version: 1.0 Cc: intel-gfx@lists.freedesktop.org Subject: [Intel-gfx] [PATCH 03/14] drm/edid: Parse the HDMI CEA block and look for 4k modes X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Tested-by: Cancan Feng Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=67030 Reviewed-by: Simon Farnsworth Reviewed-by: Ville Syrjälä --- drivers/gpu/drm/drm_edid.c | 124 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bb25ee2..9de573c 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[] = { .vrefresh = 100, }, }; +/* + * HDMI 1.4 4k modes. + */ +static const struct drm_display_mode edid_4k_modes[] = { + /* 1 - 3840x2160@30Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 4016, 4104, 4400, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, }, + /* 2 - 3840x2160@25Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 4896, 4984, 5280, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, }, + /* 3 - 3840x2160@24Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 5116, 5204, 5500, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, }, + /* 4 - 4096x2160@24Hz (SMPTE) */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, + 4096, 5116, 5204, 5500, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, }, +}; + /*** DDC fetch and block validation ***/ static const u8 edid_header[] = { @@ -2465,6 +2495,68 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) return modes; } +/* + * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block + * @connector: connector corresponding to the HDMI sink + * @db: start of the CEA vendor specific block + * @len: length of the CEA block payload, ie. one can access up to db[len] + * + * Parses the HDMI VSDB looking for modes to add to @connector. + */ +static int +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) +{ + struct drm_device *dev = connector->dev; + int modes = 0, offset = 0, i; + u8 vic_len; + + if (len < 8) + goto out; + + /* no HDMI_Video_Present */ + if (!(db[8] & (1 << 5))) + goto out; + + /* Latency_Fields_Present */ + if (db[8] & (1 << 7)) + offset += 2; + + /* I_Latency_Fields_Present */ + if (db[8] & (1 << 6)) + offset += 2; + + /* the declared length is not long enough for the 2 first bytes + * of additional video format capabilities */ + offset += 2; + if (len < (8 + offset)) + goto out; + + vic_len = db[8 + offset] >> 5; + + for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { + struct drm_display_mode *newmode; + u8 vic; + + vic = db[9 + offset + i]; + + vic--; /* VICs start at 1 */ + if (vic >= ARRAY_SIZE(edid_4k_modes)) { + DRM_ERROR("Unknown HDMI VIC: %d\n", vic); + continue; + } + + newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); + if (!newmode) + continue; + + drm_mode_probed_add(connector, newmode); + modes++; + } + +out: + return modes; +} + static int cea_db_payload_len(const u8 *db) { @@ -2496,6 +2588,21 @@ cea_db_offsets(const u8 *cea, int *start, int *end) return 0; } +static bool cea_db_is_hdmi_vsdb(const u8 *db) +{ + int hdmi_id; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 5) + return false; + + hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); + + return hdmi_id == HDMI_IDENTIFIER; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) @@ -2519,6 +2626,8 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid) if (cea_db_tag(db) == VIDEO_BLOCK) modes += do_cea_modes(connector, db + 1, dbl); + else if (cea_db_is_hdmi_vsdb(db)) + modes += do_hdmi_vsdb_modes(connector, db, dbl); } } @@ -2571,21 +2680,6 @@ monitor_name(struct detailed_timing *t, void *data) *(u8 **)data = t->data.other_data.data.str.str; } -static bool cea_db_is_hdmi_vsdb(const u8 *db) -{ - int hdmi_id; - - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; - - if (cea_db_payload_len(db) < 5) - return false; - - hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); - - return hdmi_id == HDMI_IDENTIFIER; -} - /** * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink