video: tegra: dc: hdmi vrr mode support.
Marvin Zhang [Fri, 17 Jul 2015 00:11:52 +0000 (17:11 -0700)]
Bug 1637861

Change-Id: I93db7451b6c8e56924dd94dd7c0c58cc4a3603de
Signed-off-by: Marvin Zhang <mzhang@nvidia.com>
Reviewed-on: http://git-master/r/797294
Reviewed-on: http://git-master/r/810505
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>

arch/arm/mach-tegra/include/mach/dc.h
arch/arm/mach-tegra/include/mach/fb.h
drivers/video/tegra/dc/dc_priv_defs.h
drivers/video/tegra/dc/edid.c
drivers/video/tegra/dc/edid.h
drivers/video/tegra/dc/hdmi2.0.c
drivers/video/tegra/dc/hdmi_state_machine.c
drivers/video/tegra/dc/hpd.c
drivers/video/tegra/fb.c

index 85e2dcc..04d0cd9 100644 (file)
@@ -667,6 +667,8 @@ struct tegra_vrr {
        s32     adjust_vfp;
        s32     adjust_db;
        s32     vfp;
+
+       s32     authenticated;
 };
 
 struct tegra_dc_out {
index 2100037..34523e8 100644 (file)
@@ -39,6 +39,8 @@ void tegra_fb_pan_display_reset(struct tegra_fb_info *fb_info);
 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
                              struct fb_monspecs *specs,
                              bool (*mode_filter)(const struct tegra_dc *dc,
+                                                 struct fb_videomode *mode),
+                             void (*vrr_mode)(const struct tegra_dc *dc,
                                                  struct fb_videomode *mode));
 void tegra_fb_update_fix(struct tegra_fb_info *fb_info,
                                struct fb_monspecs *specs);
@@ -66,7 +68,8 @@ static inline void tegra_fb_pan_display_reset(struct tegra_fb_info *fb_info)
 
 static inline void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
                                            struct fb_monspecs *specs,
-                                           bool (*mode_filter)(struct fb_videomode *mode))
+                               bool (*mode_filter)(struct fb_videomode *mode),
+                               void (*vrr_mode)(struct fb_videomode *mode))
 {
 }
 
index 0b950ce..1f68d72 100644 (file)
@@ -131,6 +131,9 @@ struct tegra_dc_out_ops {
        bool (*hpd_state) (struct tegra_dc *dc);
        /* Configure controller to receive hotplug events */
        int (*hotplug_init)(struct tegra_dc *dc);
+       /* Set up VRR mode */
+       void (*vrr_mode)(const struct tegra_dc *dc,
+                       struct fb_videomode *mode);
 };
 
 struct tegra_dc_shift_clk_div {
index 8fcd6b3..832a675 100644 (file)
@@ -233,6 +233,16 @@ int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data)
        return 0;
 }
 
+bool tegra_edid_is_vrr_capable(struct tegra_edid *edid)
+{
+       if (!edid || !edid->data) {
+               pr_warn("edid invalid\n");
+               return -EFAULT;
+       }
+
+       return edid->data->min_vrr_fps;
+}
+
 static int tegra_edid_parse_ext_block(const u8 *raw, int idx,
                               struct tegra_edid_pvt *edid)
 {
index 3c183d7..ff677e5 100644 (file)
@@ -146,4 +146,5 @@ int tegra_edid_underscan_supported(struct tegra_edid *edid);
 int tegra_edid_i2c_adap_change_rate(struct i2c_adapter *i2c_adap, int rate);
 int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data);
 int tegra_edid_audio_supported(struct tegra_edid *edid);
+bool tegra_edid_is_vrr_capable(struct tegra_edid *edid);
 #endif
index 8b37725..2430d33 100644 (file)
@@ -320,6 +320,29 @@ static u32 tegra_hdmi_mode_min_tmds_rate(const struct fb_videomode *mode)
 }
 
 __maybe_unused
+static void tegra_hdmi_update_vrr_mode(const struct tegra_dc *dc,
+                                               struct fb_videomode *mode)
+{
+       struct tegra_hdmi *hdmi = dc->out_data;
+       struct tegra_vrr *vrr  = dc->out->vrr;
+
+       if (!tegra_edid_is_vrr_capable(hdmi->edid))
+               return;
+
+       if (!vrr->authenticated)
+               return;
+
+       /*
+        * Inform VRR monitor to turn on VRR mode by increase vertical
+        * backporch by 2, and decrease vertical front porch by 2 to keep
+        * vertical total the same
+        */
+       mode->upper_margin += 2;
+       if (mode->lower_margin >= 4)
+               mode->lower_margin -= 2;
+}
+
+__maybe_unused
 static bool tegra_hdmi_fb_mode_filter(const struct tegra_dc *dc,
                                        struct fb_videomode *mode)
 {
@@ -507,7 +530,8 @@ static void tegra_hdmi_hotplug_notify(struct tegra_hdmi *hdmi,
 #else
        if (dc->fb) {
                tegra_fb_update_monspecs(hdmi->dc->fb, mon_spec,
-                                       tegra_hdmi_fb_mode_filter);
+                                       tegra_hdmi_fb_mode_filter,
+                                       tegra_hdmi_update_vrr_mode);
                tegra_fb_update_fix(hdmi->dc->fb, mon_spec);
        }
 #endif
@@ -520,6 +544,35 @@ static void tegra_hdmi_hotplug_notify(struct tegra_hdmi *hdmi,
 #endif
 }
 
+static int tegra_hdmi_vrr_authentication(struct tegra_hdmi *hdmi)
+{
+       int err = 0;
+       struct tegra_dc *dc = hdmi->dc;
+       struct tegra_vrr *vrr  = dc->out->vrr;
+
+       /*
+        * vrr panel authentication, the estimated time delay is about 200ms
+        * will be replaced with actual authentication code with subsequent
+        * change.
+        */
+       mdelay(200);
+       vrr->authenticated = 1;
+
+       return err;
+}
+
+static int tegra_hdmi_vrr_setup(struct tegra_hdmi *hdmi)
+{
+       int err = 0;
+
+       if (!tegra_edid_is_vrr_capable(hdmi->edid))
+               return -EINVAL;
+
+       err = tegra_hdmi_vrr_authentication(hdmi);
+
+       return err;
+}
+
 static int tegra_hdmi_edid_eld_setup(struct tegra_hdmi *hdmi)
 {
        int err;
@@ -534,6 +587,10 @@ static int tegra_hdmi_edid_eld_setup(struct tegra_hdmi *hdmi)
        if (err < 0)
                goto fail;
 
+       err = tegra_hdmi_vrr_setup(hdmi);
+       if (err < 0)
+               dev_info(&hdmi->dc->ndev->dev, "vrr_setup failed\n");
+
        tegra_dc_powergate_locked(hdmi->dc);
 
        tegra_hdmi_edid_config(hdmi);
@@ -775,6 +832,17 @@ fail:
        return err;
 }
 
+static int tegra_hdmi_vrr_init(struct tegra_hdmi *hdmi)
+{
+       struct tegra_dc *dc = hdmi->dc;
+       struct tegra_vrr *vrr  = dc->out->vrr;
+
+       if (!vrr || !vrr->capability)
+               return -EINVAL;
+
+       return 0;
+}
+
 static int tegra_hdmi_tmds_init(struct tegra_hdmi *hdmi)
 {
        struct device_node *np_prod = of_find_node_by_path(TMDS_NODE);
@@ -808,8 +876,7 @@ static int tegra_hdmi_config_tmds(struct tegra_hdmi *hdmi)
        /* Select mode with smallest clk freq > pclk */
        tmds_len = ARRAY_SIZE(tmds_config_modes);
        for (i = 0; i < tmds_len - 1 &&
-               tmds_config_modes[i].clk < hdmi->dc->mode.pclk; i++)
-               ;
+               tmds_config_modes[i].clk < hdmi->dc->mode.pclk; i++);
 
        if (tegra_platform_is_linsim())
                return 0;
@@ -900,6 +967,8 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
 
        tegra_hdmi_hpd_init(hdmi);
 
+       tegra_hdmi_vrr_init(hdmi);
+
        tegra_hdmi_debugfs_init(hdmi);
 
        tegra_hdmi_tmds_init(hdmi);
@@ -2185,4 +2254,5 @@ struct tegra_dc_out_ops tegra_dc_hdmi2_0_ops = {
        .mode_filter = tegra_hdmi_fb_mode_filter,
        .hpd_state = tegra_dc_hdmi_hpd_state,
        .vrr_enable = tegra_dc_hdmi_vrr_enable,
+       .vrr_mode = tegra_hdmi_update_vrr_mode,
 };
index 32ed7a5..01d1b24 100644 (file)
@@ -190,7 +190,7 @@ static void hdmi_disable_l(struct tegra_dc_hdmi_data *hdmi)
 #ifdef CONFIG_ADF_TEGRA
        tegra_adf_process_hotplug_disconnected(hdmi->dc->adf);
 #else
-       tegra_fb_update_monspecs(hdmi->dc->fb, NULL, NULL);
+       tegra_fb_update_monspecs(hdmi->dc->fb, NULL, NULL, NULL);
 #endif
 #ifdef CONFIG_TEGRA_DC_EXTENSIONS
        tegra_dc_ext_process_hotplug(hdmi->dc->ndev->id, false);
@@ -284,7 +284,7 @@ static void handle_check_edid_l(struct tegra_dc_hdmi_data *hdmi)
 #endif
 #ifdef CONFIG_TEGRA_DC_EXTENSIONS
        tegra_fb_update_monspecs(hdmi->dc->fb, &specs,
-               tegra_dc_hdmi_mode_filter);
+               tegra_dc_hdmi_mode_filter, NULL);
 #endif
 #ifdef CONFIG_SWITCH
        state = tegra_edid_audio_supported(hdmi->edid) ? 1 : 0;
index f09a0aa..d342115 100644 (file)
@@ -77,7 +77,7 @@ static void hpd_disable(struct tegra_hpd_data *data)
                tegra_adf_process_hotplug_disconnected(data->dc->adf);
 #else
        if (data->dc->fb)
-               tegra_fb_update_monspecs(data->dc->fb, NULL, NULL);
+               tegra_fb_update_monspecs(data->dc->fb, NULL, NULL, NULL);
 #endif
 
        if (data->ops->disable)
@@ -188,7 +188,7 @@ static void edid_read_notify(struct tegra_hpd_data *data)
        tegra_fb_update_monspecs(data->dc->fb, &data->mon_spec,
                                (data->ops->get_mode_filter) ?
                                (data->ops->get_mode_filter(data->drv_data)) :
-                               NULL);
+                               NULL, NULL);
        tegra_fb_update_fix(data->dc->fb, &data->mon_spec);
 #endif
 #ifdef CONFIG_SWITCH
index e56a609..05ece53 100644 (file)
@@ -628,7 +628,10 @@ void tegra_fb_pan_display_reset(struct tegra_fb_info *fb_info)
 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
                              struct fb_monspecs *specs,
                              bool (*mode_filter)(const struct tegra_dc *dc,
+                                                 struct fb_videomode *mode),
+                             void (*vrr_mode)(const struct tegra_dc *dc,
                                                  struct fb_videomode *mode))
+
 {
        struct fb_event event;
        int i;
@@ -671,6 +674,9 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
        fb_info->info->mode = specs->modedb;
 
        for (i = 0; i < specs->modedb_len; i++) {
+               if (vrr_mode)
+                       vrr_mode(fb_info->win.dc, &specs->modedb[i]);
+
                if (mode_filter) {
                        if (mode_filter(fb_info->win.dc, &specs->modedb[i]))
                                fb_add_videomode(&specs->modedb[i],