Skip to content

Commit

Permalink
drm/exynos/decon5433: move TE handling to DECON
Browse files Browse the repository at this point in the history
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 <a.hajda@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
  • Loading branch information
Andrzej Hajda authored and Inki Dae committed Jun 1, 2017
1 parent f8b7f1f commit b37d53a
Showing 1 changed file with 71 additions and 23 deletions.
94 changes: 71 additions & 23 deletions drivers/gpu/drm/exynos/exynos5433_drm_decon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand Down

0 comments on commit b37d53a

Please sign in to comment.