diff options
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/omap2/dss/dispc.c | 72 |
1 files changed, 69 insertions, 3 deletions
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 8c96f57..bea37bb 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -1657,6 +1657,63 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, } } +/* + * This function is used to avoid synclosts in OMAP3, because of some + * undocumented horizontal position and timing related limitations. + */ +static int check_horiz_timing_omap3(enum omap_channel channel, u16 pos_x, + u16 width, u16 height, u16 out_width, u16 out_height) +{ + int DS = DIV_ROUND_UP(height, out_height); + struct omap_dss_device *dssdev = dispc_mgr_get_device(channel); + struct omap_video_timings t = dssdev->panel.timings; + unsigned long nonactive, lclk, pclk; + static const u8 limits[3] = { 8, 10, 20 }; + u64 val, blank; + int i; + + nonactive = t.x_res + t.hfp + t.hsw + t.hbp - out_width; + pclk = dispc_mgr_pclk_rate(channel); + if (dispc_mgr_is_lcd(channel)) + lclk = dispc_mgr_lclk_rate(channel); + else + lclk = dispc_fclk_rate(); + + i = 0; + if (out_height < height) + i++; + if (out_width < width) + i++; + blank = div_u64((u64)(t.hbp + t.hsw + t.hfp) * lclk, pclk); + DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); + if (blank <= limits[i]) + return -EINVAL; + + /* + * Pixel data should be prepared before visible display point starts. + * So, atleast DS-2 lines must have already been fetched by DISPC + * during nonactive - pos_x period. + */ + val = div_u64((u64)(nonactive - pos_x) * lclk, pclk); + DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n", + val, max(0, DS - 2) * width); + if (val < max(0, DS - 2) * width) + return -EINVAL; + + /* + * All lines need to be refilled during the nonactive period of which + * only one line can be loaded during the active period. So, atleast + * DS - 1 lines should be loaded during nonactive period. + */ + val = div_u64((u64)nonactive * lclk, pclk); + DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n", + val, max(0, DS - 1) * width); + if (val < max(0, DS - 1) * width) + return -EINVAL; + + return 0; +} + static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode) @@ -1741,7 +1798,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode, bool *five_taps, - int *x_predecim, int *y_predecim) + int *x_predecim, int *y_predecim, u16 pos_x) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); @@ -1817,6 +1874,9 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, fclk = calc_fclk_five_taps(channel, in_width, in_height, out_width, out_height, color_mode); + error = check_horiz_timing_omap3(channel, pos_x, + in_width, in_height, out_width, out_height); + if (in_width > maxsinglelinewidth) if (in_height > out_height && in_height < out_height * 2) @@ -1824,7 +1884,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, if (!*five_taps) fclk = calc_fclk(channel, in_width, in_height, out_width, out_height); - error = (in_width > maxsinglelinewidth * 2 || + error = (error || in_width > maxsinglelinewidth * 2 || (in_width > maxsinglelinewidth && *five_taps) || !fclk || fclk > dispc_fclk_rate()); if (error) { @@ -1840,6 +1900,12 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, } while (decim_x <= *x_predecim && decim_y <= *y_predecim && error); + if (check_horiz_timing_omap3(channel, pos_x, width, height, + out_width, out_height)){ + DSSERR("horizontal timing too tight\n"); + return -EINVAL; + } + if (in_width > (maxsinglelinewidth * 2)) { DSSERR("Cannot setup scaling"); DSSERR("width exceeds maximum width possible"); @@ -1944,7 +2010,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, r = dispc_ovl_calc_scaling(plane, channel, in_width, in_height, out_width, out_height, oi->color_mode, &five_taps, - &x_predecim, &y_predecim); + &x_predecim, &y_predecim, oi->pos_x); if (r) return r; |