The writeback pipeline has properties of both overlays and overlay managers. It is like an overlay as it has programmable base addresses and contains blocks like scalar, color conversion unit, truncation unit, DISPC DMA FIFO. It is like a manager as enabling it immediately starts transfer to the memory, and it has a GO bit to use a new writeback configuration. Hence, writeback is a separate entity when it comes to applying configurations. When it comes to applying user_info to the private info maintained by DSS2, the writeback pipeline can be considered tightly coupled with the manager it is connected to. This makes sense as in the current DSS2 design as writeback is always connected to a manager in all its modes. In capture mode, writeback is connected to the manager from which we are capturing the frames. In memory to memory mode, we would either connected to a manager if we are capturing the manager's output, or we would connect to a dummy writeback manager when we are capturing data from just one overlay.Calling a manager's apply() calls omap_dss_mgr_apply_wb(), which copies the user_info to the private info struct only if that manager has writeback connected to it. Hence, copying user_info to private info for writeback is similar to how it is done for overlays. Unlike overlays, writeback configurations can't be totally governed by the state of the manager. In capture mode, writeback registers are governed by the GO bit of WB. Hence, a new configuration can be written to writeback shadow registers even if the manager it is connected to is busy. Hence, copying of private info to shadow register and setting GO bit for writeback is similar to how it is done for managers. We register to dss_apply_irq_handler() if writeback needs to write a pending configuration or clear the busy and shadow_dirty status if the previous configuration was transferred from the shadow registers successfully. This isn't optimal for capture mode as a new/pending writeback configuration is applied sometime later than the connected manager's VSYNC interrupt. The new configuration is taken only after WBDELAYCOUNT lines(in time) are generated by the manager after the VSYNC interrupt. Since we don't have any interrupt to get this event. We currently register to the manager's VSYNC interrupt itself. The correct approach would be to generate a timer based interrupt event after approximately WBDELAYCOUNT lines after we get the VSYNC interrupts. This is not done for now. Create dummy dispc_wb_go/go_busy and wb_setup functions. These will be filled up in a later commit. Signed-off-by: Archit Taneja <archit@xxxxxx> --- drivers/video/omap2/dss/apply.c | 164 ++++++++++++++++++++++++++++++++++++++- drivers/video/omap2/dss/dispc.c | 15 ++++ drivers/video/omap2/dss/dss.h | 4 + 3 files changed, 181 insertions(+), 2 deletions(-) diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index 19120c1..f40f58c 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -105,6 +105,15 @@ struct wb_priv_data { bool user_info_dirty; struct omap_dss_writeback_info user_info; + + bool info_dirty; + struct omap_dss_writeback_info info; + + bool shadow_info_dirty; + + /* If true, GO bit is up and shadow registers cannot be written. + * Never true for writeback in memory to memory mode */ + bool busy; }; static struct { @@ -250,6 +259,7 @@ static bool need_isr(void) struct omap_overlay_manager *mgr; struct mgr_priv_data *mp; struct omap_overlay *ovl; + struct omap_dss_device *dssdev; mgr = omap_dss_get_overlay_manager(i); mp = get_mgr_priv(mgr); @@ -304,13 +314,29 @@ static bool need_isr(void) if (op->shadow_info_dirty) return true; } + + dssdev = mgr->get_writeback(mgr); + if (dssdev) { + struct omap_dss_writeback *wb = dssdev->wbdev; + struct wb_priv_data *wp = get_wb_priv(wb); + + if (wp->busy) + return true; + + if (wp->info_dirty) + return true; + + if (wp->shadow_info_dirty) + return true; + + } } } return false; } -static bool need_go(struct omap_overlay_manager *mgr) +static bool need_mgr_go(struct omap_overlay_manager *mgr) { struct omap_overlay *ovl; struct mgr_priv_data *mp; @@ -330,6 +356,18 @@ static bool need_go(struct omap_overlay_manager *mgr) return false; } +static bool need_wb_go(struct omap_dss_writeback *wb) +{ + struct wb_priv_data *wp; + + wp = get_wb_priv(wb); + + if (wp->shadow_info_dirty) + return true; + + return false; +} + /* returns true if an extra_info field is currently being updated */ static bool extra_info_update_ongoing(void) { @@ -577,6 +615,29 @@ static void dss_ovl_write_regs_extra(struct omap_overlay *ovl) op->shadow_extra_info_dirty = true; } +static void dss_wb_write_regs(struct omap_dss_writeback *wb) +{ + struct wb_priv_data *wp = get_wb_priv(wb); + struct omap_dss_writeback_info *wi; + int r; + + if (!wp->info_dirty) + return; + + WARN_ON(wp->busy); + + wi = &wp->info; + + r = dispc_wb_setup(wb->id, wi); + if (r) { + DSSERR("dispc_wb_setup failed\n"); + return; + } + + wp->info_dirty = false; + wp->shadow_info_dirty = true; +} + static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); @@ -607,6 +668,7 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) static void dss_write_regs(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); + const int num_wb = dss_feat_get_num_wb(); int i; for (i = 0; i < num_mgrs; ++i) { @@ -631,11 +693,30 @@ static void dss_write_regs(void) dss_mgr_write_regs(mgr); } + + for (i = 0; i < num_wb; ++i) { + struct omap_dss_writeback *wb; + struct wb_priv_data *wp; + struct mgr_priv_data *mp; + + wb = omap_dss_get_writeback(i); + wp = get_wb_priv(wb); + mp = get_mgr_priv(wb->dssdev->manager); + + if (!mp->enabled) + continue; + + if (wp->busy) + continue; + + dss_wb_write_regs(wb); + } } static void dss_set_go_bits(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); + const int num_wb = dss_feat_get_num_wb(); int i; for (i = 0; i < num_mgrs; ++i) { @@ -648,7 +729,7 @@ static void dss_set_go_bits(void) if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) continue; - if (!need_go(mgr)) + if (!need_mgr_go(mgr)) continue; mp->busy = true; @@ -659,6 +740,31 @@ static void dss_set_go_bits(void) dispc_mgr_go(mgr->id); } + for (i = 0; i < num_wb; ++i) { + struct omap_dss_writeback *wb; + struct wb_priv_data *wp; + struct mgr_priv_data *mp; + + wb = omap_dss_get_writeback(i); + wp = get_wb_priv(wb); + mp = get_mgr_priv(wb->dssdev->manager); + + if (!mp->enabled) + continue; + + if (wp->busy) + continue; + + if (!need_wb_go(wb)) + continue; + + wp->busy = true; + + if (!dss_data.irq_enabled && need_isr()) + dss_register_vsync_isr(); + + dispc_wb_go(wb->id); + } } void dss_mgr_start_update(struct omap_overlay_manager *mgr) @@ -749,9 +855,18 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) } } +static void wb_clear_shadow_dirty(struct omap_dss_writeback *wb) +{ + struct wb_priv_data *wp; + + wp = get_wb_priv(wb); + wp->shadow_info_dirty = false; +} + static void dss_apply_irq_handler(void *data, u32 mask) { const int num_mgrs = dss_feat_get_num_mgrs(); + const int num_wb = dss_feat_get_num_wb(); int i; bool extra_updating; @@ -784,6 +899,32 @@ static void dss_apply_irq_handler(void *data, u32 mask) } } + + for (i = 0; i < num_wb; i++) { + struct omap_dss_writeback *wb; + struct wb_priv_data *wp; + bool was_busy; + + wb = omap_dss_get_writeback(i); + wp = get_wb_priv(wb); + + was_busy = wp->busy; + wp->busy = dispc_wb_go_busy(i); + + /* + * In writeback capture mode, the GO bit doesn't get reset + * at the manager's VSYNC interrupt. It takes an extra + * 'WBDELAYCOUNTER' time after VSYNC when the writeback + * FIFOs are flushed and the shadow registers are taken in. + * There isn't any DSS interrupt to notify this point in time. + * The correct solution would be to set off a timer here which + * generates an interrupt approximately after WBDELAYCOUNTER + * time. + */ + if (was_busy && !wp->busy) + wb_clear_shadow_dirty(wb); + } + dss_write_regs(); dss_set_go_bits(); @@ -825,6 +966,20 @@ static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr) mp->info = mp->user_info; } +static void omap_dss_mgr_apply_wb(struct omap_dss_writeback *wb) +{ + struct wb_priv_data *wp; + + wp = get_wb_priv(wb); + + if (!wp->user_info_dirty) + return; + + wp->user_info_dirty = false; + wp->info_dirty = true; + wp->info = wp->user_info; +} + int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) { unsigned long flags; @@ -852,6 +1007,11 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) /* Configure manager */ omap_dss_mgr_apply_mgr(mgr); + /* Configure writeback */ + dssdev = mgr->get_writeback(mgr); + if (dssdev) + omap_dss_mgr_apply_wb(dssdev->wbdev); + dss_write_regs(); dss_set_go_bits(); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index b228e05..d98905f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -508,6 +508,16 @@ void dispc_mgr_go(enum omap_channel channel) REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); } +bool dispc_wb_go_busy(int id) +{ + return false; +} + +void dispc_wb_go(int id) +{ + return; +} + 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); @@ -1888,6 +1898,11 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, return 0; } +int dispc_wb_setup(int id, struct omap_dss_writeback_info *wi) +{ + return 0; +} + int dispc_ovl_enable(enum omap_plane plane, bool enable) { DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 9b3b93c..556bc7c 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -476,6 +476,10 @@ int dispc_mgr_get_clock_div(enum omap_channel channel, void dispc_mgr_setup(enum omap_channel channel, struct omap_overlay_manager_info *info); +bool dispc_wb_go_busy(int id); +void dispc_wb_go(int id); +int dispc_wb_setup(int id, struct omap_dss_writeback_info *wi); + /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC int venc_init_platform_driver(void); -- 1.7.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html