video: tegra: dc: HDMI SPD InfoFrame support
Ahung Cheng [Tue, 21 Jun 2016 10:39:53 +0000 (18:39 +0800)]
Add support to send SPD over Generic InfoFrame.

Please be noted that once SPD InfoFrame support is
enabled, HDR InfoFrame will be disabled as only one
of them can use Generic InfoFrame at one time.

To enable SPD InfoFrame, please add this in kernel config
 CONFIG_TEGRA_HDMI_SPD_INFOFRAME=y

And add source product description format as below under
hdmi-display in device tree
spd-infoframe {
vendor-name = "XXX";
product-description = "YYY";
source-information = <N>;
};

bug 200211468

Change-Id: Ifa8e1f54e1cb64fd507a483b24f8962a96be180e
Signed-off-by: Ahung Cheng <ahcheng@nvidia.com>
Reviewed-on: http://git-master/r/1168466
(cherry picked from commit 5604cf8cbc53f91b6945cb933526249113fcbbe8)
Signed-off-by: Ahung Cheng <ahcheng@nvidia.com>
Reviewed-on: http://git-master/r/1170967
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bibek Basu <bbasu@nvidia.com>

Documentation/devicetree/bindings/video/nvidia,tegra210-hdmi.txt
arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/Kconfig
drivers/video/tegra/dc/edid.c
drivers/video/tegra/dc/hdmi2.0.c
drivers/video/tegra/dc/hdmi2.0.h
drivers/video/tegra/dc/of_dc.c

index 90baba1..5f4aee8 100644 (file)
@@ -151,6 +151,17 @@ NVIDIA TEGRA210 High Definition Multimedia Interface
  - nvidia,cmu-csc: CMU color space conversion matrix. It is 3X3 matrix.
  - nvidia,cmu-lut2: CMU LUT2. Should be 960 u8 arrays.
 
+ 1.A.m) NVIDIA Source Product Description InfoFrame Settings
+ This must be contained in hdmi-display parent node. This is SPD InfoFrame settings.
+
+ Required properties:
+ - name: Should be "spd-infoframe".
+ - vendor-name: A string containing the name of the vendor. It consists of eight 7-bit ASCII
+   characters.
+ - product-description: A string containing the product description. It consists of siteen
+   7-bit ASCII characters.
+ - source-information: A code classifies the source.
+
  1.B) the prod-settings node:
  the prod-settings node holds prod settings for hdmi tmds.
  Optinonal properties:
@@ -193,6 +204,11 @@ Example
                                        nvidia,out-xres = <1920>;
                                        nvidia,out-yres = <1080>;
                                };
+                               spd-infoframe {
+                                       vendor-name = "NVIDIA";
+                                       product-description = "Jetson-CV TX1";
+                                       source-information = <1>;
+                               };
                        };
                        prod-settings {
                                #prod-cells = <3>
index a8e4dae..27bf001 100644 (file)
@@ -1174,10 +1174,17 @@ struct tmds_config {
        u32 pad_ctls0_setting; /* register OR mask */
 };
 
+struct spd_infoframe {
+       u8 vendor_name[8];
+       u8 prod_desc[16];
+       u8 source_information;
+};
+
 struct tegra_hdmi_out {
        struct tmds_config *tmds_config;
        int n_tmds_config;
        bool hdmi2fpd_bridge_enable;
+       struct spd_infoframe *spd_infoframe;
 };
 
 enum {
index 3f55757..2858bf5 100644 (file)
@@ -391,6 +391,16 @@ config TEGRA_HDMIHDCP
          option to simply display a GUI on an HDMI TV. An HDMI TV will display
          unprotected content just fine.
 
+config TEGRA_HDMI_SPD_INFOFRAME
+       bool "Support SPD InfoFrame"
+       depends on TEGRA_HDMI2_0
+       help
+         Say Y here to support SPD InfoFrame over a Generic InfoFrame. This will
+         disable HDR InfoFrame as only one of them can use Generic InfoFrame at
+         one time. It is only needed if you want to send vendor name, production
+         description and source information to HDMI device. The content of SPD
+         InfoFrame can be parsed from device tree.
+
 config TEGRA_DEBUG_HDCP
        bool "Support Debug mode for HDCP on HDMI2_0"
        depends on TEGRA_HDMIHDCP
index 8ab862e..0514476 100644 (file)
@@ -556,8 +556,10 @@ u16 tegra_edid_get_ex_hdr_cap(struct tegra_edid *edid)
                return -EFAULT;
        }
 
+#ifndef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
        if (edid->data->hdr_present)
                ret |= FB_CAP_HDR;
+#endif
 
        return ret;
 }
index d33e5f7..17c5461 100644 (file)
@@ -1471,6 +1471,7 @@ static void tegra_hdmi_vendor_infoframe(struct tegra_hdmi *hdmi)
 #undef HDMI_INFOFRAME_LEN_VENDOR_LLC
 }
 
+#ifndef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
 static void tegra_hdmi_hdr_infoframe_update(struct tegra_hdmi *hdmi)
 {
        struct hdmi_hdr_infoframe *hdr = &hdmi->hdr;
@@ -1552,6 +1553,89 @@ static void tegra_hdmi_hdr_infoframe(struct tegra_hdmi *hdmi)
 
        return;
 }
+#endif
+
+#ifdef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
+static void tegra_hdmi_spd_infoframe_update(struct tegra_hdmi *hdmi)
+{
+       struct hdmi_spd_infoframe *spd = &hdmi->spd;
+       struct spd_infoframe *spd_infoframe =
+               hdmi->dc->pdata->default_out->hdmi_out->spd_infoframe;
+
+       memset(&hdmi->spd, 0, sizeof(hdmi->spd));
+
+       if (tegra_platform_is_linsim())
+               return;
+
+       /* PB1-8 : Vendor Name */
+       spd->vendor_name_char_1 = spd_infoframe->vendor_name[0];
+       spd->vendor_name_char_2 = spd_infoframe->vendor_name[1];
+       spd->vendor_name_char_3 = spd_infoframe->vendor_name[2];
+       spd->vendor_name_char_4 = spd_infoframe->vendor_name[3];
+       spd->vendor_name_char_5 = spd_infoframe->vendor_name[4];
+       spd->vendor_name_char_6 = spd_infoframe->vendor_name[5];
+       spd->vendor_name_char_7 = spd_infoframe->vendor_name[6];
+       spd->vendor_name_char_8 = spd_infoframe->vendor_name[7];
+
+       /* PB9-24 : Product Description */
+       spd->prod_desc_char_1 = spd_infoframe->prod_desc[0];
+       spd->prod_desc_char_2 = spd_infoframe->prod_desc[1];
+       spd->prod_desc_char_3 = spd_infoframe->prod_desc[2];
+       spd->prod_desc_char_4 = spd_infoframe->prod_desc[3];
+       spd->prod_desc_char_5 = spd_infoframe->prod_desc[4];
+       spd->prod_desc_char_6 = spd_infoframe->prod_desc[5];
+       spd->prod_desc_char_7 = spd_infoframe->prod_desc[6];
+       spd->prod_desc_char_8 = spd_infoframe->prod_desc[7];
+       spd->prod_desc_char_9 = spd_infoframe->prod_desc[8];
+       spd->prod_desc_char_10 = spd_infoframe->prod_desc[9];
+       spd->prod_desc_char_11 = spd_infoframe->prod_desc[10];
+       spd->prod_desc_char_12 = spd_infoframe->prod_desc[11];
+       spd->prod_desc_char_13 = spd_infoframe->prod_desc[12];
+       spd->prod_desc_char_14 = spd_infoframe->prod_desc[13];
+       spd->prod_desc_char_15 = spd_infoframe->prod_desc[14];
+       spd->prod_desc_char_16 = spd_infoframe->prod_desc[15];
+
+       /* PB25 : Source Information */
+       spd->source_information = spd_infoframe->source_information;
+
+       return;
+}
+
+static void tegra_hdmi_spd_infoframe(struct tegra_hdmi *hdmi)
+{
+       struct tegra_dc_sor_data *sor = hdmi->sor;
+       u32 val;
+
+       /* set_bits = contains all the bits to be set
+        * for NV_SOR_HDMI_GENERIC_CTRL reg */
+       u32 set_bits = (NV_SOR_HDMI_GENERIC_CTRL_ENABLE_YES |
+                       NV_SOR_HDMI_GENERIC_CTRL_OTHER_DISABLE |
+                       NV_SOR_HDMI_GENERIC_CTRL_SINGLE_DISABLE);
+
+       /* read the current value to restore some bit values */
+       val = (tegra_sor_readl(sor, NV_SOR_HDMI_GENERIC_CTRL)
+                               & ~set_bits);
+
+       /* disable generic infoframe before configuring */
+       tegra_sor_writel(sor, NV_SOR_HDMI_GENERIC_CTRL, 0);
+
+       tegra_hdmi_spd_infoframe_update(hdmi);
+
+       tegra_hdmi_infoframe_pkt_write(hdmi, NV_SOR_HDMI_GENERIC_HEADER,
+                                       HDMI_INFOFRAME_TYPE_SPD,
+                                       HDMI_INFOFRAME_VS_SPD,
+                                       HDMI_INFOFRAME_LEN_SPD,
+                                       &hdmi->spd, sizeof(hdmi->spd),
+                                       true);
+
+       /* set the required bits in NV_SOR_HDMI_GENERIC_CTRL*/
+       val = val | set_bits;
+
+       tegra_sor_writel(sor, NV_SOR_HDMI_GENERIC_CTRL, val);
+
+       return;
+}
+#endif
 
 __maybe_unused
 static int tegra_hdmi_scdc_read(struct tegra_hdmi *hdmi,
@@ -1856,6 +1940,9 @@ static int tegra_hdmi_controller_enable(struct tegra_hdmi *hdmi)
        tegra_hdmi_config(hdmi);
        tegra_hdmi_avi_infoframe(hdmi);
        tegra_hdmi_vendor_infoframe(hdmi);
+#ifdef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
+       tegra_hdmi_spd_infoframe(hdmi);
+#endif
 
        tegra_sor_pad_cal_power(sor, true);
        tegra_hdmi_config_tmds(hdmi);
@@ -2166,7 +2253,9 @@ static int tegra_dc_hdmi_set_hdr(struct tegra_dc *dc)
        ret = tegra_edid_get_ex_hdr_cap(hdmi->edid);
        if (!(ret & FB_CAP_HDR))
                return 0;
+#ifndef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
        tegra_hdmi_hdr_infoframe(hdmi);
+#endif
 
        /*
         *If hdr is disabled then send HDR infoframe for
index 972c381..cfc01cd 100644 (file)
@@ -264,6 +264,60 @@ struct hdmi_hdr_infoframe {
 } __packed;
 
 enum {
+       HDMI_SPD_SI_UNKNOWN = 0x0,
+       HDMI_SPD_SI_DIGITAL_STB = 0x1,
+       HDMI_SPD_SI_DVD_PLAYER = 0x2,
+       HDMI_SPD_SI_DVHS = 0x3,
+       HDMI_SPD_SI_HDD_VIDEORECORDER = 0x4,
+       HDMI_SPD_SI_DVD = 0x5,
+       HDMI_SPD_SI_DSC = 0x6,
+       HDMI_SPD_SI_VIDEO_CD = 0x7,
+       HDMI_SPD_SI_GAME = 0x8,
+       HDMI_SPD_SI_PC_GENERAL = 0x9,
+       HDMI_SPD_SI_BLUERAY_DISC = 0xa,
+       HDMI_SPD_SI_SUPER_AUDIO_CD = 0xb,
+       HDMI_SPD_SI_HD_DVD = 0xc,
+       HDMI_SPD_SI_PMP = 0xd,
+       HDMI_SPD_SI_RESERVED = 0xFF,
+};
+
+struct hdmi_spd_infoframe {
+       /* PB0 */
+       u32 csum:8;     /* checksum */
+
+       /* PB1-8 : Vendor Name */
+       u32 vendor_name_char_1:8;
+       u32 vendor_name_char_2:8;
+       u32 vendor_name_char_3:8;
+       u32 vendor_name_char_4:8;
+       u32 vendor_name_char_5:8;
+       u32 vendor_name_char_6:8;
+       u32 vendor_name_char_7:8;
+       u32 vendor_name_char_8:8;
+
+       /* PB9-24 : Product Description */
+       u32 prod_desc_char_1:8;
+       u32 prod_desc_char_2:8;
+       u32 prod_desc_char_3:8;
+       u32 prod_desc_char_4:8;
+       u32 prod_desc_char_5:8;
+       u32 prod_desc_char_6:8;
+       u32 prod_desc_char_7:8;
+       u32 prod_desc_char_8:8;
+       u32 prod_desc_char_9:8;
+       u32 prod_desc_char_10:8;
+       u32 prod_desc_char_11:8;
+       u32 prod_desc_char_12:8;
+       u32 prod_desc_char_13:8;
+       u32 prod_desc_char_14:8;
+       u32 prod_desc_char_15:8;
+       u32 prod_desc_char_16:8;
+
+       /* PB25 : Source Information */
+       u32 source_information:8;
+} __packed;
+
+enum {
        HDMI_AUDIO_CHANNEL_CNT_STREAM,  /* refer to audio stream header */
        HDMI_AUDIO_CHANNEL_CNT_2,
        HDMI_AUDIO_CHANNEL_CNT_3,
@@ -347,6 +401,7 @@ struct tegra_hdmi {
        struct tegra_dc_sor_data *sor;
        struct hdmi_avi_infoframe avi;
        struct hdmi_hdr_infoframe hdr;
+       struct hdmi_spd_infoframe spd;
        bool enabled;
        atomic_t clock_refcount;
 
index eab9e88..69d5864 100644 (file)
@@ -566,6 +566,64 @@ static int parse_vrr_settings(struct platform_device *ndev,
        return 0;
 }
 
+#ifdef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
+static int parse_spd(struct platform_device *ndev,
+               struct device_node *np,
+               struct spd_infoframe *spd)
+{
+       const char *temp_str0;
+       u32 temp;
+
+       if (!of_property_read_string(np, "vendor-name",
+               &temp_str0)) {
+               memcpy(spd->vendor_name, temp_str0,
+                               sizeof(spd->vendor_name));
+               OF_DC_LOG("spd vendor-name %s\n", spd->vendor_name);
+       }
+
+       if (!of_property_read_string(np, "product-description",
+               &temp_str0)) {
+               memcpy(spd->prod_desc, temp_str0,
+                               sizeof(spd->prod_desc));
+               OF_DC_LOG("spd product-description %s\n", spd->prod_desc);
+       }
+
+       if (!of_property_read_u32(np, "source-information", &temp)) {
+               spd->source_information = (u32)temp;
+               OF_DC_LOG("spd source-information %d\n", temp);
+       }
+
+       return 0;
+}
+
+static int parse_spd_infoframe(struct platform_device *ndev,
+       struct device_node *np, struct tegra_dc_out *default_out)
+{
+       struct device_node *spd_np = NULL;
+       int err = 0;
+
+       spd_np = of_get_child_by_name(np, "spd-infoframe");
+       if (!spd_np) {
+               pr_err("%s: could not find spd-infoframe node\n",
+                               __func__);
+               goto fail_parse_spd;
+       } else {
+
+               default_out->hdmi_out->spd_infoframe = devm_kzalloc(&ndev->dev,
+                       sizeof(struct spd_infoframe), GFP_KERNEL);
+
+               err = parse_spd(ndev, spd_np,
+                               default_out->hdmi_out->spd_infoframe);
+               if (err)
+                       goto fail_parse_spd;
+       }
+       return 0;
+
+fail_parse_spd:
+       return err;
+}
+#endif
+
 static int parse_tmds_config(struct platform_device *ndev,
        struct device_node *np, struct tegra_dc_out *default_out)
 {
@@ -2350,6 +2408,14 @@ struct tegra_dc_platform_data
                                pdata->default_out);
                if (err)
                        goto fail_parse;
+
+#ifdef CONFIG_TEGRA_HDMI_SPD_INFOFRAME
+               err = parse_spd_infoframe(ndev, np_target_disp,
+                               pdata->default_out);
+               if (err)
+                       goto fail_parse;
+#endif
+
                if (!of_property_read_u32(np_target_disp,
                                        "nvidia,hdmi-fpd-bridge", &temp)) {
                        pdata->default_out->hdmi_out->