diff options
Diffstat (limited to 'drivers/video/fbdev/omap2/dss/dispc.c')
-rw-r--r-- | drivers/video/fbdev/omap2/dss/dispc.c | 156 |
1 files changed, 129 insertions, 27 deletions
diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index f4fc77d9d3bf..be716c9ffb88 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -39,6 +39,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/of.h> +#include <linux/component.h> #include <video/omapdss.h> @@ -95,6 +96,9 @@ struct dispc_features { bool mstandby_workaround:1; bool set_max_preload:1; + + /* PIXEL_INC is not added to the last pixel of a line */ + bool last_pixel_inc_missing:1; }; #define DISPC_MAX_NR_FIFOS 5 @@ -1741,6 +1745,15 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation, row_repeat = false; } + /* + * OMAP4/5 Errata i631: + * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra + * rows beyond the framebuffer, which may cause OCP error. + */ + if (color_mode == OMAP_DSS_COLOR_NV12 && + rotation_type != OMAP_DSS_ROT_TILER) + vidrot = 1; + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); if (dss_has_feature(FEAT_ROWREPEATENABLE)) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), @@ -2154,7 +2167,7 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk, if (height > out_height) { unsigned int ppl = mgr_timings->x_res; - tmp = pclk * height * out_width; + tmp = (u64)pclk * height * out_width; do_div(tmp, 2 * out_height * ppl); core_clk = tmp; @@ -2162,14 +2175,14 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk, if (ppl == out_width) return 0; - tmp = pclk * (height - 2 * out_height) * out_width; + tmp = (u64)pclk * (height - 2 * out_height) * out_width; do_div(tmp, 2 * out_height * (ppl - out_width)); core_clk = max_t(u32, core_clk, tmp); } } if (width > out_width) { - tmp = pclk * width; + tmp = (u64)pclk * width; do_div(tmp, out_width); core_clk = max_t(u32, core_clk, tmp); @@ -2267,6 +2280,11 @@ static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, } } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); + if (error) { + DSSERR("failed to find scaling settings\n"); + return -EINVAL; + } + if (in_width > maxsinglelinewidth) { DSSERR("Cannot scale max input width exceeded"); return -EINVAL; @@ -2283,7 +2301,6 @@ static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk, { int error; u16 in_width, in_height; - int min_factor = min(*decim_x, *decim_y); const int maxsinglelinewidth = dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); @@ -2317,20 +2334,32 @@ again: error = (error || in_width > maxsinglelinewidth * 2 || (in_width > maxsinglelinewidth && *five_taps) || !*core_clk || *core_clk > dispc_core_clk_rate()); - if (error) { - if (*decim_x == *decim_y) { - *decim_x = min_factor; - ++*decim_y; + + if (!error) { + /* verify that we're inside the limits of scaler */ + if (in_width / 4 > out_width) + error = 1; + + if (*five_taps) { + if (in_height / 4 > out_height) + error = 1; } else { - swap(*decim_x, *decim_y); - if (*decim_x < *decim_y) - ++*decim_x; + if (in_height / 2 > out_height) + error = 1; } } + + if (error) + ++*decim_y; } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); - if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, width, - height, out_width, out_height, *five_taps)) { + if (error) { + DSSERR("failed to find scaling settings\n"); + return -EINVAL; + } + + if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, in_width, + in_height, out_width, out_height, *five_taps)) { DSSERR("horizontal timing too tight\n"); return -EINVAL; } @@ -2390,6 +2419,9 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, return 0; } +#define DIV_FRAC(dividend, divisor) \ + ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100)) + static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, enum omap_overlay_caps caps, const struct omap_video_timings *mgr_timings, @@ -2449,8 +2481,19 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, if (ret) return ret; - DSSDBG("required core clk rate = %lu Hz\n", core_clk); - DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate()); + DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n", + width, height, + out_width, out_height, + out_width / width, DIV_FRAC(out_width, width), + out_height / height, DIV_FRAC(out_height, height), + + decim_x, decim_y, + width / decim_x, height / decim_y, + out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x), + out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y), + + *five_taps ? 5 : 3, + core_clk, dispc_core_clk_rate()); if (!core_clk || core_clk > dispc_core_clk_rate()) { DSSERR("failed to set up scaling, " @@ -2533,6 +2576,21 @@ static int dispc_ovl_setup_common(enum omap_plane plane, if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER) return -EINVAL; + switch (color_mode) { + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_NV12: + if (in_width & 1) { + DSSERR("input width %d is not even for YUV format\n", + in_width); + return -EINVAL; + } + break; + + default: + break; + } + out_width = out_width == 0 ? width : out_width; out_height = out_height == 0 ? height : out_height; @@ -2563,6 +2621,27 @@ static int dispc_ovl_setup_common(enum omap_plane plane, in_width = in_width / x_predecim; in_height = in_height / y_predecim; + if (x_predecim > 1 || y_predecim > 1) + DSSDBG("predecimation %d x %x, new input size %d x %d\n", + x_predecim, y_predecim, in_width, in_height); + + switch (color_mode) { + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_NV12: + if (in_width & 1) { + DSSDBG("predecimated input width is not even for YUV format\n"); + DSSDBG("adjusting input width %d -> %d\n", + in_width, in_width & ~1); + + in_width &= ~1; + } + break; + + default: + break; + } + if (color_mode == OMAP_DSS_COLOR_YUV2 || color_mode == OMAP_DSS_COLOR_UYVY || color_mode == OMAP_DSS_COLOR_NV12) @@ -2632,6 +2711,9 @@ static int dispc_ovl_setup_common(enum omap_plane plane, dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1); } + if (dispc.feat->last_pixel_inc_missing) + row_inc += pix_inc - 1; + dispc_ovl_set_row_inc(plane, row_inc); dispc_ovl_set_pix_inc(plane, pix_inc); @@ -3692,7 +3774,7 @@ static void _omap_dispc_initial_config(void) dispc_init_mflag(); } -static const struct dispc_features omap24xx_dispc_feats __initconst = { +static const struct dispc_features omap24xx_dispc_feats = { .sw_start = 5, .fp_start = 15, .bp_start = 27, @@ -3709,9 +3791,10 @@ static const struct dispc_features omap24xx_dispc_feats __initconst = { .num_fifos = 3, .no_framedone_tv = true, .set_max_preload = false, + .last_pixel_inc_missing = true, }; -static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = { +static const struct dispc_features omap34xx_rev1_0_dispc_feats = { .sw_start = 5, .fp_start = 15, .bp_start = 27, @@ -3729,9 +3812,10 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = { .num_fifos = 3, .no_framedone_tv = true, .set_max_preload = false, + .last_pixel_inc_missing = true, }; -static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = { +static const struct dispc_features omap34xx_rev3_0_dispc_feats = { .sw_start = 7, .fp_start = 19, .bp_start = 31, @@ -3749,9 +3833,10 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = { .num_fifos = 3, .no_framedone_tv = true, .set_max_preload = false, + .last_pixel_inc_missing = true, }; -static const struct dispc_features omap44xx_dispc_feats __initconst = { +static const struct dispc_features omap44xx_dispc_feats = { .sw_start = 7, .fp_start = 19, .bp_start = 31, @@ -3771,7 +3856,7 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = { .set_max_preload = true, }; -static const struct dispc_features omap54xx_dispc_feats __initconst = { +static const struct dispc_features omap54xx_dispc_feats = { .sw_start = 7, .fp_start = 19, .bp_start = 31, @@ -3792,7 +3877,7 @@ static const struct dispc_features omap54xx_dispc_feats __initconst = { .set_max_preload = true, }; -static int __init dispc_init_features(struct platform_device *pdev) +static int dispc_init_features(struct platform_device *pdev) { const struct dispc_features *src; struct dispc_features *dst; @@ -3882,8 +3967,9 @@ void dispc_free_irq(void *dev_id) EXPORT_SYMBOL(dispc_free_irq); /* DISPC HW IP initialisation */ -static int __init omap_dispchw_probe(struct platform_device *pdev) +static int dispc_bind(struct device *dev, struct device *master, void *data) { + struct platform_device *pdev = to_platform_device(dev); u32 rev; int r = 0; struct resource *dispc_mem; @@ -3955,12 +4041,27 @@ err_runtime_get: return r; } -static int __exit omap_dispchw_remove(struct platform_device *pdev) +static void dispc_unbind(struct device *dev, struct device *master, + void *data) { - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); dss_uninit_overlay_managers(); +} + +static const struct component_ops dispc_component_ops = { + .bind = dispc_bind, + .unbind = dispc_unbind, +}; +static int dispc_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dispc_component_ops); +} + +static int dispc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dispc_component_ops); return 0; } @@ -4013,7 +4114,8 @@ static const struct of_device_id dispc_of_match[] = { }; static struct platform_driver omap_dispchw_driver = { - .remove = __exit_p(omap_dispchw_remove), + .probe = dispc_probe, + .remove = dispc_remove, .driver = { .name = "omapdss_dispc", .pm = &dispc_pm_ops, @@ -4024,10 +4126,10 @@ static struct platform_driver omap_dispchw_driver = { int __init dispc_init_platform_driver(void) { - return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe); + return platform_driver_register(&omap_dispchw_driver); } -void __exit dispc_uninit_platform_driver(void) +void dispc_uninit_platform_driver(void) { platform_driver_unregister(&omap_dispchw_driver); } |