From b37d53a0382c03064e6fee3ea1fdc77caf4906c6 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 5 Apr 2017 09:28:32 +0200 Subject: [PATCH] drm/exynos/decon5433: move TE handling to DECON DECON is the only user of TE signal, moving all TE related code to DECON driver allows to precise control of IRQ handlers. This control allows to fix race between IRQ handler and DECON disable code - now it is possible to disable DECON during IRQ handling which can result in kernel crash. Beside race fixing this change allows further code simplification. Signed-off-by: Andrzej Hajda Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 94 ++++++++++++++----- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 028a6575f7d5..5bdf1a0ec862 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -63,6 +63,8 @@ struct decon_context { void __iomem *addr; struct regmap *sysreg; struct clk *clks[ARRAY_SIZE(decon_clks_name)]; + unsigned int irq; + unsigned int te_irq; unsigned long flags; unsigned long out_type; int first_win; @@ -105,6 +107,11 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc) val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP; writel(val, ctx->addr + DECON_VIDINTCON0); + + enable_irq(ctx->irq); + if (!(ctx->out_type & I80_HW_TRG)) + enable_irq(ctx->te_irq); + set_bit(BIT_IRQS_ENABLED, &ctx->flags); return 0; @@ -118,6 +125,10 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc) if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; + if (!(ctx->out_type & I80_HW_TRG)) + disable_irq_nosync(ctx->te_irq); + disable_irq_nosync(ctx->irq); + writel(0, ctx->addr + DECON_VIDINTCON0); } @@ -491,6 +502,10 @@ static void decon_disable(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; int i; + if (!(ctx->out_type & I80_HW_TRG)) + synchronize_irq(ctx->te_irq); + synchronize_irq(ctx->irq); + if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; @@ -513,17 +528,19 @@ static void decon_disable(struct exynos_drm_crtc *crtc) set_bit(BIT_SUSPENDED, &ctx->flags); } -static void decon_te_irq_handler(struct exynos_drm_crtc *crtc) +static irqreturn_t decon_te_irq_handler(int irq, void *dev_id) { - struct decon_context *ctx = crtc->ctx; + struct decon_context *ctx = dev_id; if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags) || (ctx->out_type & I80_HW_TRG)) - return; + return IRQ_HANDLED; if (test_and_clear_bit(BIT_WIN_UPDATED, &ctx->flags) || test_bit(BIT_IRQS_ENABLED, &ctx->flags)) decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0); + + return IRQ_HANDLED; } static void decon_clear_channels(struct exynos_drm_crtc *crtc) @@ -564,7 +581,6 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = { .update_plane = decon_update_plane, .disable_plane = decon_disable_plane, .atomic_flush = decon_atomic_flush, - .te_handler = decon_te_irq_handler, }; static int decon_bind(struct device *dev, struct device *master, void *data) @@ -717,6 +733,31 @@ static const struct of_device_id exynos5433_decon_driver_dt_match[] = { }; MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match); +static int decon_conf_irq(struct decon_context *ctx, const char *name, + irq_handler_t handler, unsigned long int flags, bool required) +{ + struct platform_device *pdev = to_platform_device(ctx->dev); + int ret, irq = platform_get_irq_byname(pdev, name); + + if (irq < 0) { + if (irq == -EPROBE_DEFER) + return irq; + if (required) + dev_err(ctx->dev, "cannot get %s IRQ\n", name); + else + irq = 0; + return irq; + } + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = devm_request_irq(ctx->dev, irq, handler, flags, "drm_decon", ctx); + if (ret < 0) { + dev_err(ctx->dev, "IRQ %s request failed\n", name); + return ret; + } + + return irq; +} + static int exynos5433_decon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -740,15 +781,6 @@ static int exynos5433_decon_probe(struct platform_device *pdev) ctx->out_type |= IFTYPE_I80; } - if (ctx->out_type & I80_HW_TRG) { - ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,disp-sysreg"); - if (IS_ERR(ctx->sysreg)) { - dev_err(dev, "failed to get system register\n"); - return PTR_ERR(ctx->sysreg); - } - } - for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { struct clk *clk; @@ -771,18 +803,34 @@ static int exynos5433_decon_probe(struct platform_device *pdev) return PTR_ERR(ctx->addr); } - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - (ctx->out_type & IFTYPE_I80) ? "lcd_sys" : "vsync"); - if (!res) { - dev_err(dev, "cannot find IRQ resource\n"); - return -ENXIO; + if (ctx->out_type & IFTYPE_I80) { + ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0, true); + if (ret < 0) + return ret; + ctx->irq = ret; + + ret = decon_conf_irq(ctx, "te", decon_te_irq_handler, + IRQF_TRIGGER_RISING, false); + if (ret < 0) + return ret; + if (ret) { + ctx->te_irq = ret; + ctx->out_type &= ~I80_HW_TRG; + } + } else { + ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0, true); + if (ret < 0) + return ret; + ctx->irq = ret; } - ret = devm_request_irq(dev, res->start, decon_irq_handler, 0, - "drm_decon", ctx); - if (ret < 0) { - dev_err(dev, "lcd_sys irq request failed\n"); - return ret; + if (ctx->out_type & I80_HW_TRG) { + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,disp-sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_err(dev, "failed to get system register\n"); + return PTR_ERR(ctx->sysreg); + } } platform_set_drvdata(pdev, ctx);