Merge tag 'imx-drm-fixes-2017-10-12' of git://git.pengutronix.de/git/pza/linux into drm-fixes
drm/imx: i.MX5 regression fix and i.MX6QP PRE/PRG stability fixes
- Disable channel burst locking on IPUv3EX (i.MX51) and IPUv3M (i.MX53).
This fixes a regression introduced by commit 790cb4c7c9
("drm/imx: lock
scanout transfers for consecutive bursts").
- Give PRG a head start. Waiting for both double buffers to fill up before
enabling the IPU improves startup reliability.
- Avoid PRE control register updates during unsafe window, workaround for
ERR009624.
* tag 'imx-drm-fixes-2017-10-12' of git://git.pengutronix.de/git/pza/linux:
gpu: ipu-v3: pre: implement workaround for ERR009624
gpu: ipu-v3: prg: wait for double buffers to be filled on channel startup
gpu: ipu-v3: Allow channel burst locking on i.MX6 only
This commit is contained in:
@@ -405,6 +405,14 @@ int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
|
||||||
|
* i.MX53 channel arbitration locking doesn't seem to work properly.
|
||||||
|
* Allow enabling the lock feature on IPUv3H / i.MX6 only.
|
||||||
|
*/
|
||||||
|
if (bursts && ipu->ipu_type != IPUV3H)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
|
||||||
if (channel->num == idmac_lock_en_info[i].chnum)
|
if (channel->num == idmac_lock_en_info[i].chnum)
|
||||||
break;
|
break;
|
||||||
|
@@ -73,6 +73,14 @@
|
|||||||
#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1)
|
#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1)
|
||||||
#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
|
#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
|
||||||
|
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS 0x120
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30)
|
||||||
|
#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31)
|
||||||
|
|
||||||
#define IPU_PRE_STORE_ENG_SIZE 0x130
|
#define IPU_PRE_STORE_ENG_SIZE 0x130
|
||||||
#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0)
|
#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0)
|
||||||
#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)
|
#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)
|
||||||
@@ -93,6 +101,7 @@ struct ipu_pre {
|
|||||||
dma_addr_t buffer_paddr;
|
dma_addr_t buffer_paddr;
|
||||||
void *buffer_virt;
|
void *buffer_virt;
|
||||||
bool in_use;
|
bool in_use;
|
||||||
|
unsigned int safe_window_end;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_MUTEX(ipu_pre_list_mutex);
|
static DEFINE_MUTEX(ipu_pre_list_mutex);
|
||||||
@@ -160,6 +169,9 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
|
|||||||
u32 active_bpp = info->cpp[0] >> 1;
|
u32 active_bpp = info->cpp[0] >> 1;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
|
/* calculate safe window for ctrl register updates */
|
||||||
|
pre->safe_window_end = height - 2;
|
||||||
|
|
||||||
writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
|
writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
|
||||||
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
||||||
|
|
||||||
@@ -199,7 +211,24 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
|
|||||||
|
|
||||||
void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
|
void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
|
||||||
{
|
{
|
||||||
|
unsigned long timeout = jiffies + msecs_to_jiffies(5);
|
||||||
|
unsigned short current_yblock;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
dev_warn(pre->dev, "timeout waiting for PRE safe window\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS);
|
||||||
|
current_yblock =
|
||||||
|
(val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) &
|
||||||
|
IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK;
|
||||||
|
} while (current_yblock == 0 || current_yblock >= pre->safe_window_end);
|
||||||
|
|
||||||
writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
|
writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include <drm/drm_fourcc.h>
|
#include <drm/drm_fourcc.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@@ -329,6 +330,12 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
|
|||||||
val = IPU_PRG_REG_UPDATE_REG_UPDATE;
|
val = IPU_PRG_REG_UPDATE_REG_UPDATE;
|
||||||
writel(val, prg->regs + IPU_PRG_REG_UPDATE);
|
writel(val, prg->regs + IPU_PRG_REG_UPDATE);
|
||||||
|
|
||||||
|
/* wait for both double buffers to be filled */
|
||||||
|
readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val,
|
||||||
|
(val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) &&
|
||||||
|
(val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)),
|
||||||
|
5, 1000);
|
||||||
|
|
||||||
clk_disable_unprepare(prg->clk_ipg);
|
clk_disable_unprepare(prg->clk_ipg);
|
||||||
|
|
||||||
chan->enabled = true;
|
chan->enabled = true;
|
||||||
|
Reference in New Issue
Block a user