From patchwork Tue Jul 4 14:11:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sharma, Shashank" X-Patchwork-Id: 9825117 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 73F8C603B5 for ; Tue, 4 Jul 2017 14:09:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 63FCE27F88 for ; Tue, 4 Jul 2017 14:09:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 58DAD280B0; Tue, 4 Jul 2017 14:09:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 768E627F88 for ; Tue, 4 Jul 2017 14:09:55 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 378946E356; Tue, 4 Jul 2017 14:09:22 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by gabe.freedesktop.org (Postfix) with ESMTPS id 441A46E330; Tue, 4 Jul 2017 14:09:20 +0000 (UTC) Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Jul 2017 07:09:08 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.40,307,1496127600"; d="scan'208";a="120727560" Received: from shashanks-linuxbox.iind.intel.com ([10.223.161.29]) by orsmga005.jf.intel.com with ESMTP; 04 Jul 2017 07:09:06 -0700 From: Shashank Sharma To: dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org Date: Tue, 4 Jul 2017 19:41:58 +0530 Message-Id: <1499177524-26292-12-git-send-email-shashank.sharma@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1499177524-26292-1-git-send-email-shashank.sharma@intel.com> References: <1499177524-26292-1-git-send-email-shashank.sharma@intel.com> Cc: Jose Abreu , Daniel Vetter Subject: [Intel-gfx] [PATCH v5 11/17] drm: add helper functions for YCBCR output handling X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP This patch adds set of helper functions for YCBCR HDMI output handling. These functions provide functionality like: - check if a given video mode is YCBCR 420 only mode. - check if a given video mode is YCBCR 420 mode. - get the highest subsamled ycbcr output. - get the lowest subsamled ycbcr output. - check if a given source and sink combination can support any YCBCR HDMI output. - check if a given source and sink combination can support a particular YCBCR HDMI output. Currently all these helpers are kept static, and only one wrapper function is exposed to callers, which is "drm_find_hdmi_output_type". This function takes inputs as current mode, user preference and src + sink capabilities, and provides the most suitable HDMI output format to match all of these inputs. This wrapper function will be used in next few patches in this series from I915 driver. V2: Added YCBCR functions as helpers in DRM layer, instead of keeping it in I915 layer. V3: Added handling for YCBCR-420 only modes too. V4: EXPORT_SYMBOL(drm_find_hdmi_output_type) V5: Addressed review comments from Danvet: - %s/drm_find_hdmi_output_type/drm_display_info_hdmi_output_type - %s/drm_can_support_ycbcr_output/drm_display_supports_ycbcr_output - %s/drm_can_support_this_ycbcr_output/ drm_display_supports_this_ycbcr_output - pass drm_display_info instead of drm_connector for consistency - For drm_get_highest_quality_ycbcr_supported doc, move the variable description above, and then the function description. Cc: Ville Syrjala Cc: Jose Abreu Cc: Daniel Vetter Signed-off-by: Shashank Sharma --- drivers/gpu/drm/drm_modes.c | 307 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_modes.h | 5 + 2 files changed, 312 insertions(+) diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3b53c8e3..0c42bbb 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1604,3 +1604,310 @@ int drm_mode_convert_umode(struct drm_display_mode *out, out: return ret; } + +/** + * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 + * output format + * + * @connector: drm connector under action. + * @mode: video mode to be tested. + * + * Returns: + * true if the mode can support YCBCR420 output + * false if not. + */ +static bool drm_mode_is_420_only(struct drm_display_info *display, + struct drm_display_mode *mode) +{ + u8 vic = drm_match_cea_mode(mode); + + /* + * Requirements of a 420_only mode: + * must be a valid cea mode + * entry in 420_only bitmap + */ + if (!drm_valid_cea_vic(vic)) + return false; + + return test_bit(vic, display->hdmi.y420_vdb_modes); +} + +/** + * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 + * output format also (along with RGB/YCBCR444/422) + * + * @display: display under action. + * @mode: video mode to be tested. + * + * Returns: + * true if the mode can support YCBCR420 output + * false if not. + */ +static bool drm_mode_is_420_also(struct drm_display_info *display, + struct drm_display_mode *mode) +{ + u8 vic = drm_match_cea_mode(mode); + + /* + * Requirements of a 420_also mode: + * must be a valid cea mode + * entry in 420_also bitmap + */ + if (!drm_valid_cea_vic(vic)) + return false; + + return test_bit(vic, display->hdmi.y420_cmdb_modes); +} + + +static bool drm_mode_is_420(struct drm_display_info *display, + struct drm_display_mode *mode) +{ + return drm_mode_is_420_only(display, mode) || + drm_mode_is_420_also(display, mode); +} + +/** + * drm_display_supports_this_ycbcr_output - can a given source and sink + * combination support a particular YCBCR output type. + * + * @display: sink information. + * @mode: video mode from modeset + * @type: enum indicating YCBCR output type + * @source_outputs: bitmap of source supported HDMI output formats. + * + * Returns: + * true on success. + * false on failure. + */ +static bool +drm_display_supports_this_ycbcr_output(struct drm_display_info *display, + struct drm_display_mode *mode, + enum drm_hdmi_output_type type, + u32 source_outputs) +{ + /* YCBCR420 output support can be per mode basis */ + if (type == DRM_HDMI_OUTPUT_YCBCR420 && + !drm_mode_is_420(display, mode)) + return false; + + return display->color_formats & source_outputs & type; +} + +/** + * drm_display_supports_ycbcr_output - can a given source and sink combination + * support any YCBCR outputs ? + * + * @display: sink information. + * @source_outputs: bitmap of source supported HDMI output formats. + * + * Returns: + * true on success. + * false on failure. + */ +static bool drm_display_supports_ycbcr_output(struct drm_display_info *display, + u32 source_outputs) +{ + u32 supported_formats; + + if (!source_outputs || !display->color_formats) { + DRM_DEBUG_KMS("Source/Sink doesn't support any output ?\n"); + return DRM_HDMI_OUTPUT_INVALID; + } + + /* Get the common supported fromats between source and sink */ + supported_formats = display->color_formats & source_outputs; + if (!supported_formats || (supported_formats == + DRM_COLOR_FORMAT_RGB444)) { + DRM_ERROR("No common YCBCR formats between source and sink\n"); + return false; + } + + DRM_DEBUG_KMS("Src and Sink combination can support YCBCR output\n"); + return true; +} + +/** + * drm_get_highest_quality_ycbcr_supported - get the ycbcr output mode + * with highest subsampling rate + * @display: struct drm_display_info from current connector + * @mode: video mode from modeset + * @source_output_map: bitmap of supported HDMI output modes from source + * + * Finds the best possible ycbcr output mode (based on subsampling), for the + * given source and sink combination. + * + * Returns: + * enum corresponding to best output mode on success. + * DRM_HDMI_OUTPUT_INVALID on failure. + */ +static enum drm_hdmi_output_type +drm_get_highest_quality_ycbcr_supported(struct drm_display_info *display, + struct drm_display_mode *mode, + u32 source_output_map) +{ + enum drm_hdmi_output_type output = DRM_HDMI_OUTPUT_INVALID; + u32 supported_formats = source_output_map & display->color_formats; + + /* + * Get the ycbcr output with the highest possible subsampling rate. + * Preference should go as: + * ycbcr 444 + * ycbcr 422 + * ycbcr 420 + */ + if (supported_formats & DRM_COLOR_FORMAT_YCRCB444) + output = DRM_COLOR_FORMAT_YCRCB444; + else if (supported_formats & DRM_COLOR_FORMAT_YCRCB422) + output = DRM_COLOR_FORMAT_YCRCB422; + else if (supported_formats & DRM_COLOR_FORMAT_YCRCB420 && + drm_mode_is_420(display, mode)) + output = DRM_COLOR_FORMAT_YCRCB420; + + DRM_DEBUG_KMS("Highest subsampled YCBCR mode supported is %s\n", + drm_get_hdmi_output_name(supported_formats)); + return output; +} + +/** + * drm_get_lowest_quality_ycbcr_supported - get the ycbcr output mode + * with lowest subsampling rate + * @display: struct drm_display_info from current connector + * @mode: video mode from modeset + * @source_output_map: bitmap of supported HDMI output modes from source + * + * Finds the lowest possible ycbcr output mode (based on subsampling), for the + * given source and sink combination. + * + * Returns: + * enum corresponding to best output mode on success. + * DRM_HDMI_OUTPUT_INVALID on failure. + */ +static enum drm_hdmi_output_type +drm_get_lowest_quality_ycbcr_supported(struct drm_display_info *display, + struct drm_display_mode *mode, + u32 source_output_map) +{ + enum drm_hdmi_output_type output = DRM_HDMI_OUTPUT_INVALID; + u32 supported_formats = source_output_map & display->color_formats; + + /* + * Get the ycbcr output with the lowest possible subsampling rate. + * Preference should go as: + * ycbcr 420 + * ycbcr 422 + * ycbcr 444 + */ + if (supported_formats & DRM_COLOR_FORMAT_YCRCB420 && + drm_mode_is_420(display, mode)) + output = DRM_HDMI_OUTPUT_YCBCR420; + else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output = DRM_HDMI_OUTPUT_YCBCR422; + else if (display->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output = DRM_HDMI_OUTPUT_YCBCR420; + + DRM_DEBUG_KMS("Lowest subsampled YCBCR mode supported is %s\n", + drm_get_hdmi_output_name(supported_formats)); + return output; +} + +/** + * drm_find_hdmi_output_type - get the most suitable output + * @info: connected display's information from connector + * @mode: video mode under modeset + * @type: user's choice for preferred mode, set via drm property + * "hdmi_output_format" + * @src_output_cap: bitmap of source's supported outputs formats + * src_output_cap = (1 << DRM_COLOR_FORMAT_RGB444) means source + * supports RGB444 + * src_output_cap = (1 << DRM_COLOR_FORMAT_YCRCB444) means source + * supports YUV444, and so on. + * + * Find the best suitable HDMI output considering mode, source capability, + * sink capability and user's choice (expressed in form of drm property) + * + * Returns: + * enum corresponding to best suitable output type on success. + * DRM_HDMI_OUTPUT_INVALID on failure. + */ +enum drm_hdmi_output_type +drm_display_info_hdmi_output_type(struct drm_display_info *info, + struct drm_display_mode *mode, + enum drm_hdmi_output_type type, + u32 src_output_cap) +{ + bool ret; + bool mode_is_420_only = drm_mode_is_420_only(info, mode); + + /* + * If the preferred output is not set to YCBCR by user, and the mode + * doesn't force us to drive YCBCR420 output, respect the user + * preference for the output type. But if the mode is 420_only, we will + * be force to drive YCBCR420 output. + */ + if (!mode_is_420_only) { + if (type == DRM_HDMI_OUTPUT_DEFAULT_RGB) + return DRM_HDMI_OUTPUT_DEFAULT_RGB; + } else { + type = DRM_HDMI_OUTPUT_YCBCR420; + DRM_DEBUG_KMS("Got a 420 only mode(%s)\n", mode->name); + } + + /* If this src + sink combination can support any YCBCR output */ + ret = drm_display_supports_ycbcr_output(info, src_output_cap); + if (!ret) { + DRM_ERROR("No supported YCBCR output\n"); + return DRM_HDMI_OUTPUT_INVALID; + } + + switch (type) { + case DRM_HDMI_OUTPUT_YCBCR_HQ: + type = drm_get_highest_quality_ycbcr_supported(info, mode, + src_output_cap); + break; + + case DRM_HDMI_OUTPUT_YCBCR_LQ: + type = drm_get_lowest_quality_ycbcr_supported(info, mode, + src_output_cap); + break; + + case DRM_HDMI_OUTPUT_YCBCR420: + ret = drm_mode_is_420(info, mode); + if (!ret) { + DRM_ERROR("Mode %s doesn't support 420 output\n", + mode->name); + type = DRM_HDMI_OUTPUT_INVALID; + } + + break; + + /* Below cases are just to satisfy checkpatch's AI */ + case DRM_HDMI_OUTPUT_DEFAULT_RGB: + case DRM_HDMI_OUTPUT_YCBCR444: + case DRM_HDMI_OUTPUT_YCBCR422: + break; + + default: + type = DRM_HDMI_OUTPUT_INVALID; + } + + if (type == DRM_HDMI_OUTPUT_INVALID) { + DRM_ERROR("Can't support mode %s in YCBCR format\n", + mode->name); + return DRM_HDMI_OUTPUT_INVALID; + } + + /* Test if this src/sink/mode combination can support selected output */ + ret = drm_display_supports_this_ycbcr_output(info, mode, type, + src_output_cap); + if (!ret) { + DRM_ERROR("Output %s can't be supported\n", + drm_get_hdmi_output_name(type)); + return DRM_HDMI_OUTPUT_INVALID; + } + + DRM_DEBUG_KMS("Best supported output is: %s\n", + drm_get_hdmi_output_name(type)); + return type; +} +EXPORT_SYMBOL(drm_display_info_hdmi_output_type); diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index f8a1268..c5824aa 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -453,6 +453,11 @@ int drm_mode_convert_umode(struct drm_display_mode *out, void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); void drm_mode_debug_printmodeline(const struct drm_display_mode *mode); +enum drm_hdmi_output_type +drm_display_info_hdmi_output_type(struct drm_display_info *info, + struct drm_display_mode *mode, + enum drm_hdmi_output_type type, + u32 src_output_cap); struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool reduced, bool interlaced,