}
EXPORT_SYMBOL(ipu_pre_disable);
-int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
+int ipu_pre_set_fb_buffer(int id, bool resolve,
+ unsigned long fb_paddr,
+ unsigned int y_res,
unsigned int x_crop,
unsigned int y_crop,
unsigned int sec_buf_off,
unsigned int trd_buf_off)
{
struct ipu_pre_data *pre = get_pre(id);
+ unsigned int store_stat, store_block_y;
unsigned long lock_flags;
+ bool update = true;
if (!pre)
return -EINVAL;
pre_write(pre, BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(x_crop) |
BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(y_crop),
HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC);
- pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
+
+ /*
+ * Update shadow only when store engine runs out of the problematic
+ * window to workaround the SoC design bug recorded by errata ERR009624.
+ */
+ if (y_res > IPU_PRE_SMALL_LINE) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(20);
+
+ do {
+ if (time_after(jiffies, timeout)) {
+ update = false;
+ dev_warn(pre->dev, "timeout waiting for PRE "
+ "to run out of problematic window for "
+ "shadow update\n");
+ break;
+ }
+
+ store_stat = pre_read(pre, HW_PRE_STORE_ENGINE_STATUS);
+ store_block_y = (store_stat &
+ BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y) >>
+ BP_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y;
+ } while (store_block_y >=
+ (y_res / (resolve ? 4 : 1) - 2) || store_block_y == 0);
+ }
+
+ if (update)
+ pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
bool on_the_fly;
uint32_t final_pfmt;
unsigned long gpu_sec_buf_off;
+ unsigned long base;
+ uint32_t x_crop;
+ uint32_t y_crop;
+ unsigned int sec_buf_off;
+ unsigned int trd_buf_off;
dma_addr_t store_addr;
dma_addr_t alpha_phy_addr0;
dma_addr_t alpha_phy_addr1;
uint32_t cur_ipu_pfmt;
uint32_t cur_fb_pfmt;
bool cur_prefetch;
+ spinlock_t spin_lock; /* for PRE small yres cases */
};
struct mxcfb_pfmt {
* so we call complete() for both mxc_fbi->flip_complete
* and mxc_fbi->alpha_flip_complete.
*/
- complete(&mxc_fbi->flip_complete);
+ if (!mxc_fbi->prefetch ||
+ (mxc_fbi->prefetch && !ipu_pre_yres_is_small(fbi->var.yres)))
+ complete(&mxc_fbi->flip_complete);
if (mxc_fbi->alpha_chan_en) {
mxc_fbi->cur_ipu_alpha_buf = 1;
init_completion(&mxc_fbi->alpha_flip_complete);
*mxc_graphic_fbi = NULL;
u_int y_bottom;
unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
- unsigned int x_crop = 0, y_crop = 0;
- unsigned int sec_buf_off = 0, trd_buf_off = 0;
unsigned long base, ipu_base = 0, active_alpha_phy_addr = 0;
bool loc_alpha_en = false;
int fb_stride;
if (mxc_fbi->cur_prefetch && (info->var.vmode & FB_VMODE_INTERLACED))
base += info->var.rotate ?
fr_w * bytes_per_pixel(fbi_to_pixfmt(info, true)) : 0;
- } else {
- x_crop = fr_xoff & ~(bw - 1);
- y_crop = fr_yoff & ~(bh - 1);
}
if (mxc_fbi->cur_prefetch) {
+ unsigned long lock_flags = 0;
+
+ if (ipu_pre_yres_is_small(info->var.yres))
+ /*
+ * Update the PRE buffer address in the flip interrupt
+ * handler in this case to workaround the SoC design
+ * bug recorded by errata ERR009624.
+ */
+ spin_lock_irqsave(&mxc_fbi->spin_lock, lock_flags);
+
+ if (mxc_fbi->resolve) {
+ mxc_fbi->x_crop = fr_xoff & ~(bw - 1);
+ mxc_fbi->y_crop = fr_yoff & ~(bh - 1);
+ } else {
+ mxc_fbi->x_crop = 0;
+ mxc_fbi->y_crop = 0;
+ }
+
ipu_get_channel_offset(fbi_to_pixfmt(info, true),
info->var.xres,
fr_h,
0, 0,
fr_yoff,
fr_xoff,
- &sec_buf_off,
- &trd_buf_off);
+ &mxc_fbi->sec_buf_off,
+ &mxc_fbi->trd_buf_off);
if (mxc_fbi->resolve)
- sec_buf_off = mxc_fbi->gpu_sec_buf_off;
+ mxc_fbi->sec_buf_off = mxc_fbi->gpu_sec_buf_off;
+
+ if (ipu_pre_yres_is_small(info->var.yres)) {
+ mxc_fbi->base = base;
+ spin_unlock_irqrestore(&mxc_fbi->spin_lock, lock_flags);
+ }
} else {
ipu_base = base;
}
}
}
- ret = wait_for_completion_timeout(&mxc_fbi->flip_complete, HZ/2);
- if (ret == 0) {
- dev_err(info->device, "timeout when waiting for flip irq\n");
- return -ETIMEDOUT;
+ if (!mxc_fbi->cur_prefetch ||
+ (mxc_fbi->cur_prefetch && !ipu_pre_yres_is_small(info->var.yres))) {
+ ret = wait_for_completion_timeout(&mxc_fbi->flip_complete,
+ HZ/2);
+ if (ret == 0) {
+ dev_err(info->device, "timeout when waiting for flip "
+ "irq\n");
+ return -ETIMEDOUT;
+ }
}
if (!mxc_fbi->cur_prefetch) {
ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
IPU_INPUT_BUFFER, mxc_fbi->cur_ipu_buf);
- } else {
- ipu_pre_set_fb_buffer(mxc_fbi->pre_num, base,
- x_crop, y_crop,
- sec_buf_off, trd_buf_off);
+ } else if (!ipu_pre_yres_is_small(info->var.yres)) {
+ ipu_pre_set_fb_buffer(mxc_fbi->pre_num,
+ mxc_fbi->resolve,
+ base, info->var.yres,
+ mxc_fbi->x_crop,
+ mxc_fbi->y_crop,
+ mxc_fbi->sec_buf_off,
+ mxc_fbi->trd_buf_off);
}
ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
return -EBUSY;
}
+ if (mxc_fbi->cur_prefetch && ipu_pre_yres_is_small(info->var.yres)) {
+ ret = wait_for_completion_timeout(&mxc_fbi->flip_complete,
+ HZ/2);
+ if (ret == 0) {
+ dev_err(info->device, "timeout when waiting for flip "
+ "irq\n");
+ return -ETIMEDOUT;
+ }
+ }
+
dev_dbg(info->device, "Update complete\n");
info->var.yoffset = var->yoffset;
struct fb_info *fbi = dev_id;
struct mxcfb_info *mxc_fbi = fbi->par;
+ if (mxc_fbi->cur_prefetch && ipu_pre_yres_is_small(fbi->var.yres)) {
+ spin_lock(&mxc_fbi->spin_lock);
+ ipu_pre_set_fb_buffer(mxc_fbi->pre_num,
+ mxc_fbi->resolve,
+ mxc_fbi->base, fbi->var.yres,
+ mxc_fbi->x_crop, mxc_fbi->y_crop,
+ mxc_fbi->sec_buf_off,
+ mxc_fbi->trd_buf_off);
+ spin_unlock(&mxc_fbi->spin_lock);
+ }
+
complete(&mxc_fbi->flip_complete);
return IRQ_HANDLED;
}
mxcfbi->first_set_par = true;
mxcfbi->prefetch = plat_data->prefetch;
mxcfbi->pre_num = -1;
+ spin_lock_init(&mxcfbi->spin_lock);
ret = mxcfb_dispdrv_init(pdev, fbi);
if (ret < 0)