mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
drm/exynos: dsi: check whether dsi is enabled before sending data
exynos_dsi_host_transfer() can be called through a panel driver while DSI is turning down. It is possible because the function checks only whether DSI is initialized or not, and there is a moment which DSI is set by uninitialized, but DSI is still turning down. To prevent it, DSI must be set by disabled before starting to be turned down, and exynos_dsi_host_transfer() must check whether DSI is enabled or not. Kernel dump: [ 4721.351448] Unhandled fault: synchronous external abort (0x96000210) at 0xffffff800015e018 [ 4721.351809] Internal error: : 96000210 [#1] PREEMPT SMP [ 4721.352031] Modules linked in: [ 4721.352173] CPU: 2 PID: 300 Comm: deviced Tainted: G W 4.0.4-01017-g7964a87 #1 [ 4721.353989] Hardware name: Samsung DRACO board (DT) [ 4721.358852] task: ffffffc0a0b70000 ti: ffffffc0a00ec000 task.ti: ffffffc0a00ec000 [ 4721.366327] PC is at exynos_dsi_enable_lane+0x14/0x5c [ 4721.371353] LR is at exynos_dsi_host_transfer+0x834/0x8d8 [ 4721.376731] pc : [<ffffffc000432bcc>] lr : [<ffffffc000434590>] pstate: 60000145 [ 4721.384107] sp : ffffffc0a00efbe0 [ 4721.387405] x29: ffffffc0a00efbe0 x28: ffffffc0a00ec000 [ 4721.392699] x27: ffffffc000968000 x26: 0000000000000040 [ 4721.397994] x25: ffffffc000f74dc0 x24: ffffffc0a00efec8 [ 4721.403290] x23: ffffffc0a4815400 x22: ffffffc0009f2729 [ 4721.408584] x21: ffffffc0a00efcc8 x20: ffffffc0a4a2a848 [ 4721.413879] x19: ffffffc0a4a2a818 x18: 0000000000000004 [ 4721.419173] x17: 0000007faa5cddf0 x16: ffffffc0001a40a8 [ 4721.424469] x15: 0000000000000009 x14: 000000000000000d [ 4721.429762] x13: 6e6e6f63206b726f x12: 0000000000000010 [ 4721.435058] x11: 0101010101010101 x10: 0000000000000000 [ 4721.440353] x9 : 000000000000000a x8 : 8386838282818381 [ 4721.445648] x7 : ffffffc0a201efe8 x6 : 0000000000000000 [ 4721.450943] x5 : 00000000fffffffa x4 : ffffffc0a201f170 [ 4721.456237] x3 : ffffff800015e000 x2 : ffffff800015e018 [ 4721.461531] x1 : 000000000000000f x0 : ffffffc0a4a2a818 [ 4721.466826] [ 4721.468305] Process deviced (pid: 300, stack limit = 0xffffffc0a00ec028) [ 4721.474989] Stack: (0xffffffc0a00efbe0 to 0xffffffc0a00f0000) [ 4721.480720] fbe0: a00efca0 ffffffc0 0042c944 ffffffc0 a0f2d680 ffffffc0 00000024 00000000 [ 4721.488895] fc00: a4b6d000 ffffffc0 009f2729 ffffffc0 a4815400 ffffffc0 a00efec8 ffffffc0 Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com>
This commit is contained in:
parent
e3b9e4602a
commit
0e480f6ffb
1 changed files with 14 additions and 4 deletions
|
@ -260,6 +260,7 @@ struct exynos_dsi_transfer {
|
||||||
#define DSIM_STATE_ENABLED BIT(0)
|
#define DSIM_STATE_ENABLED BIT(0)
|
||||||
#define DSIM_STATE_INITIALIZED BIT(1)
|
#define DSIM_STATE_INITIALIZED BIT(1)
|
||||||
#define DSIM_STATE_CMD_LPM BIT(2)
|
#define DSIM_STATE_CMD_LPM BIT(2)
|
||||||
|
#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3)
|
||||||
|
|
||||||
struct exynos_dsi_driver_data {
|
struct exynos_dsi_driver_data {
|
||||||
unsigned int plltmr_reg;
|
unsigned int plltmr_reg;
|
||||||
|
@ -1119,7 +1120,7 @@ static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
|
||||||
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
|
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
|
||||||
struct drm_encoder *encoder = dsi->display.encoder;
|
struct drm_encoder *encoder = dsi->display.encoder;
|
||||||
|
|
||||||
if (dsi->state & DSIM_STATE_ENABLED)
|
if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)
|
||||||
exynos_drm_crtc_te_handler(encoder->crtc);
|
exynos_drm_crtc_te_handler(encoder->crtc);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -1252,6 +1253,9 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||||
struct exynos_dsi_transfer xfer;
|
struct exynos_dsi_transfer xfer;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!(dsi->state & DSIM_STATE_ENABLED))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
|
if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
|
||||||
ret = exynos_dsi_init(dsi);
|
ret = exynos_dsi_init(dsi);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1370,8 +1374,11 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
dsi->state |= DSIM_STATE_ENABLED;
|
||||||
|
|
||||||
ret = drm_panel_prepare(dsi->panel);
|
ret = drm_panel_prepare(dsi->panel);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
dsi->state &= ~DSIM_STATE_ENABLED;
|
||||||
exynos_dsi_poweroff(dsi);
|
exynos_dsi_poweroff(dsi);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1379,8 +1386,6 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
|
||||||
exynos_dsi_set_display_mode(dsi);
|
exynos_dsi_set_display_mode(dsi);
|
||||||
exynos_dsi_set_display_enable(dsi, true);
|
exynos_dsi_set_display_enable(dsi, true);
|
||||||
|
|
||||||
dsi->state |= DSIM_STATE_ENABLED;
|
|
||||||
|
|
||||||
ret = drm_panel_enable(dsi->panel);
|
ret = drm_panel_enable(dsi->panel);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dsi->state &= ~DSIM_STATE_ENABLED;
|
dsi->state &= ~DSIM_STATE_ENABLED;
|
||||||
|
@ -1390,6 +1395,8 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1398,12 +1405,15 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
|
||||||
if (!(dsi->state & DSIM_STATE_ENABLED))
|
if (!(dsi->state & DSIM_STATE_ENABLED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
|
||||||
|
|
||||||
drm_panel_disable(dsi->panel);
|
drm_panel_disable(dsi->panel);
|
||||||
exynos_dsi_set_display_enable(dsi, false);
|
exynos_dsi_set_display_enable(dsi, false);
|
||||||
drm_panel_unprepare(dsi->panel);
|
drm_panel_unprepare(dsi->panel);
|
||||||
exynos_dsi_poweroff(dsi);
|
|
||||||
|
|
||||||
dsi->state &= ~DSIM_STATE_ENABLED;
|
dsi->state &= ~DSIM_STATE_ENABLED;
|
||||||
|
|
||||||
|
exynos_dsi_poweroff(dsi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
|
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
|
||||||
|
|
Loading…
Reference in a new issue