diff options
Diffstat (limited to 'drivers/video/tegra/dc/mode.c')
-rw-r--r-- | drivers/video/tegra/dc/mode.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/mode.c b/drivers/video/tegra/dc/mode.c index b600c70286e3..ab8907815749 100644 --- a/drivers/video/tegra/dc/mode.c +++ b/drivers/video/tegra/dc/mode.c @@ -32,6 +32,115 @@ #include "dc_reg.h" #include "dc_priv.h" +const struct fb_videomode tegra_modes[] = { + /* TouchRevolution Fusion 10" aka Chunghwa Picture Tubes + * CLAA100NC05 10.1 inch 1024x600 single channel LVDS panel + */ + { + .name = "1024x600", + .refresh = 60, + .xres = 1024, + .yres = 600, + .pixclock = 20833, + .left_margin = 104, + .right_margin = 43, + .upper_margin = 24, + .lower_margin = 20, + .hsync_len = 5, + .vsync_len = 5, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .flag = FB_FLAG_RATIO_16_9, + .vmode = FB_VMODE_NONINTERLACED + }, + { + /* 1366x768 */ + .refresh = 60, + .xres = 1366, + .yres = 768, + .pixclock = KHZ2PICOS(72072), + .hsync_len = 58, /* h_sync_width */ + .vsync_len = 4, /* v_sync_width */ + .left_margin = 58, /* h_back_porch */ + .upper_margin = 4, /* v_back_porch */ + .right_margin = 58, /* h_front_porch */ + .lower_margin = 4, /* v_front_porch */ + .vmode = FB_VMODE_NONINTERLACED, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + }, + { + /* 1680x1050p 59.94/60hz */ + .refresh = 60, + .xres = 1680, + .yres = 1050, + .pixclock = KHZ2PICOS(147140), + .hsync_len = 184, /* h_sync_width */ + .vsync_len = 3, /* v_sync_width */ + .left_margin = 288, /* h_back_porch */ + .upper_margin = 33, /* v_back_porch */ + .right_margin = 104, /* h_front_porch */ + .lower_margin = 1, /* v_front_porch */ + .vmode = FB_VMODE_NONINTERLACED, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + }, +{ + /* 1920x1080p 59.94/60hz CVT */ + .refresh = 60, + .xres = 1920, + .yres = 1080, + .pixclock = KHZ2PICOS(148500), + .hsync_len = 44, /* h_sync_width */ + .vsync_len = 5, /* v_sync_width */ + .left_margin = 148, /* h_back_porch */ + .upper_margin = 36, /* v_back_porch */ + .right_margin = 88, /* h_front_porch */ + .lower_margin = 4, /* v_front_porch */ + .vmode = FB_VMODE_NONINTERLACED, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + }, + { + /* 1920x1200p 60hz */ + .refresh = 60, + .xres = 1920, + .yres = 1200, + .pixclock = KHZ2PICOS(154000), + .hsync_len = 32, /* h_sync_width */ + .vsync_len = 6, /* v_sync_width */ + .left_margin = 80, /* h_back_porch */ + .upper_margin = 26, /* v_back_porch */ + .right_margin = 48, /* h_front_porch */ + .lower_margin = 3, /* v_front_porch */ + .vmode = FB_VMODE_NONINTERLACED, + .sync = 0, + }, +}; + +/* try to find best matching mode using our modes, VESA and CEA modes from + * modedb + */ +int tegra_fb_find_mode(struct fb_var_screeninfo *var, struct fb_info *info, + const char* option, unsigned int default_bpp) +{ + int out; + + out = fb_find_mode(var, info, option, tegra_modes, + ARRAY_SIZE(tegra_modes), NULL, default_bpp); + + /* Only accept this mode if we found a reasonable match (resolution) */ + if (out == 1 || out == 2) + return out; + + out = fb_find_mode(&info->var, info, option, + cea_modes, CEA_MODEDB_SIZE, NULL, default_bpp); + + /* Check if we found a full match */ + if (out == 1 || out == 2) + return out; + + return fb_find_mode(&info->var, info, option, + vesa_modes, VESA_MODEDB_SIZE, NULL, default_bpp); +} +EXPORT_SYMBOL(tegra_fb_find_mode); + /* return non-zero if constraint is violated */ static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href) { @@ -417,6 +526,12 @@ static int _tegra_dc_set_mode(struct tegra_dc *dc, } memcpy(&dc->mode, mode, sizeof(dc->mode)); + + dev_info(&dc->ndev->dev, "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 + ); + dc->mode_dirty = true; if (dc->out->type == TEGRA_DC_OUT_RGB) @@ -440,6 +555,78 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) } EXPORT_SYMBOL(tegra_dc_set_mode); +int tegra_dc_var_to_dc_mode(struct tegra_dc *dc, struct fb_var_screeninfo *var, + struct tegra_dc_mode *mode) +{ + bool stereo_mode = false; + int err; + + if (!var->pixclock) + return -EINVAL; + + mode->pclk = PICOS2KHZ(var->pixclock) * 1000; + mode->h_sync_width = var->hsync_len; + mode->v_sync_width = var->vsync_len; + mode->h_back_porch = var->left_margin; + mode->v_back_porch = var->upper_margin; + mode->h_active = var->xres; + mode->v_active = var->yres; + mode->h_front_porch = var->right_margin; + mode->v_front_porch = var->lower_margin; + mode->stereo_mode = stereo_mode; + if (dc->out->type == TEGRA_DC_OUT_HDMI) { + /* HDMI controller requires h_ref=1, v_ref=1 */ + mode->h_ref_to_sync = 1; + mode->v_ref_to_sync = 1; + } else { + /* + * HACK: + * If v_front_porch is only 1, we would violate Constraint 5/6 + * in this case, increase front porch by 1 + */ + if (mode->v_front_porch <= 1) + mode->v_front_porch = 2; + + err = calc_ref_to_sync(mode); + if (err) { + dev_err(&dc->ndev->dev, "display timing ref_to_sync" + "calculation failed with code %d\n", err); + return -EINVAL; + } + dev_info(&dc->ndev->dev, "Calculated sync href=%d vref=%d\n", + mode->h_ref_to_sync, mode->v_ref_to_sync); + } + if (!check_ref_to_sync(mode)) { + dev_err(&dc->ndev->dev, + "display timing doesn't meet restrictions.\n"); + return -EINVAL; + } + +#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT + /* Double the pixel clock and update v_active only for + * frame packed mode */ + if (mode->stereo_mode) { + mode->pclk *= 2; + /* total v_active = yres*2 + activespace */ + mode->v_active = var->yres * 2 + + var->vsync_len + + var->upper_margin + + var->lower_margin; + } +#endif + + mode->flags = 0; + + if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) + mode->flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC; + + if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) + mode->flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC; + + return 0; +} +EXPORT_SYMBOL(tegra_dc_var_to_dc_mode); + int tegra_dc_to_fb_videomode(struct fb_videomode *fbmode, const struct tegra_dc_mode *mode) { @@ -554,6 +741,7 @@ EXPORT_SYMBOL(tegra_dc_set_drm_mode); int tegra_dc_set_fb_mode(struct tegra_dc *dc, const struct fb_videomode *fbmode, bool stereo_mode) { + int err; struct tegra_dc_mode mode; if (!fbmode->pixclock) @@ -570,6 +758,7 @@ int tegra_dc_set_fb_mode(struct tegra_dc *dc, mode.h_front_porch = fbmode->right_margin; mode.v_front_porch = fbmode->lower_margin; mode.stereo_mode = stereo_mode; +#if 0 mode.vmode = fbmode->vmode; if (fbmode->flag & FB_FLAG_RATIO_16_9) mode.avi_m = TEGRA_DC_MODE_AVI_M_16_9; @@ -580,6 +769,35 @@ int tegra_dc_set_fb_mode(struct tegra_dc *dc, if (!check_mode_timings(dc, &mode)) return -EINVAL; +#endif + + if (dc->out->type == TEGRA_DC_OUT_HDMI) { + /* HDMI controller requires h_ref=1, v_ref=1 */ + mode.h_ref_to_sync = 1; + mode.v_ref_to_sync = 1; + } else { + /* + * HACK: + * If v_front_porch is only 1, we would violate Constraint 5/6 + * in this case, increase front porch by 1 + */ + if (mode.v_front_porch <= 1) + mode.v_front_porch = 2; + + err = calc_ref_to_sync(&mode); + if (err) { + dev_err(&dc->ndev->dev, "display timing ref_to_sync" + "calculation failed with code %d\n", err); + return -EINVAL; + } + dev_info(&dc->ndev->dev, "Calculated sync href=%d vref=%d\n", + mode.h_ref_to_sync, mode.v_ref_to_sync); + } + if (!check_ref_to_sync(&mode)) { + dev_err(&dc->ndev->dev, + "display timing doesn't meet restrictions.\n"); + return -EINVAL; + } #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT /* Double the pixel clock and update v_active only for |