video: tegra: dc: load video mode during vblank
Jon Mayo [Fri, 16 Mar 2012 19:50:59 +0000 (12:50 -0700)]
Handle mode set for FBIOPUT_VSCREENINFO at the end of a frame (during
vblank). This elimiates the work around that requires disabling then
enabling display to change modes.

Bug 560152
Bug 1166276

Change-Id: If6d22627b7ff1f07691937235aec687688d3c608
Signed-off-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-on: http://git-master/r/163108
Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_priv_defs.h
drivers/video/tegra/dc/mode.c
drivers/video/tegra/fb.c

index 0bc520d..a618c50 100644 (file)
@@ -696,6 +696,8 @@ bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts);
 
 int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
 struct fb_videomode;
+int tegra_dc_to_fb_videomode(struct fb_videomode *fbmode,
+       const struct tegra_dc_mode *mode);
 int tegra_dc_set_fb_mode(struct tegra_dc *dc, const struct fb_videomode *fbmode,
        bool stereo_mode);
 
index c7a0bc8..80012a4 100644 (file)
@@ -1566,6 +1566,10 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        else
                tegra_dc_continuous_irq(dc, status);
 
+       /* update video mode if it has changed since the last frame */
+       if (status & (FRAME_END_INT | V_BLANK_INT))
+               tegra_dc_update_mode(dc);
+
        tegra_dc_release_dc_out(dc);
        tegra_dc_io_end(dc);
        clk_disable(dc->clk);
@@ -2176,6 +2180,24 @@ static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
 }
 #endif
 
+static void tegra_dc_add_modes(struct tegra_dc *dc)
+{
+       struct fb_monspecs specs;
+       int i;
+
+       memset(&specs, 0, sizeof(specs));
+       specs.max_x = dc->mode.h_active * 1000;
+       specs.max_y = dc->mode.v_active * 1000;
+       specs.modedb_len = dc->out->n_modes;
+       specs.modedb = kzalloc(specs.modedb_len *
+               sizeof(struct fb_videomode), GFP_KERNEL);
+       for (i = 0; i < dc->out->n_modes; i++)
+               tegra_dc_to_fb_videomode(&specs.modedb[i],
+                       &dc->out->modes[i]);
+       tegra_fb_update_monspecs(dc->fb, &specs, NULL);
+       kfree(specs.modedb);
+}
+
 static int tegra_dc_probe(struct platform_device *ndev)
 {
        struct tegra_dc *dc;
@@ -2381,6 +2403,9 @@ static int tegra_dc_probe(struct platform_device *ndev)
                tegra_dc_io_end(dc);
        }
 
+       if (dc->out && dc->out->n_modes)
+               tegra_dc_add_modes(dc);
+
        if (dc->out && dc->out->hotplug_init)
                dc->out->hotplug_init(&ndev->dev);
 
index bc0254d..bf600cb 100644 (file)
@@ -261,9 +261,10 @@ void tegra_dc_clear_bandwidth(struct tegra_dc *dc);
 void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new);
 int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n);
 
-/* defined in mode.c, used in dc.c */
+/* defined in mode.c, used in dc.c and window.c */
 int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode);
 int tegra_dc_calc_refresh(const struct tegra_dc_mode *m);
+void tegra_dc_update_mode(struct tegra_dc *dc);
 
 /* defined in clock.c, used in dc.c, dsi.c and hdmi.c */
 void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk);
index 3a8e104..34db533 100644 (file)
@@ -183,6 +183,8 @@ struct tegra_dc {
        u32                             one_shot_delay_ms;
        struct delayed_work             one_shot_work;
        s64                             frame_end_timestamp;
+
+       bool                            mode_dirty;
 };
 
 #endif
index 59e2187..d271c5e 100644 (file)
@@ -270,7 +270,9 @@ EXPORT_SYMBOL(tegra_dc_get_panel_sync_rate);
 
 int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
 {
+       mutex_lock(&dc->lock);
        memcpy(&dc->mode, mode, sizeof(dc->mode));
+       dc->mode_dirty = true;
 
        if (dc->out->type == TEGRA_DC_OUT_RGB)
                panel_sync_rate = tegra_dc_calc_refresh(mode);
@@ -279,11 +281,55 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
 
        print_mode(dc, mode, __func__);
        dc->frametime_ns = calc_frametime_ns(mode);
+       mutex_unlock(&dc->lock);
 
        return 0;
 }
 EXPORT_SYMBOL(tegra_dc_set_mode);
 
+int tegra_dc_to_fb_videomode(struct fb_videomode *fbmode,
+       const struct tegra_dc_mode *mode)
+{
+       if (!fbmode || !mode || !mode->pclk)
+               return -EINVAL;
+       memset(fbmode, 0, sizeof(*fbmode));
+       if (mode->rated_pclk >= 1000) /* handle DSI one-shot modes */
+               fbmode->pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
+       else if (mode->pclk >= 1000) /* normal continous modes */
+               fbmode->pixclock = KHZ2PICOS(mode->pclk / 1000);
+       fbmode->right_margin = mode->h_front_porch;
+       fbmode->lower_margin = mode->v_front_porch;
+       fbmode->hsync_len = mode->h_sync_width;
+       fbmode->vsync_len = mode->v_sync_width;
+       fbmode->left_margin = mode->h_back_porch;
+       fbmode->upper_margin = mode->v_back_porch;
+       fbmode->xres = mode->h_active;
+       fbmode->yres = mode->v_active;
+       fbmode->vmode = FB_VMODE_NONINTERLACED;
+       if (mode->stereo_mode)
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+               fbmode->vmode |= FB_VMODE_STEREO_FRAME_PACK;
+#else
+               fbmode->vmode |= FB_VMODE_STEREO_LEFT_RIGHT;
+#endif
+       if (!(mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC))
+               fbmode->sync |=  FB_SYNC_HOR_HIGH_ACT;
+       if (!(mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC))
+               fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+       /* round up refresh rate for fractions above 0.500Hz */
+       fbmode->refresh = (tegra_dc_calc_refresh(mode) + 500) / 1000;
+
+       return 0;
+}
+
+void tegra_dc_update_mode(struct tegra_dc *dc)
+{
+       if (dc->mode_dirty) {
+               tegra_dc_program_mode(dc, &dc->mode);
+               dc->mode_dirty = false;
+       }
+}
+
 int tegra_dc_set_fb_mode(struct tegra_dc *dc,
                const struct fb_videomode *fbmode, bool stereo_mode)
 {
index 16255a6..07ea12d 100644 (file)
@@ -529,6 +529,7 @@ struct tegra_fb_info *tegra_fb_register(struct platform_device *ndev,
        unsigned long fb_phys = 0;
        int ret = 0;
        unsigned stride;
+       struct fb_videomode m;
 
        win = tegra_dc_get_window(dc, fb_data->win);
        if (!win) {
@@ -584,22 +585,16 @@ struct tegra_fb_info *tegra_fb_register(struct platform_device *ndev,
        info->fix.smem_len      = fb_size;
        info->fix.line_length = stride;
 
-       info->var.xres                  = fb_data->xres;
-       info->var.yres                  = fb_data->yres;
+       INIT_LIST_HEAD(&info->modelist);
+       /* pick first mode as the default for initialization */
+       tegra_dc_to_fb_videomode(&m, &dc->mode);
+       fb_videomode_to_var(&info->var, &m);
        info->var.xres_virtual          = fb_data->xres;
        info->var.yres_virtual          = fb_data->yres * 2;
        info->var.bits_per_pixel        = fb_data->bits_per_pixel;
        info->var.activate              = FB_ACTIVATE_VBL;
        info->var.height                = tegra_dc_get_out_height(dc);
        info->var.width                 = tegra_dc_get_out_width(dc);
-       info->var.pixclock              = 0;
-       info->var.left_margin           = 0;
-       info->var.right_margin          = 0;
-       info->var.upper_margin          = 0;
-       info->var.lower_margin          = 0;
-       info->var.hsync_len             = 0;
-       info->var.vsync_len             = 0;
-       info->var.vmode                 = FB_VMODE_NONINTERLACED;
 
        win->x.full = dfixed_const(0);
        win->y.full = dfixed_const(0);