video: tegra: calculate h_ref_to_sync for mode
Jon Mayo [Fri, 11 Mar 2011 22:39:26 +0000 (14:39 -0800)]
add tegra_dc_set_fb_mode() to create tegra_dc_mode from fb_videmode.

Original-Change-Id: Icfaf99ab1c14b64f52b5b382bc8b94ed2e8306db
Reviewed-on: http://git-master/r/22689
Reviewed-by: Jonathan Mayo <jmayo@nvidia.com>
Tested-by: Jonathan Mayo <jmayo@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Original-Change-Id: I233b15e1c88e0946752cac1251d5122a1f50a579

Rebase-Id: R7e280e6d4a1e2f2432ec4ca85bfcf209fd55c9dc

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

index 5711f11..e7aeb8c 100644 (file)
@@ -366,6 +366,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
 int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
 
 int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
+struct fb_videomode;
+int tegra_dc_set_fb_mode(struct tegra_dc *dc, const struct fb_videomode *fbmode,
+       bool stereo_mode);
 
 unsigned tegra_dc_get_out_height(struct tegra_dc *dc);
 unsigned tegra_dc_get_out_width(struct tegra_dc *dc);
index a3aabf6..541346a 100644 (file)
@@ -774,6 +774,96 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
        tegra_dvfs_set_rate(clk, pclk);
 }
 
+static void calc_h_ref_to_sync(const struct tegra_dc_mode *mode,
+       long *refmin, long *refmax)
+{
+       long a, b;
+       a = 0; /* Constraint 5 */
+       /* Constraint 1 */
+       if (a + mode->h_sync_width + mode->h_back_porch <= 11)
+               a = 1 + 11 - mode->h_sync_width - mode->h_back_porch;
+       /* Constraint 6 */
+       if (mode->h_front_porch < a + 1)
+               a = mode->h_front_porch - 1;
+
+       b = mode->h_front_porch - 1; /* Constraint 6 */
+       /* Constraint 1 */
+       if (b + mode->h_sync_width + mode->h_back_porch <= 11)
+               b = 1 + 11 - mode->h_sync_width - mode->h_back_porch;
+       /* Constraint 5 */
+       if (b < 0)
+               b = 0;
+
+       if (refmin)
+               *refmin = a;
+       if (refmax)
+               *refmax = b;
+}
+
+static void calc_v_ref_to_sync(const struct tegra_dc_mode *mode, long *refmin)
+{
+       long a;
+       a = 1; /* Constraint 5 */
+       /* Constraint 2 */
+       if (a + mode->v_sync_width + mode->v_back_porch <= 1)
+               a = 1 + 1 - mode->v_sync_width - mode->v_back_porch;
+       /* Constraint 6 */
+       if (mode->v_front_porch < a + 1)
+               a = mode->v_front_porch - 1;
+
+        if (refmin)
+                *refmin = a;
+}
+
+static int calc_ref_to_sync(struct tegra_dc_mode *mode)
+{
+       long a, b;
+
+       calc_h_ref_to_sync(mode, &a, &b);
+       if (b > a && b % 2)
+               mode->h_ref_to_sync = b - 1; /* use largest even value */
+       else
+               mode->h_ref_to_sync = b; /* even or only possible value */
+
+       /* use smallest valid value for vertical ref */
+       calc_v_ref_to_sync(mode, &a);
+       mode->v_ref_to_sync = a;
+
+       return 0;
+}
+
+#ifdef DEBUG
+/* return in 1000ths of a Hertz */
+static int calc_refresh(const struct tegra_dc_mode *m)
+{
+       long h_total, v_total, refresh;
+       h_total = m->h_active + m->h_front_porch + m->h_back_porch +
+               m->h_sync_width;
+       v_total = m->v_active + m->v_front_porch + m->v_back_porch +
+               m->v_sync_width;
+       refresh = m->pclk / h_total;
+       refresh *= 1000;
+       refresh /= v_total;
+       return refresh;
+}
+
+static void print_mode(struct tegra_dc *dc,
+                       const struct tegra_dc_mode *mode, const char *note)
+{
+       if (mode) {
+               int refresh = calc_refresh(mode);
+               dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n",
+                       note ? note : "",
+                       mode->h_active, mode->v_active,
+                       refresh / 1000, refresh % 1000,
+                       mode->pclk);
+       }
+}
+#else
+static inline void print_mode(struct tegra_dc *dc,
+                       const struct tegra_dc_mode *mode, const char *note) { }
+#endif
+
 static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 {
        unsigned long val;
@@ -781,6 +871,8 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
        unsigned long div;
        unsigned long pclk;
 
+       print_mode(dc, mode, __func__);
+
        tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
        tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
                        DC_DISP_REF_TO_SYNC);
@@ -852,10 +944,54 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
 {
        memcpy(&dc->mode, mode, sizeof(dc->mode));
 
+       print_mode(dc, mode, __func__);
+
        return 0;
 }
 EXPORT_SYMBOL(tegra_dc_set_mode);
 
+int tegra_dc_set_fb_mode(struct tegra_dc *dc,
+               const struct fb_videomode *fbmode, bool stereo_mode)
+{
+       struct tegra_dc_mode mode;
+
+       mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000;
+       mode.h_sync_width = fbmode->hsync_len;
+       mode.v_sync_width = fbmode->vsync_len;
+       mode.h_back_porch = fbmode->left_margin;
+       mode.v_back_porch = fbmode->upper_margin;
+       mode.h_active = fbmode->xres;
+       mode.v_active = fbmode->yres;
+       mode.h_front_porch = fbmode->right_margin;
+       mode.v_front_porch = fbmode->lower_margin;
+       mode.stereo_mode = stereo_mode;
+       calc_ref_to_sync(&mode);
+       printk("Using mode %dx%d pclk=%d href=%d vref=%d\n",
+               mode.h_active, mode.v_active, mode.pclk,
+               mode.h_ref_to_sync, mode.v_ref_to_sync
+       );
+
+       if (mode.stereo_mode) {
+               mode.pclk *= 2;
+               /* total v_active = yres*2 + activespace */
+               mode.v_active = fbmode->yres*2 +
+                               fbmode->vsync_len +
+                               fbmode->upper_margin +
+                               fbmode->lower_margin;
+       }
+
+       mode.flags = 0;
+
+       if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT))
+               mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC;
+
+       if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT))
+               mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC;
+
+       return tegra_dc_set_mode(dc, &mode);
+}
+EXPORT_SYMBOL(tegra_dc_set_fb_mode);
+
 void
 tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
 {
@@ -1213,6 +1349,8 @@ static void tegra_dc_init(struct tegra_dc *dc)
        dc->syncpt_min = dc->syncpt_max =
                nvhost_syncpt_read(&dc->ndev->host->syncpt, disp_syncpt);
 
+       print_mode(dc, &dc->mode, __func__);
+
        if (dc->mode.pclk)
                tegra_dc_program_mode(dc, &dc->mode);
 }
index 0d2c991..c8cb7d5 100644 (file)
@@ -174,7 +174,7 @@ static int tegra_fb_set_par(struct fb_info *info)
        }
 
        if (var->pixclock) {
-               struct tegra_dc_mode mode;
+               bool stereo;
 
                info->mode = (struct fb_videomode *)
                        fb_find_best_mode(var, &info->modelist);
@@ -183,42 +183,14 @@ static int tegra_fb_set_par(struct fb_info *info)
                        return -EINVAL;
                }
 
-               mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
-               mode.h_ref_to_sync = 1;
-               mode.v_ref_to_sync = 1;
-               mode.h_sync_width = info->mode->hsync_len;
-               mode.v_sync_width = info->mode->vsync_len;
-               mode.h_back_porch = info->mode->left_margin;
-               mode.v_back_porch = info->mode->upper_margin;
-               mode.h_active = info->mode->xres;
-               mode.v_active = info->mode->yres;
-               mode.h_front_porch = info->mode->right_margin;
-               mode.v_front_porch = info->mode->lower_margin;
                /*
                 * only enable stereo if the mode supports it and
                 * client requests it
                 */
-               mode.stereo_mode = !!(var->vmode & info->mode->vmode &
+               stereo = !!(var->vmode & info->mode->vmode &
                                        FB_VMODE_STEREO_FRAME_PACK);
 
-               if (mode.stereo_mode) {
-                       mode.pclk *= 2;
-                       /* total v_active = yres*2 + activespace */
-                       mode.v_active = info->mode->yres*2 +
-                                       info->mode->vsync_len +
-                                       info->mode->upper_margin +
-                                       info->mode->lower_margin;
-               }
-
-               mode.flags = 0;
-
-               if (!(info->mode->sync & FB_SYNC_HOR_HIGH_ACT))
-                       mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC;
-
-               if (!(info->mode->sync & FB_SYNC_VERT_HIGH_ACT))
-                       mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC;
-
-               tegra_dc_set_mode(tegra_fb->win->dc, &mode);
+               tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo);
 
                tegra_fb->win->w = info->mode->xres;
                tegra_fb->win->h = info->mode->yres;