Hi,
This series modifies LCD I80 interface display for Exynos DRM driver to make it similar with Video(RGB) interface. Some patches in v1 are applied already and these are modified as Inki's comments. And Joonyoung's patches are also included for merge convenience.
This is based on exynos-drm-next branch.
The previous patches, v1: http://www.spinics.net/lists/dri-devel/msg69159.html
Changelog v2: - Splits patches into single feature. (commented by Inki)
I welcome any comments.
Thank you. Best regards YJ
Joonyoung Shim (2): drm/exynos: move triggering checking drm/exynos: use irq_flags instead of triggering
YoungJun Cho (7): drm/exynos: fimd: move shadow unprotection position drm/exynos: fimd: add fimd_enable_video_output() to cleanup drm/exynos: fimd: add fimd_enable_shadow_channel_path() to cleanup drm/exynos: fimd: modify I80 i/f irq relevant routine drm/exynos: fimd: add triggering unset routine in fimd_trigger() drm/exynos: dsi: move TE irq handler registration position drm/exynos: dsi: set TE GPIO IRQ status as IRQ_NOAUTOEN
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 19 ++--- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 135 ++++++++++++++++++------------- 2 files changed, 85 insertions(+), 69 deletions(-)
From: Joonyoung Shim jy0922.shim@samsung.com
It's better to be checking whether triggerring in fimd_trigger function. Also it will return if in triggerring on fimd_te_handler, then it can't execute remain codes.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 0673a39..ec2d170 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -949,6 +949,13 @@ static void fimd_trigger(struct device *dev) void *timing_base = ctx->regs + driver_data->timing_base; u32 reg;
+ /* + * Skips to trigger if in triggering state, because multiple triggering + * requests can cause panel reset. + */ + if (atomic_read(&ctx->triggering)) + return; + atomic_set(&ctx->triggering, 1);
reg = readl(ctx->regs + VIDINTCON0); @@ -969,13 +976,6 @@ static void fimd_te_handler(struct exynos_drm_manager *mgr) if (ctx->pipe < 0 || !ctx->drm_dev) return;
- /* - * Skips to trigger if in triggering state, because multiple triggering - * requests can cause panel reset. - */ - if (atomic_read(&ctx->triggering)) - return; - /* * If there is a page flip request, triggers and handles the page flip * event so that current fb can be updated into panel GRAM.
The C#_EN_F in SHADOWCON register is updated per frame. So it should be protected by fimd_shadow_protect_win().
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index ec2d170..77ba961 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -751,15 +751,15 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) val |= WINCONx_ENWIN; writel(val, ctx->regs + WINCON(win));
- /* Enable DMA channel and unprotect windows */ - fimd_shadow_protect_win(ctx, win, false); - if (ctx->driver_data->has_shadowcon) { val = readl(ctx->regs + SHADOWCON); val |= SHADOWCON_CHx_ENABLE(win); writel(val, ctx->regs + SHADOWCON); }
+ /* Enable DMA channel and unprotect windows */ + fimd_shadow_protect_win(ctx, win, false); + win_data->enabled = true;
if (ctx->i80_if)
This bit is used for video output and logic signal control. So it is better for readability.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 77ba961..5cfd251 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -229,6 +229,19 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) DRM_DEBUG_KMS("vblank wait timed out.\n"); }
+static void fimd_enable_video_output(struct fimd_context *ctx, int win, + bool enable) +{ + u32 val = readl(ctx->regs + WINCON(win)); + + if (enable) + val |= WINCONx_ENWIN; + else + val &= ~WINCONx_ENWIN; + + writel(val, ctx->regs + WINCON(win)); +} + static void fimd_clear_channel(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; @@ -241,9 +254,7 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) u32 val = readl(ctx->regs + WINCON(win));
if (val & WINCONx_ENWIN) { - /* wincon */ - val &= ~WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); + fimd_enable_video_output(ctx, win, false);
/* unprotect windows */ if (ctx->driver_data->has_shadowcon) { @@ -746,10 +757,7 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) if (win != 0) fimd_win_set_colkey(ctx, win);
- /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val |= WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); + fimd_enable_video_output(ctx, win, true);
if (ctx->driver_data->has_shadowcon) { val = readl(ctx->regs + SHADOWCON); @@ -790,10 +798,7 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) /* protect windows */ fimd_shadow_protect_win(ctx, win, true);
- /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val &= ~WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); + fimd_enable_video_output(ctx, win, false);
/* unprotect windows */ if (ctx->driver_data->has_shadowcon) {
This function is valid only the SoC has SHADOWCON register and it should be used together with fimd_enable_video_output() to match the ENWIN_F bit in WINCON# and C#_EN_F bit in SHADOWCON.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 40 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 5cfd251..fd7b469 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -242,6 +242,19 @@ static void fimd_enable_video_output(struct fimd_context *ctx, int win, writel(val, ctx->regs + WINCON(win)); }
+static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win, + bool enable) +{ + u32 val = readl(ctx->regs + SHADOWCON); + + if (enable) + val |= SHADOWCON_CHx_ENABLE(win); + else + val &= ~SHADOWCON_CHx_ENABLE(win); + + writel(val, ctx->regs + SHADOWCON); +} + static void fimd_clear_channel(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; @@ -256,12 +269,10 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) if (val & WINCONx_ENWIN) { fimd_enable_video_output(ctx, win, false);
- /* unprotect windows */ - if (ctx->driver_data->has_shadowcon) { - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_CHx_ENABLE(win); - writel(val, ctx->regs + SHADOWCON); - } + if (ctx->driver_data->has_shadowcon) + fimd_enable_shadow_channel_path(ctx, win, + false); + ch_enabled = 1; } } @@ -759,11 +770,8 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
fimd_enable_video_output(ctx, win, true);
- if (ctx->driver_data->has_shadowcon) { - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_CHx_ENABLE(win); - writel(val, ctx->regs + SHADOWCON); - } + if (ctx->driver_data->has_shadowcon) + fimd_enable_shadow_channel_path(ctx, win, true);
/* Enable DMA channel and unprotect windows */ fimd_shadow_protect_win(ctx, win, false); @@ -779,7 +787,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int win = zpos; - u32 val;
if (win == DEFAULT_ZPOS) win = ctx->default_win; @@ -800,13 +807,10 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
fimd_enable_video_output(ctx, win, false);
- /* unprotect windows */ - if (ctx->driver_data->has_shadowcon) { - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_CHx_ENABLE(win); - writel(val, ctx->regs + SHADOWCON); - } + if (ctx->driver_data->has_shadowcon) + fimd_enable_shadow_channel_path(ctx, win, false);
+ /* unprotect windows */ fimd_shadow_protect_win(ctx, win, false);
win_data->enabled = false;
For the I80 interface, the video interrupt pending register(VIDINTCON1) should be handled in fimd_irq_handler() and the video interrupt control register(VIDINTCON0) should be handled in fimd_enable_vblank() and fimd_disable_vblank() like RGB interface. So this patch moves each set / unset routines into proper positions.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index fd7b469..3c63237 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -469,12 +469,19 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr) val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE; - val |= VIDINTCON0_INT_FRAME;
- val &= ~VIDINTCON0_FRAMESEL0_MASK; - val |= VIDINTCON0_FRAMESEL0_VSYNC; - val &= ~VIDINTCON0_FRAMESEL1_MASK; - val |= VIDINTCON0_FRAMESEL1_NONE; + if (ctx->i80_if) { + val |= VIDINTCON0_INT_I80IFDONE; + val |= VIDINTCON0_INT_SYSMAINCON; + val &= ~VIDINTCON0_INT_SYSSUBCON; + } else { + val |= VIDINTCON0_INT_FRAME; + + val &= ~VIDINTCON0_FRAMESEL0_MASK; + val |= VIDINTCON0_FRAMESEL0_VSYNC; + val &= ~VIDINTCON0_FRAMESEL1_MASK; + val |= VIDINTCON0_FRAMESEL1_NONE; + }
writel(val, ctx->regs + VIDINTCON0); } @@ -493,9 +500,15 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr) if (test_and_clear_bit(0, &ctx->irq_flags)) { val = readl(ctx->regs + VIDINTCON0);
- val &= ~VIDINTCON0_INT_FRAME; val &= ~VIDINTCON0_INT_ENABLE;
+ if (ctx->i80_if) { + val &= ~VIDINTCON0_INT_I80IFDONE; + val &= ~VIDINTCON0_INT_SYSMAINCON; + val &= ~VIDINTCON0_INT_SYSSUBCON; + } else + val &= ~VIDINTCON0_INT_FRAME; + writel(val, ctx->regs + VIDINTCON0); } } @@ -959,19 +972,15 @@ static void fimd_trigger(struct device *dev) u32 reg;
/* - * Skips to trigger if in triggering state, because multiple triggering - * requests can cause panel reset. - */ + * Skips triggering if in triggering state, because multiple triggering + * requests can cause panel reset. + */ if (atomic_read(&ctx->triggering)) return;
+ /* Enters triggering mode */ atomic_set(&ctx->triggering, 1);
- reg = readl(ctx->regs + VIDINTCON0); - reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE | - VIDINTCON0_INT_SYSMAINCON); - writel(reg, ctx->regs + VIDINTCON0); - reg = readl(timing_base + TRIGCON); reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE); writel(reg, timing_base + TRIGCON); @@ -1036,21 +1045,13 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) if (ctx->pipe < 0 || !ctx->drm_dev) goto out;
- if (ctx->i80_if) { - /* unset I80 frame done interrupt */ - val = readl(ctx->regs + VIDINTCON0); - val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON); - writel(val, ctx->regs + VIDINTCON0); + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
- /* exit triggering mode */ + if (ctx->i80_if) { + /* Exits triggering mode */ atomic_set(&ctx->triggering, 0); - - drm_handle_vblank(ctx->drm_dev, ctx->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); } else { - drm_handle_vblank(ctx->drm_dev, ctx->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); - /* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) { atomic_set(&ctx->wait_vsync_event, 0);
There is a case like set config which requires triggering but vblank is not enabled yet. So triggering unset routine is required to exit from triggering mode.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 3c63237..e488b80 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -984,6 +984,13 @@ static void fimd_trigger(struct device *dev) reg = readl(timing_base + TRIGCON); reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE); writel(reg, timing_base + TRIGCON); + + /* + * Exits triggering mode if vblank is not enabled yet, because when the + * VIDINTCON0 register is not set, it can not exit from triggering mode. + */ + if (!test_bit(0, &ctx->irq_flags)) + atomic_set(&ctx->triggering, 0); }
static void fimd_te_handler(struct exynos_drm_manager *mgr)
From: Joonyoung Shim jy0922.shim@samsung.com
The drm_handle_vblank should be called whenever be vsync, te interrupt means vsync on i80 interface.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index e488b80..a8ab3ec 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -1014,7 +1014,7 @@ static void fimd_te_handler(struct exynos_drm_manager *mgr) wake_up(&ctx->wait_vsync_queue); }
- if (!atomic_read(&ctx->triggering)) + if (test_bit(0, &ctx->irq_flags)) drm_handle_vblank(ctx->drm_dev, ctx->pipe); }
@@ -1052,13 +1052,15 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) if (ctx->pipe < 0 || !ctx->drm_dev) goto out;
- drm_handle_vblank(ctx->drm_dev, ctx->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); - if (ctx->i80_if) { + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + /* Exits triggering mode */ atomic_set(&ctx->triggering, 0); } else { + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + /* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) { atomic_set(&ctx->wait_vsync_event, 0);
The drm_helper_hpd_irq_event() does dpms control and the panel is initialized and displayed on by it. So the exynos_dsi_te_irq_handler() should be registered beforehand.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 66d427e..6fe6486 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1207,9 +1207,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, dsi->mode_flags = device->mode_flags; dsi->panel_node = device->dev.of_node;
- if (dsi->connector.dev) - drm_helper_hpd_irq_event(dsi->connector.dev); - /* * This is a temporary solution and should be made by more generic way. * @@ -1223,6 +1220,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, return ret; }
+ if (dsi->connector.dev) + drm_helper_hpd_irq_event(dsi->connector.dev); + return 0; }
The exynos_dsi_te_irq_handler() works only dsi(DPMS) is on. So it is enough to enable and disable TE GPIO IRQ in exynos_dsi_enable(disable)_irq() like DSI IRQ.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.coma Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6fe6486..717c719 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1155,6 +1155,7 @@ static int exynos_dsi_init(struct exynos_dsi *dsi) static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) { int ret; + int te_gpio_irq;
dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0); if (!gpio_is_valid(dsi->te_gpio)) { @@ -1169,14 +1170,10 @@ static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) goto out; }
- /* - * This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel - * calls drm_panel_init() first then calls mipi_dsi_attach() in probe(). - * It means that te_gpio is invalid when exynos_dsi_enable_irq() is - * called by drm_panel_init() before panel is attached. - */ - ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio), - exynos_dsi_te_irq_handler, NULL, + te_gpio_irq = gpio_to_irq(dsi->te_gpio); + + irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN); + ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL, IRQF_TRIGGER_RISING, "TE", dsi); if (ret) { dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
dri-devel@lists.freedesktop.org