drivers: video: tegra: Add full range HDMI support
Aly Hirani [Thu, 23 Apr 2015 23:41:15 +0000 (16:41 -0700)]
HDMI sinks by default are only expected to support limited range
which restricts each channel to 16-235, rather than 0-255. The spec also
optionally allows sinks to support full range by declaring a
"quantization selectable" bit in the VCDB block in the EDID.

If the quantiazation selectable is marked as true (independent bools for
RGB/YUV), then the source is allowed to select full range by setting the
RGB/YUV limited/full range bits in the avi infoframe.

This patch adds:
1. A new VMODE flag to indicate that the specific mode is limited range
2. A new FB_CAP_* to indicate that the display supports overriding
RGB/YUV and the associated EDID parsing.
3. Applies the new VMODE LIMITED RANGE to the RGB/YUV modes based
on whether the TV supports overriding the quantization in that color
space as the default.
4. Adds the logic in the hdmi modeset to enable CMU iff we are in RGB
and limited range.
5. Adds the logic in the hdmi driver to set the avi infoframe based on
whether the current mode has the limited range flag set or not.

Bug 1611691

Change-Id: I9c42fea51211c6ed71945a17fe8f1353811951d9
Signed-off-by: Aly Hirani <ahirani@nvidia.com>
Reviewed-on: http://git-master/r/937115
GVS: Gerrit_Virtual_Submit
Reviewed-by: Jon Mayo <jmayo@nvidia.com>

drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/edid.c
drivers/video/tegra/dc/edid.h
drivers/video/tegra/dc/hdmi2.0.c
drivers/video/tegra/fb.c
include/uapi/linux/fb.h

index 8e41cef..5e71431 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (c) 2010-2015, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION, All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -2191,6 +2191,14 @@ static void _tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
        }
 }
 
+void _tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable)
+{
+       dc->cmu_enabled = cmu_enable;
+       _tegra_dc_update_cmu(dc, tegra_dc_get_cmu(dc));
+       tegra_dc_set_color_control(dc);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+}
+
 int tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 {
        mutex_lock(&dc->lock);
@@ -2276,6 +2284,7 @@ EXPORT_SYMBOL(tegra_dc_update_cmu_aligned);
 #define tegra_dc_cache_cmu(dc, src_cmu)
 #define tegra_dc_set_cmu(dc, cmu)
 #define tegra_dc_update_cmu(dc, cmu)
+#define _tegra_dc_enable_cmu(dc, cmu)
 #define tegra_dc_update_cmu_aligned(dc, cmu)
 #endif
 
@@ -2516,7 +2525,16 @@ static struct tegra_dc_mode *tegra_dc_get_override_mode(struct tegra_dc *dc)
                 */
                refresh = tegra_dc_calc_refresh(mode);
                if (refresh % 1000)
-                       mode->vmode = FB_VMODE_1000DIV1001;
+                       mode->vmode |= FB_VMODE_1000DIV1001;
+
+               /*
+                * Implicit contract between BL and us. If CMU is enabled,
+                * assume limited range. This sort of works because we know
+                * BL doesn't support YUV
+                */
+               val = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
+               if (val & CMU_ENABLE)
+                       mode->vmode |= FB_VMODE_LIMITED_RANGE;
 
                tegra_dc_put(dc);
        }
index 91ecd01..4f22153 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (c) 2010-2015, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION, All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -567,6 +567,7 @@ void tegra_dc_trigger_windows(struct tegra_dc *dc);
 void tegra_dc_set_color_control(struct tegra_dc *dc);
 #if defined(CONFIG_TEGRA_DC_CMU) || defined(CONFIG_TEGRA_DC_CMU_V2)
 void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable);
+void _tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable);
 #endif
 
 #ifdef CONFIG_TEGRA_DC_CMU
index 1f3ad9a..86c603e 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (c) 2010-2015, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION, All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -38,6 +38,8 @@ struct tegra_edid_pvt {
        bool                            scdc_present;
        bool                            db420_present;
        bool                            hfvsdb_present;
+       bool                            rgb_quant_selectable;
+       bool                            yuv_quant_selectable;
        int                             hdmi_vic_len;
        u8                              hdmi_vic[7];
        u16                     color_depth_flag;
@@ -451,6 +453,10 @@ static int tegra_edid_parse_ext_block(const u8 *raw, int idx,
                        u8 ext_db = ptr[1];
 
                        switch (ext_db) {
+                       case CEA_DATA_BLOCK_EXT_VCDB:
+                               edid->rgb_quant_selectable = ptr[2] & 0x40;
+                               edid->yuv_quant_selectable = ptr[2] & 0x80;
+                               break;
                        case CEA_DATA_BLOCK_EXT_Y420VDB: /* fall through */
                        case CEA_DATA_BLOCK_EXT_Y420CMDB:
                                edid->db420_present = true;
@@ -538,6 +544,24 @@ u16 tegra_edid_get_ex_hdr_cap(struct tegra_edid *edid)
        return ret;
 }
 
+u16 tegra_edid_get_quant_cap(struct tegra_edid *edid)
+{
+       u16 ret = 0;
+
+       if (!edid || !edid->data) {
+               pr_warn("edid invalid\n");
+               return 0;
+       }
+
+       if (edid->data->rgb_quant_selectable)
+               ret |= FB_CAP_RGB_QUANT_SELECTABLE;
+
+       if (edid->data->yuv_quant_selectable)
+               ret |= FB_CAP_YUV_QUANT_SELECTABLE;
+
+       return ret;
+}
+
 /* hdmi spec mandates sink to specify correct max_tmds_clk only for >165MHz */
 u16 tegra_edid_get_max_clk_rate(struct tegra_edid *edid)
 {
@@ -804,6 +828,17 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
                }
        }
 #endif
+
+       for (j = 0; j < specs->modedb_len; j++) {
+               if (!new_data->rgb_quant_selectable &&
+                   !(specs->modedb[j].vmode & FB_VMODE_SET_YUV_MASK))
+                       specs->modedb[j].vmode |= FB_VMODE_LIMITED_RANGE;
+
+               if (!new_data->yuv_quant_selectable &&
+                   (specs->modedb[j].vmode & FB_VMODE_SET_YUV_MASK))
+                       specs->modedb[j].vmode |= FB_VMODE_LIMITED_RANGE;
+       }
+
        if (use_fallback)
                edid->errors |= EDID_ERRORS_USING_FALLBACK;
 
index dd39367..7a89fca 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (c) 2011-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2011-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -154,6 +154,7 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid,
                                struct fb_monspecs *specs);
 u16 tegra_edid_get_cd_flag(struct tegra_edid *edid);
 u16 tegra_edid_get_ex_hdr_cap(struct tegra_edid *edid);
+u16 tegra_edid_get_quant_cap(struct tegra_edid *edid);
 u16 tegra_edid_get_max_clk_rate(struct tegra_edid *edid);
 bool tegra_edid_is_scdc_present(struct tegra_edid *edid);
 bool tegra_edid_is_420db_present(struct tegra_edid *edid);
index ce39b43..fad9c22 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/video/tegra/dc/hdmi2.0.c
  *
- * Copyright (c) 2014-2015, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION, All rights reserved.
  * Author: Animesh Kishore <ankishore@nvidia.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -1266,6 +1266,36 @@ static u32 tegra_hdmi_get_ex_colorimetry(struct tegra_hdmi *hdmi)
                HDMI_AVI_EXT_COLORIMETRY_INVALID;
 }
 
+static u32 tegra_hdmi_get_rgb_quant(struct tegra_hdmi *hdmi)
+{
+       u32 vmode = hdmi->dc->mode.vmode;
+
+       if (tegra_edid_get_quant_cap(hdmi->edid) & FB_CAP_RGB_QUANT_SELECTABLE)
+               return vmode & FB_VMODE_LIMITED_RANGE ?
+                       HDMI_AVI_RGB_QUANT_LIMITED : HDMI_AVI_RGB_QUANT_FULL;
+       else
+               /*
+                * The safest way to break the HDMI spec when forcing full range
+                * on a limited system: send full data with the QUANT_DEFAULT
+                * */
+               return HDMI_AVI_RGB_QUANT_DEFAULT;
+}
+
+static u32 tegra_hdmi_get_ycc_quant(struct tegra_hdmi *hdmi)
+{
+       u32 vmode = hdmi->dc->mode.vmode;
+
+       if (tegra_edid_get_quant_cap(hdmi->edid) & FB_CAP_YUV_QUANT_SELECTABLE)
+               return vmode & FB_VMODE_LIMITED_RANGE ?
+                       HDMI_AVI_YCC_QUANT_LIMITED : HDMI_AVI_YCC_QUANT_FULL;
+       else
+               /*
+                * The safest way to break the HDMI spec when forcing full range
+                * on a limited system: send full data with the QUANT_DEFAULT
+                * */
+               return HDMI_AVI_YCC_QUANT_NONE;
+}
+
 static void tegra_hdmi_avi_infoframe_update(struct tegra_hdmi *hdmi)
 {
        struct hdmi_avi_infoframe *avi = &hdmi->avi;
@@ -1287,7 +1317,7 @@ static void tegra_hdmi_avi_infoframe_update(struct tegra_hdmi *hdmi)
                        HDMI_AVI_COLORIMETRY_DEFAULT;
 
        avi->scaling = HDMI_AVI_SCALING_UNKNOWN;
-       avi->rgb_quant = HDMI_AVI_RGB_QUANT_DEFAULT;
+       avi->rgb_quant = tegra_hdmi_get_rgb_quant(hdmi);
        avi->ext_colorimetry = tegra_hdmi_get_ex_colorimetry(hdmi);
        avi->it_content = HDMI_AVI_IT_CONTENT_FALSE;
 
@@ -1296,7 +1326,7 @@ static void tegra_hdmi_avi_infoframe_update(struct tegra_hdmi *hdmi)
 
        avi->pix_rep = HDMI_AVI_NO_PIX_REPEAT;
        avi->it_content_type = HDMI_AVI_IT_CONTENT_NONE;
-       avi->ycc_quant = HDMI_AVI_YCC_QUANT_NONE;
+       avi->ycc_quant = tegra_hdmi_get_ycc_quant(hdmi);
 
        avi->top_bar_end_line_low_byte = 0;
        avi->top_bar_end_line_high_byte = 0;
@@ -2153,6 +2183,10 @@ static void tegra_dc_hdmi_modeset_notifier(struct tegra_dc *dc)
                tegra_hdmi_v2_x_host_config(hdmi, false);
        }
 
+       if (!(dc->mode.vmode & FB_VMODE_SET_YUV_MASK))
+               _tegra_dc_cmu_enable(dc,
+                       dc->mode.vmode & FB_VMODE_LIMITED_RANGE);
+
        tegra_dc_io_end(dc);
        tegra_hdmi_put(dc);
 }
index a36f0aa..144e3ce 100644 (file)
@@ -6,7 +6,7 @@
  *         Colin Cross <ccross@android.com>
  *         Travis Geiselbrecht <travis@palm.com>
  *
- * Copyright (c) 2010-2015, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION, All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -712,7 +712,8 @@ void tegra_fb_update_fix(struct tegra_fb_info *fb_info,
        mutex_lock(&fb_info->info->lock);
 
        fix->capabilities = ((tegra_edid_get_cd_flag(dc_edid) <<
-                       FB_CAP_FOURCC) | tegra_edid_get_ex_hdr_cap(dc_edid))
+                       FB_CAP_FOURCC) | tegra_edid_get_ex_hdr_cap(dc_edid) |
+                       tegra_edid_get_quant_cap(dc_edid))
                        & FB_CAP_DC_MASK;
 
        fix->max_clk_rate = tegra_edid_get_max_clk_rate(dc_edid);
index a1a1866..e427051 100644 (file)
 #define FB_CAP_Y420_DC_36      4       /* YCbCr 4:2:0 deep color 36bpp */
 #define FB_CAP_Y420_DC_48      8       /* YCbCr 4:2:0 deep color 48bpp */
 #define FB_CAP_HDR             16      /* Device supports HDR*/
+/* Device supports overriding range for RGB modes */
+#define FB_CAP_RGB_QUANT_SELECTABLE            32
+/* Device supports overriding range for YUV modes */
+#define FB_CAP_YUV_QUANT_SELECTABLE            64
 #define FB_CAP_DC_MASK         (FB_CAP_Y420_DC_30 | \
                                FB_CAP_Y420_DC_36 | FB_CAP_Y420_DC_48 | \
-                               FB_CAP_HDR)
+                               FB_CAP_HDR | FB_CAP_RGB_QUANT_SELECTABLE | \
+                               FB_CAP_YUV_QUANT_SELECTABLE)
 
 #define FB_COL_XVYCC601                0x1
 #define FB_COL_XVYCC709                0x2
@@ -275,8 +280,9 @@ struct fb_bitfield {
 #define FB_VMODE_IS_DETAILED   0x080000
 #define FB_VMODE_IS_CEA                0x100000
 #define FB_VMODE_IS_HDMI_EXT   0x200000
+#define FB_VMODE_LIMITED_RANGE 0x400000
 
-#define FB_VMODE_MASK          0x38ffff
+#define FB_VMODE_MASK          0x78ffff
 
 #define FB_VMODE_YWRAP         0x10000 /* ywrap instead of panning     */
 #define FB_VMODE_SMOOTH_XPAN   0x20000 /* smooth xpan possible (internally used) */