diff options
Diffstat (limited to 'drivers/video/omap2/dss/dispc.c')
-rw-r--r-- | drivers/video/omap2/dss/dispc.c | 1019 |
1 files changed, 712 insertions, 307 deletions
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 5b289c5f695b..a173a9481a23 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -37,8 +37,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <plat/clock.h> - #include <video/omapdss.h> #include "dss.h" @@ -81,6 +79,30 @@ struct dispc_irq_stats { unsigned irqs[32]; }; +struct dispc_features { + u8 sw_start; + u8 fp_start; + u8 bp_start; + u16 sw_max; + u16 vp_max; + u16 hp_max; + int (*calc_scaling) (enum omap_plane plane, + const struct omap_video_timings *mgr_timings, + 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 *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, bool mem_to_mem); + unsigned long (*calc_core_clk) (enum omap_plane plane, + u16 width, u16 height, u16 out_width, u16 out_height, + bool mem_to_mem); + u8 num_fifos; + + /* swap GFX & WB fifos */ + bool gfx_fifo_workaround:1; +}; + +#define DISPC_MAX_NR_FIFOS 5 + static struct { struct platform_device *pdev; void __iomem *base; @@ -90,7 +112,9 @@ static struct { int irq; struct clk *dss_clk; - u32 fifo_size[MAX_DSS_OVERLAYS]; + u32 fifo_size[DISPC_MAX_NR_FIFOS]; + /* maps which plane is using a fifo. fifo-id -> plane-id */ + int fifo_assignment[DISPC_MAX_NR_FIFOS]; spinlock_t irq_lock; u32 irq_error_mask; @@ -101,6 +125,8 @@ static struct { bool ctx_valid; u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; + const struct dispc_features *feat; + #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS spinlock_t irq_stats_lock; struct dispc_irq_stats irq_stats; @@ -210,7 +236,14 @@ static const struct { }, }; +struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; +}; + static void _omap_dispc_set_irqs(void); +static unsigned long dispc_plane_pclk_rate(enum omap_plane plane); +static unsigned long dispc_plane_lclk_rate(enum omap_plane plane); static inline void dispc_write_reg(const u16 idx, u32 val) { @@ -508,6 +541,11 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) return mgr_desc[channel].framedone_irq; } +u32 dispc_wb_get_framedone_irq(void) +{ + return DISPC_IRQ_FRAMEDONEWB; +} + bool dispc_mgr_go_busy(enum omap_channel channel) { return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1; @@ -535,6 +573,30 @@ void dispc_mgr_go(enum omap_channel channel) mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1); } +bool dispc_wb_go_busy(void) +{ + return REG_GET(DISPC_CONTROL2, 6, 6) == 1; +} + +void dispc_wb_go(void) +{ + enum omap_plane plane = OMAP_DSS_WB; + bool enable, go; + + enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1; + + if (!enable) + return; + + go = REG_GET(DISPC_CONTROL2, 6, 6) == 1; + if (go) { + DSSERR("GO bit not down for WB\n"); + return; + } + + REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6); +} + static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value) { dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); @@ -617,41 +679,41 @@ static void dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc, } } -static void _dispc_setup_color_conv_coef(void) -{ - int i; - const struct color_conv_coef { - int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; - int full_range; - } ctbl_bt601_5 = { - 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, - }; - - const struct color_conv_coef *ct; +static void dispc_ovl_write_color_conv_coef(enum omap_plane plane, + const struct color_conv_coef *ct) +{ #define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) - ct = &ctbl_bt601_5; + dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb)); - for (i = 1; i < dss_feat_get_num_ovls(); i++) { - dispc_write_reg(DISPC_OVL_CONV_COEF(i, 0), - CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_OVL_CONV_COEF(i, 1), - CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_OVL_CONV_COEF(i, 2), - CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_OVL_CONV_COEF(i, 3), - CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_OVL_CONV_COEF(i, 4), - CVAL(0, ct->bcb)); - - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), ct->full_range, - 11, 11); - } + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11); #undef CVAL } +static void dispc_setup_color_conv_coef(void) +{ + int i; + int num_ovl = dss_feat_get_num_ovls(); + int num_wb = dss_feat_get_num_wbs(); + const struct color_conv_coef ctbl_bt601_5_ovl = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + const struct color_conv_coef ctbl_bt601_5_wb = { + 66, 112, -38, 129, -94, -74, 25, -18, 112, 0, + }; + + for (i = 1; i < num_ovl; i++) + dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl); + + for (; i < num_wb; i++) + dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_wb); +} static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr) { @@ -673,24 +735,32 @@ static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr) dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); } -static void dispc_ovl_set_pos(enum omap_plane plane, int x, int y) +static void dispc_ovl_set_pos(enum omap_plane plane, + enum omap_overlay_caps caps, int x, int y) { - u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + u32 val; + + if ((caps & OMAP_DSS_OVL_CAP_POS) == 0) + return; + + val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); dispc_write_reg(DISPC_OVL_POSITION(plane), val); } -static void dispc_ovl_set_pic_size(enum omap_plane plane, int width, int height) +static void dispc_ovl_set_input_size(enum omap_plane plane, int width, + int height) { u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - if (plane == OMAP_DSS_GFX) + if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB) dispc_write_reg(DISPC_OVL_SIZE(plane), val); else dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); } -static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height) +static void dispc_ovl_set_output_size(enum omap_plane plane, int width, + int height) { u32 val; @@ -698,14 +768,16 @@ static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height) val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_OVL_SIZE(plane), val); + if (plane == OMAP_DSS_WB) + dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); + else + dispc_write_reg(DISPC_OVL_SIZE(plane), val); } -static void dispc_ovl_set_zorder(enum omap_plane plane, u8 zorder) +static void dispc_ovl_set_zorder(enum omap_plane plane, + enum omap_overlay_caps caps, u8 zorder) { - struct omap_overlay *ovl = omap_dss_get_overlay(plane); - - if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) + if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) return; REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26); @@ -722,23 +794,22 @@ static void dispc_ovl_enable_zorder_planes(void) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25); } -static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, bool enable) +static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, + enum omap_overlay_caps caps, bool enable) { - struct omap_overlay *ovl = omap_dss_get_overlay(plane); - - if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) + if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) return; REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); } -static void dispc_ovl_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +static void dispc_ovl_setup_global_alpha(enum omap_plane plane, + enum omap_overlay_caps caps, u8 global_alpha) { static const unsigned shifts[] = { 0, 8, 16, 24, }; int shift; - struct omap_overlay *ovl = omap_dss_get_overlay(plane); - if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) + if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) return; shift = shifts[plane]; @@ -946,10 +1017,17 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) return channel; } +void dispc_wb_set_channel_in(enum dss_writeback_channel channel) +{ + enum omap_plane plane = OMAP_DSS_WB; + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16); +} + static void dispc_ovl_set_burst_size(enum omap_plane plane, enum omap_burst_size burst_size) { - static const unsigned shifts[] = { 6, 14, 14, 14, }; + static const unsigned shifts[] = { 6, 14, 14, 14, 14, }; int shift; shift = shifts[plane]; @@ -1026,11 +1104,15 @@ static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable) dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } -static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable) +static void dispc_ovl_enable_replication(enum omap_plane plane, + enum omap_overlay_caps caps, bool enable) { static const unsigned shifts[] = { 5, 10, 10, 10 }; int shift; + if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0) + return; + shift = shifts[plane]; REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } @@ -1044,10 +1126,10 @@ static void dispc_mgr_set_size(enum omap_channel channel, u16 width, dispc_write_reg(DISPC_SIZE_MGR(channel), val); } -static void dispc_read_plane_fifo_sizes(void) +static void dispc_init_fifos(void) { u32 size; - int plane; + int fifo; u8 start, end; u32 unit; @@ -1055,16 +1137,53 @@ static void dispc_read_plane_fifo_sizes(void) dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); - for (plane = 0; plane < dss_feat_get_num_ovls(); ++plane) { - size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end); + for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { + size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end); size *= unit; - dispc.fifo_size[plane] = size; + dispc.fifo_size[fifo] = size; + + /* + * By default fifos are mapped directly to overlays, fifo 0 to + * ovl 0, fifo 1 to ovl 1, etc. + */ + dispc.fifo_assignment[fifo] = fifo; + } + + /* + * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo + * causes problems with certain use cases, like using the tiler in 2D + * mode. The below hack swaps the fifos of GFX and WB planes, thus + * giving GFX plane a larger fifo. WB but should work fine with a + * smaller fifo. + */ + if (dispc.feat->gfx_fifo_workaround) { + u32 v; + + v = dispc_read_reg(DISPC_GLOBAL_BUFFER); + + v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */ + v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */ + v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */ + v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */ + + dispc_write_reg(DISPC_GLOBAL_BUFFER, v); + + dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB; + dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX; } } static u32 dispc_ovl_get_fifo_size(enum omap_plane plane) { - return dispc.fifo_size[plane]; + int fifo; + u32 size = 0; + + for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { + if (dispc.fifo_assignment[fifo] == plane) + size += dispc.fifo_size[fifo]; + } + + return size; } void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) @@ -1140,6 +1259,14 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { *fifo_low = ovl_fifo_size - burst_size * 2; *fifo_high = total_fifo_size - burst_size; + } else if (plane == OMAP_DSS_WB) { + /* + * Most optimal configuration for writeback is to push out data + * to the interconnect the moment writeback pushes enough pixels + * in the FIFO to form a burst + */ + *fifo_low = 0; + *fifo_high = burst_size; } else { *fifo_low = ovl_fifo_size - burst_size; *fifo_high = total_fifo_size - buf_unit; @@ -1382,6 +1509,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, { int scale_x = out_width != orig_width; int scale_y = out_height != orig_height; + bool chroma_upscale = plane != OMAP_DSS_WB ? true : false; if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) return; @@ -1389,7 +1517,8 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, color_mode != OMAP_DSS_COLOR_UYVY && color_mode != OMAP_DSS_COLOR_NV12)) { /* reset chroma resampling for RGB formats */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); + if (plane != OMAP_DSS_WB) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); return; } @@ -1398,23 +1527,34 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, switch (color_mode) { case OMAP_DSS_COLOR_NV12: - /* UV is subsampled by 2 vertically*/ - orig_height >>= 1; - /* UV is subsampled by 2 horz.*/ - orig_width >>= 1; + if (chroma_upscale) { + /* UV is subsampled by 2 horizontally and vertically */ + orig_height >>= 1; + orig_width >>= 1; + } else { + /* UV is downsampled by 2 horizontally and vertically */ + orig_height <<= 1; + orig_width <<= 1; + } + break; case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: - /*For YUV422 with 90/270 rotation, - *we don't upsample chroma - */ + /* For YUV422 with 90/270 rotation, we don't upsample chroma */ if (rotation == OMAP_DSS_ROT_0 || - rotation == OMAP_DSS_ROT_180) - /* UV is subsampled by 2 hrz*/ - orig_width >>= 1; + rotation == OMAP_DSS_ROT_180) { + if (chroma_upscale) + /* UV is subsampled by 2 horizontally */ + orig_width >>= 1; + else + /* UV is downsampled by 2 horizontally */ + orig_width <<= 1; + } + /* must use FIR for YUV422 if rotated */ if (rotation != OMAP_DSS_ROT_0) scale_x = scale_y = true; + break; default: BUG(); @@ -1430,8 +1570,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, out_width, out_height, five_taps, rotation, DISPC_COLOR_COMPONENT_UV); - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), - (scale_x || scale_y) ? 1 : 0, 8, 8); + if (plane != OMAP_DSS_WB) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), + (scale_x || scale_y) ? 1 : 0, 8, 8); + /* set H scaling */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); /* set V scaling */ @@ -1847,22 +1989,19 @@ static void calc_tiler_rotation_offset(u16 screen_width, u16 width, * 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, +static int check_horiz_timing_omap3(enum omap_plane plane, const struct omap_video_timings *t, u16 pos_x, u16 width, u16 height, u16 out_width, u16 out_height) { int DS = DIV_ROUND_UP(height, out_height); - unsigned long nonactive, lclk, pclk; + unsigned long nonactive; static const u8 limits[3] = { 8, 10, 20 }; u64 val, blank; + unsigned long pclk = dispc_plane_pclk_rate(plane); + unsigned long lclk = dispc_plane_lclk_rate(plane); int i; nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width; - pclk = dispc_mgr_pclk_rate(channel); - if (dss_mgr_is_lcd(channel)) - lclk = dispc_mgr_lclk_rate(channel); - else - lclk = dispc_fclk_rate(); i = 0; if (out_height < height) @@ -1899,13 +2038,14 @@ static int check_horiz_timing_omap3(enum omap_channel channel, return 0; } -static unsigned long calc_core_clk_five_taps(enum omap_channel channel, +static unsigned long calc_core_clk_five_taps(enum omap_plane plane, const struct omap_video_timings *mgr_timings, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode) { u32 core_clk = 0; - u64 tmp, pclk = dispc_mgr_pclk_rate(channel); + u64 tmp; + unsigned long pclk = dispc_plane_pclk_rate(plane); if (height <= out_height && width <= out_width) return (unsigned long) pclk; @@ -1939,11 +2079,22 @@ static unsigned long calc_core_clk_five_taps(enum omap_channel channel, return core_clk; } -static unsigned long calc_core_clk(enum omap_channel channel, u16 width, - u16 height, u16 out_width, u16 out_height) +static unsigned long calc_core_clk_24xx(enum omap_plane plane, u16 width, + u16 height, u16 out_width, u16 out_height, bool mem_to_mem) +{ + unsigned long pclk = dispc_plane_pclk_rate(plane); + + if (height > out_height && width > out_width) + return pclk * 4; + else + return pclk * 2; +} + +static unsigned long calc_core_clk_34xx(enum omap_plane plane, u16 width, + u16 height, u16 out_width, u16 out_height, bool mem_to_mem) { unsigned int hf, vf; - unsigned long pclk = dispc_mgr_pclk_rate(channel); + unsigned long pclk = dispc_plane_pclk_rate(plane); /* * FIXME how to determine the 'A' factor @@ -1958,51 +2109,207 @@ static unsigned long calc_core_clk(enum omap_channel channel, u16 width, hf = 2; else hf = 1; - if (height > out_height) vf = 2; else vf = 1; - if (cpu_is_omap24xx()) { - if (vf > 1 && hf > 1) - return pclk * 4; - else - return pclk * 2; - } else if (cpu_is_omap34xx()) { - return pclk * vf * hf; - } else { - if (hf > 1) - return DIV_ROUND_UP(pclk, out_width) * width; - else - return pclk; + return pclk * vf * hf; +} + +static unsigned long calc_core_clk_44xx(enum omap_plane plane, u16 width, + u16 height, u16 out_width, u16 out_height, bool mem_to_mem) +{ + unsigned long pclk; + + /* + * If the overlay/writeback is in mem to mem mode, there are no + * downscaling limitations with respect to pixel clock, return 1 as + * required core clock to represent that we have sufficient enough + * core clock to do maximum downscaling + */ + if (mem_to_mem) + return 1; + + pclk = dispc_plane_pclk_rate(plane); + + if (width > out_width) + return DIV_ROUND_UP(pclk, out_width) * width; + else + return pclk; +} + +static int dispc_ovl_calc_scaling_24xx(enum omap_plane plane, + const struct omap_video_timings *mgr_timings, + 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 *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, bool mem_to_mem) +{ + 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); + + *five_taps = false; + + do { + in_height = DIV_ROUND_UP(height, *decim_y); + in_width = DIV_ROUND_UP(width, *decim_x); + *core_clk = dispc.feat->calc_core_clk(plane, in_width, + in_height, out_width, out_height, mem_to_mem); + error = (in_width > maxsinglelinewidth || !*core_clk || + *core_clk > dispc_core_clk_rate()); + if (error) { + if (*decim_x == *decim_y) { + *decim_x = min_factor; + ++*decim_y; + } else { + swap(*decim_x, *decim_y); + if (*decim_x < *decim_y) + ++*decim_x; + } + } + } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); + + if (in_width > maxsinglelinewidth) { + DSSERR("Cannot scale max input width exceeded"); + return -EINVAL; } + return 0; } -static int dispc_ovl_calc_scaling(enum omap_plane plane, - enum omap_channel channel, +static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane, const struct omap_video_timings *mgr_timings, 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, u16 pos_x) + int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, bool mem_to_mem) { - struct omap_overlay *ovl = omap_dss_get_overlay(plane); - const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); + 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); + + do { + in_height = DIV_ROUND_UP(height, *decim_y); + in_width = DIV_ROUND_UP(width, *decim_x); + *core_clk = calc_core_clk_five_taps(plane, mgr_timings, + in_width, in_height, out_width, out_height, color_mode); + + error = check_horiz_timing_omap3(plane, mgr_timings, + pos_x, in_width, in_height, out_width, + out_height); + + if (in_width > maxsinglelinewidth) + if (in_height > out_height && + in_height < out_height * 2) + *five_taps = false; + if (!*five_taps) + *core_clk = dispc.feat->calc_core_clk(plane, in_width, + in_height, out_width, out_height, + mem_to_mem); + + 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; + } else { + swap(*decim_x, *decim_y); + if (*decim_x < *decim_y) + ++*decim_x; + } + } + } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); + + if (check_horiz_timing_omap3(plane, mgr_timings, 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"); + return -EINVAL; + } + + if (in_width > maxsinglelinewidth && *five_taps) { + DSSERR("cannot setup scaling with five taps"); + return -EINVAL; + } + return 0; +} + +static int dispc_ovl_calc_scaling_44xx(enum omap_plane plane, + const struct omap_video_timings *mgr_timings, + 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 *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, bool mem_to_mem) +{ + u16 in_width, in_width_max; + int decim_x_min = *decim_x; + u16 in_height = DIV_ROUND_UP(height, *decim_y); const int maxsinglelinewidth = dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); + unsigned long pclk = dispc_plane_pclk_rate(plane); + const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); + + if (mem_to_mem) + in_width_max = DIV_ROUND_UP(out_width, maxdownscale); + else + in_width_max = dispc_core_clk_rate() / + DIV_ROUND_UP(pclk, out_width); + + *decim_x = DIV_ROUND_UP(width, in_width_max); + + *decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min; + if (*decim_x > *x_predecim) + return -EINVAL; + + do { + in_width = DIV_ROUND_UP(width, *decim_x); + } while (*decim_x <= *x_predecim && + in_width > maxsinglelinewidth && ++*decim_x); + + if (in_width > maxsinglelinewidth) { + DSSERR("Cannot scale width exceeds max line width"); + return -EINVAL; + } + + *core_clk = dispc.feat->calc_core_clk(plane, in_width, in_height, + out_width, out_height, mem_to_mem); + return 0; +} + +static int dispc_ovl_calc_scaling(enum omap_plane plane, + enum omap_overlay_caps caps, + const struct omap_video_timings *mgr_timings, + 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, u16 pos_x, + enum omap_dss_rotation_type rotation_type, bool mem_to_mem) +{ + const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); const int max_decim_limit = 16; unsigned long core_clk = 0; - int decim_x, decim_y, error, min_factor; - u16 in_width, in_height, in_width_max = 0; + int decim_x, decim_y, ret; if (width == out_width && height == out_height) return 0; - if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) + if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0) return -EINVAL; *x_predecim = max_decim_limit; - *y_predecim = max_decim_limit; + *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER && + dss_has_feature(FEAT_BURST_2D)) ? 2 : max_decim_limit; if (color_mode == OMAP_DSS_COLOR_CLUT1 || color_mode == OMAP_DSS_COLOR_CLUT2 || @@ -2017,118 +2324,18 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); - min_factor = min(decim_x, decim_y); - if (decim_x > *x_predecim || out_width > width * 8) return -EINVAL; if (decim_y > *y_predecim || out_height > height * 8) return -EINVAL; - if (cpu_is_omap24xx()) { - *five_taps = false; - - do { - in_height = DIV_ROUND_UP(height, decim_y); - in_width = DIV_ROUND_UP(width, decim_x); - core_clk = calc_core_clk(channel, in_width, in_height, - out_width, out_height); - error = (in_width > maxsinglelinewidth || !core_clk || - core_clk > dispc_core_clk_rate()); - if (error) { - if (decim_x == decim_y) { - decim_x = min_factor; - decim_y++; - } else { - swap(decim_x, decim_y); - if (decim_x < decim_y) - decim_x++; - } - } - } while (decim_x <= *x_predecim && decim_y <= *y_predecim && - error); - - if (in_width > maxsinglelinewidth) { - DSSERR("Cannot scale max input width exceeded"); - return -EINVAL; - } - } else if (cpu_is_omap34xx()) { - - do { - in_height = DIV_ROUND_UP(height, decim_y); - in_width = DIV_ROUND_UP(width, decim_x); - core_clk = calc_core_clk_five_taps(channel, mgr_timings, - in_width, in_height, out_width, out_height, - color_mode); - - error = check_horiz_timing_omap3(channel, mgr_timings, - pos_x, in_width, in_height, out_width, - out_height); - - if (in_width > maxsinglelinewidth) - if (in_height > out_height && - in_height < out_height * 2) - *five_taps = false; - if (!*five_taps) - core_clk = calc_core_clk(channel, in_width, - in_height, out_width, out_height); - 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++; - } else { - swap(decim_x, decim_y); - if (decim_x < decim_y) - decim_x++; - } - } - } while (decim_x <= *x_predecim && decim_y <= *y_predecim - && error); - - if (check_horiz_timing_omap3(channel, mgr_timings, 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"); - return -EINVAL; - } - - if (in_width > maxsinglelinewidth && *five_taps) { - DSSERR("cannot setup scaling with five taps"); - return -EINVAL; - } - } else { - int decim_x_min = decim_x; - in_height = DIV_ROUND_UP(height, decim_y); - in_width_max = dispc_core_clk_rate() / - DIV_ROUND_UP(dispc_mgr_pclk_rate(channel), - out_width); - decim_x = DIV_ROUND_UP(width, in_width_max); - - decim_x = decim_x > decim_x_min ? decim_x : decim_x_min; - if (decim_x > *x_predecim) - return -EINVAL; - - do { - in_width = DIV_ROUND_UP(width, decim_x); - } while (decim_x <= *x_predecim && - in_width > maxsinglelinewidth && decim_x++); - - if (in_width > maxsinglelinewidth) { - DSSERR("Cannot scale width exceeds max line width"); - return -EINVAL; - } - - core_clk = calc_core_clk(channel, in_width, in_height, - out_width, out_height); - } + ret = dispc.feat->calc_scaling(plane, mgr_timings, width, height, + out_width, out_height, color_mode, five_taps, + x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk, + mem_to_mem); + 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()); @@ -2146,69 +2353,64 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, return 0; } -int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool replication, const struct omap_video_timings *mgr_timings) +static int dispc_ovl_setup_common(enum omap_plane plane, + enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr, + u16 screen_width, int pos_x, int pos_y, u16 width, u16 height, + u16 out_width, u16 out_height, enum omap_color_mode color_mode, + u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha, + u8 global_alpha, enum omap_dss_rotation_type rotation_type, + bool replication, const struct omap_video_timings *mgr_timings, + bool mem_to_mem) { - struct omap_overlay *ovl = omap_dss_get_overlay(plane); bool five_taps = true; bool fieldmode = 0; int r, cconv = 0; unsigned offset0, offset1; s32 row_inc; s32 pix_inc; - u16 frame_height = oi->height; + u16 frame_height = height; unsigned int field_offset = 0; - u16 in_height = oi->height; - u16 in_width = oi->width; - u16 out_width, out_height; - enum omap_channel channel; + u16 in_height = height; + u16 in_width = width; int x_predecim = 1, y_predecim = 1; bool ilace = mgr_timings->interlace; - channel = dispc_ovl_get_channel_out(plane); - - DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> " - "%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d\n", - plane, oi->paddr, oi->p_uv_addr, - oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, - oi->out_width, oi->out_height, oi->color_mode, oi->rotation, - oi->mirror, ilace, channel, replication); - - if (oi->paddr == 0) + if (paddr == 0) return -EINVAL; - out_width = oi->out_width == 0 ? oi->width : oi->out_width; - out_height = oi->out_height == 0 ? oi->height : oi->out_height; + out_width = out_width == 0 ? width : out_width; + out_height = out_height == 0 ? height : out_height; - if (ilace && oi->height == out_height) + if (ilace && height == out_height) fieldmode = 1; if (ilace) { if (fieldmode) in_height /= 2; - oi->pos_y /= 2; + pos_y /= 2; out_height /= 2; DSSDBG("adjusting for ilace: height %d, pos_y %d, " - "out_height %d\n", - in_height, oi->pos_y, out_height); + "out_height %d\n", in_height, pos_y, + out_height); } - if (!dss_feat_color_mode_supported(plane, oi->color_mode)) + if (!dss_feat_color_mode_supported(plane, color_mode)) return -EINVAL; - r = dispc_ovl_calc_scaling(plane, channel, mgr_timings, in_width, - in_height, out_width, out_height, oi->color_mode, - &five_taps, &x_predecim, &y_predecim, oi->pos_x); + r = dispc_ovl_calc_scaling(plane, caps, mgr_timings, in_width, + in_height, out_width, out_height, color_mode, + &five_taps, &x_predecim, &y_predecim, pos_x, + rotation_type, mem_to_mem); if (r) return r; in_width = DIV_ROUND_UP(in_width, x_predecim); in_height = DIV_ROUND_UP(in_height, y_predecim); - if (oi->color_mode == OMAP_DSS_COLOR_YUV2 || - oi->color_mode == OMAP_DSS_COLOR_UYVY || - oi->color_mode == OMAP_DSS_COLOR_NV12) + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY || + color_mode == OMAP_DSS_COLOR_NV12) cconv = 1; if (ilace && !fieldmode) { @@ -2234,70 +2436,144 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, row_inc = 0; pix_inc = 0; - if (oi->rotation_type == OMAP_DSS_ROT_TILER) - calc_tiler_rotation_offset(oi->screen_width, in_width, - oi->color_mode, fieldmode, field_offset, + if (rotation_type == OMAP_DSS_ROT_TILER) + calc_tiler_rotation_offset(screen_width, in_width, + color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc, x_predecim, y_predecim); - else if (oi->rotation_type == OMAP_DSS_ROT_DMA) - calc_dma_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, in_width, frame_height, - oi->color_mode, fieldmode, field_offset, + else if (rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(rotation, mirror, + screen_width, in_width, frame_height, + color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc, x_predecim, y_predecim); else - calc_vrfb_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, in_width, frame_height, - oi->color_mode, fieldmode, field_offset, + calc_vrfb_rotation_offset(rotation, mirror, + screen_width, in_width, frame_height, + color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc, x_predecim, y_predecim); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); - dispc_ovl_set_color_mode(plane, oi->color_mode); + dispc_ovl_set_color_mode(plane, color_mode); - dispc_ovl_configure_burst_type(plane, oi->rotation_type); + dispc_ovl_configure_burst_type(plane, rotation_type); - dispc_ovl_set_ba0(plane, oi->paddr + offset0); - dispc_ovl_set_ba1(plane, oi->paddr + offset1); + dispc_ovl_set_ba0(plane, paddr + offset0); + dispc_ovl_set_ba1(plane, paddr + offset1); - if (OMAP_DSS_COLOR_NV12 == oi->color_mode) { - dispc_ovl_set_ba0_uv(plane, oi->p_uv_addr + offset0); - dispc_ovl_set_ba1_uv(plane, oi->p_uv_addr + offset1); + if (OMAP_DSS_COLOR_NV12 == color_mode) { + dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0); + dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1); } - dispc_ovl_set_row_inc(plane, row_inc); dispc_ovl_set_pix_inc(plane, pix_inc); - DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, in_width, + DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width, in_height, out_width, out_height); - dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y); + dispc_ovl_set_pos(plane, caps, pos_x, pos_y); - dispc_ovl_set_pic_size(plane, in_width, in_height); + dispc_ovl_set_input_size(plane, in_width, in_height); - if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) { + if (caps & OMAP_DSS_OVL_CAP_SCALE) { dispc_ovl_set_scaling(plane, in_width, in_height, out_width, out_height, ilace, five_taps, fieldmode, - oi->color_mode, oi->rotation); - dispc_ovl_set_vid_size(plane, out_width, out_height); + color_mode, rotation); + dispc_ovl_set_output_size(plane, out_width, out_height); dispc_ovl_set_vid_color_conv(plane, cconv); } - dispc_ovl_set_rotation_attrs(plane, oi->rotation, oi->mirror, - oi->color_mode); + dispc_ovl_set_rotation_attrs(plane, rotation, mirror, color_mode); - dispc_ovl_set_zorder(plane, oi->zorder); - dispc_ovl_set_pre_mult_alpha(plane, oi->pre_mult_alpha); - dispc_ovl_setup_global_alpha(plane, oi->global_alpha); + dispc_ovl_set_zorder(plane, caps, zorder); + dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha); + dispc_ovl_setup_global_alpha(plane, caps, global_alpha); - dispc_ovl_enable_replication(plane, replication); + dispc_ovl_enable_replication(plane, caps, replication); return 0; } +int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi, + bool replication, const struct omap_video_timings *mgr_timings, + bool mem_to_mem) +{ + int r; + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + enum omap_channel channel; + + channel = dispc_ovl_get_channel_out(plane); + + DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n", + plane, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x, + oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, + oi->color_mode, oi->rotation, oi->mirror, channel, replication); + + r = dispc_ovl_setup_common(plane, ovl->caps, oi->paddr, oi->p_uv_addr, + oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, + oi->out_width, oi->out_height, oi->color_mode, oi->rotation, + oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha, + oi->rotation_type, replication, mgr_timings, mem_to_mem); + + return r; +} + +int dispc_wb_setup(const struct omap_dss_writeback_info *wi, + bool mem_to_mem, const struct omap_video_timings *mgr_timings) +{ + int r; + u32 l; + enum omap_plane plane = OMAP_DSS_WB; + const int pos_x = 0, pos_y = 0; + const u8 zorder = 0, global_alpha = 0; + const bool replication = false; + bool truncation; + int in_width = mgr_timings->x_res; + int in_height = mgr_timings->y_res; + enum omap_overlay_caps caps = + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA; + + DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, " + "rot %d, mir %d\n", wi->paddr, wi->p_uv_addr, in_width, + in_height, wi->width, wi->height, wi->color_mode, wi->rotation, + wi->mirror); + + r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr, + wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, + wi->height, wi->color_mode, wi->rotation, wi->mirror, zorder, + wi->pre_mult_alpha, global_alpha, wi->rotation_type, + replication, mgr_timings, mem_to_mem); + + switch (wi->color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_RGBA16: + case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_XRGB16_1555: + case OMAP_DSS_COLOR_RGBX16: + truncation = true; + break; + default: + truncation = false; + break; + } + + /* setup extra DISPC_WB_ATTRIBUTES */ + l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); + l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */ + l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */ + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); + + return r; +} + int dispc_ovl_enable(enum omap_plane plane, bool enable) { DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); @@ -2450,6 +2726,47 @@ void dispc_mgr_enable(enum omap_channel channel, bool enable) BUG(); } +void dispc_wb_enable(bool enable) +{ + enum omap_plane plane = OMAP_DSS_WB; + struct completion frame_done_completion; + bool is_on; + int r; + u32 irq; + + is_on = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0); + irq = DISPC_IRQ_FRAMEDONEWB; + + if (!enable && is_on) { + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, + &frame_done_completion, irq); + if (r) + DSSERR("failed to register FRAMEDONEWB isr\n"); + } + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); + + if (!enable && is_on) { + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for FRAMEDONEWB\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, irq); + if (r) + DSSERR("failed to unregister FRAMEDONEWB isr\n"); + } +} + +bool dispc_wb_is_enabled(void) +{ + enum omap_plane plane = OMAP_DSS_WB; + + return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0); +} + void dispc_lcd_enable_signal_polarity(bool act_high) { if (!dss_has_feature(FEAT_LCDENABLEPOL)) @@ -2604,24 +2921,13 @@ static bool _dispc_mgr_size_ok(u16 width, u16 height) static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, int vsw, int vfp, int vbp) { - if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { - if (hsw < 1 || hsw > 64 || - hfp < 1 || hfp > 256 || - hbp < 1 || hbp > 256 || - vsw < 1 || vsw > 64 || - vfp < 0 || vfp > 255 || - vbp < 0 || vbp > 255) - return false; - } else { - if (hsw < 1 || hsw > 256 || - hfp < 1 || hfp > 4096 || - hbp < 1 || hbp > 4096 || - vsw < 1 || vsw > 256 || - vfp < 0 || vfp > 4095 || - vbp < 0 || vbp > 4095) - return false; - } - + if (hsw < 1 || hsw > dispc.feat->sw_max || + hfp < 1 || hfp > dispc.feat->hp_max || + hbp < 1 || hbp > dispc.feat->hp_max || + vsw < 1 || vsw > dispc.feat->sw_max || + vfp < 0 || vfp > dispc.feat->vp_max || + vbp < 0 || vbp > dispc.feat->vp_max) + return false; return true; } @@ -2653,19 +2959,12 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, u32 timing_h, timing_v, l; bool onoff, rf, ipc; - if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { - timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | - FLD_VAL(hbp-1, 27, 20); - - timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | - FLD_VAL(vbp, 27, 20); - } else { - timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) | - FLD_VAL(hbp-1, 31, 20); - - timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) | - FLD_VAL(vbp, 31, 20); - } + timing_h = FLD_VAL(hsw-1, dispc.feat->sw_start, 0) | + FLD_VAL(hfp-1, dispc.feat->fp_start, 8) | + FLD_VAL(hbp-1, dispc.feat->bp_start, 20); + timing_v = FLD_VAL(vsw-1, dispc.feat->sw_start, 0) | + FLD_VAL(vfp, dispc.feat->fp_start, 8) | + FLD_VAL(vbp, dispc.feat->bp_start, 20); dispc_write_reg(DISPC_TIMING_H(channel), timing_h); dispc_write_reg(DISPC_TIMING_V(channel), timing_v); @@ -2871,6 +3170,23 @@ unsigned long dispc_core_clk_rate(void) return fclk / lcd; } +static unsigned long dispc_plane_pclk_rate(enum omap_plane plane) +{ + enum omap_channel channel = dispc_ovl_get_channel_out(plane); + + return dispc_mgr_pclk_rate(channel); +} + +static unsigned long dispc_plane_lclk_rate(enum omap_plane plane) +{ + enum omap_channel channel = dispc_ovl_get_channel_out(plane); + + if (dss_mgr_is_lcd(channel)) + return dispc_mgr_lclk_rate(channel); + else + return dispc_fclk_rate(); + +} static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel) { int lcd, pcd; @@ -3491,7 +3807,7 @@ static void dispc_error_worker(struct work_struct *work) ovl->name); dispc_ovl_enable(ovl->id, false); dispc_mgr_go(ovl->manager->id); - mdelay(50); + msleep(50); } } @@ -3503,7 +3819,7 @@ static void dispc_error_worker(struct work_struct *work) bit = mgr_desc[i].sync_lost_irq; if (bit & errors) { - struct omap_dss_device *dssdev = mgr->device; + struct omap_dss_device *dssdev = mgr->get_device(mgr); bool enable; DSSERR("SYNC_LOST on channel %s, restarting the output " @@ -3523,7 +3839,7 @@ static void dispc_error_worker(struct work_struct *work) } dispc_mgr_go(mgr->id); - mdelay(50); + msleep(50); if (enable) dssdev->driver->enable(dssdev); @@ -3534,9 +3850,13 @@ static void dispc_error_worker(struct work_struct *work) DSSERR("OCP_ERR\n"); for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { struct omap_overlay_manager *mgr; + struct omap_dss_device *dssdev; + mgr = omap_dss_get_overlay_manager(i); - if (mgr->device && mgr->device->driver) - mgr->device->driver->disable(mgr->device); + dssdev = mgr->get_device(mgr); + + if (dssdev && dssdev->driver) + dssdev->driver->disable(dssdev); } } @@ -3660,17 +3980,98 @@ static void _omap_dispc_initial_config(void) if (dss_has_feature(FEAT_FUNCGATED)) REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); - _dispc_setup_color_conv_coef(); + dispc_setup_color_conv_coef(); dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); - dispc_read_plane_fifo_sizes(); + dispc_init_fifos(); dispc_configure_burst_sizes(); dispc_ovl_enable_zorder_planes(); } +static const struct dispc_features omap24xx_dispc_feats __initconst = { + .sw_start = 5, + .fp_start = 15, + .bp_start = 27, + .sw_max = 64, + .vp_max = 255, + .hp_max = 256, + .calc_scaling = dispc_ovl_calc_scaling_24xx, + .calc_core_clk = calc_core_clk_24xx, + .num_fifos = 3, +}; + +static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = { + .sw_start = 5, + .fp_start = 15, + .bp_start = 27, + .sw_max = 64, + .vp_max = 255, + .hp_max = 256, + .calc_scaling = dispc_ovl_calc_scaling_34xx, + .calc_core_clk = calc_core_clk_34xx, + .num_fifos = 3, +}; + +static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = { + .sw_start = 7, + .fp_start = 19, + .bp_start = 31, + .sw_max = 256, + .vp_max = 4095, + .hp_max = 4096, + .calc_scaling = dispc_ovl_calc_scaling_34xx, + .calc_core_clk = calc_core_clk_34xx, + .num_fifos = 3, +}; + +static const struct dispc_features omap44xx_dispc_feats __initconst = { + .sw_start = 7, + .fp_start = 19, + .bp_start = 31, + .sw_max = 256, + .vp_max = 4095, + .hp_max = 4096, + .calc_scaling = dispc_ovl_calc_scaling_44xx, + .calc_core_clk = calc_core_clk_44xx, + .num_fifos = 5, + .gfx_fifo_workaround = true, +}; + +static int __init dispc_init_features(struct device *dev) +{ + const struct dispc_features *src; + struct dispc_features *dst; + + dst = devm_kzalloc(dev, sizeof(*dst), GFP_KERNEL); + if (!dst) { + dev_err(dev, "Failed to allocate DISPC Features\n"); + return -ENOMEM; + } + + if (cpu_is_omap24xx()) { + src = &omap24xx_dispc_feats; + } else if (cpu_is_omap34xx()) { + if (omap_rev() < OMAP3430_REV_ES3_0) + src = &omap34xx_rev1_0_dispc_feats; + else + src = &omap34xx_rev3_0_dispc_feats; + } else if (cpu_is_omap44xx()) { + src = &omap44xx_dispc_feats; + } else if (soc_is_omap54xx()) { + src = &omap44xx_dispc_feats; + } else { + return -ENODEV; + } + + memcpy(dst, src, sizeof(*dst)); + dispc.feat = dst; + + return 0; +} + /* DISPC HW IP initialisation */ static int __init omap_dispchw_probe(struct platform_device *pdev) { @@ -3681,6 +4082,10 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) dispc.pdev = pdev; + r = dispc_init_features(&dispc.pdev->dev); + if (r) + return r; + spin_lock_init(&dispc.irq_lock); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS |