From f66ee304ae8990bd31fa639b775a840d6757d746 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 14 Jun 2013 14:15:01 +0200 Subject: [PATCH] drm/rcar-du: Support per-CRTC clock and IRQ Some of the DU revisions use one clock and IRQ per CRTC instead of one clock and IRQ per device. Retrieve the correct clock and register the correct IRQ for each CRTC. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 120 +++++++++++++++++++++++++-------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 52 +++----------- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 +- 4 files changed, 103 insertions(+), 74 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 24183fb..aefc8a0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -69,6 +69,30 @@ static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); } +static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + int ret; + + ret = clk_prepare_enable(rcrtc->clock); + if (ret < 0) + return ret; + + ret = rcar_du_get(rcdu); + if (ret < 0) + clk_disable_unprepare(rcrtc->clock); + + return ret; +} + +static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_put(rcdu); + clk_disable_unprepare(rcrtc->clock); +} + static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; @@ -79,7 +103,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) u32 div; /* Dot clock */ - clk = clk_get_rate(rcdu->clock); + clk = clk_get_rate(rcrtc->clock); div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); div = clamp(div, 1U, 64U) - 1; @@ -313,20 +337,16 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); } void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - if (rcrtc->dpms != DRM_MODE_DPMS_ON) return; - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); } @@ -340,18 +360,17 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); if (rcrtc->dpms == mode) return; if (mode == DRM_MODE_DPMS_ON) { - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); } else { rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); } rcrtc->dpms = mode; @@ -367,13 +386,12 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); /* We need to access the hardware during mode set, acquire a reference - * to the DU. + * to the CRTC. */ - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); /* Stop the CRTC and release the plane. Force the DPMS mode to off as a * result. @@ -423,10 +441,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, error: /* There's no rollback/abort operation to clean up in case of error. We - * thus need to release the reference to the DU acquired in prepare() + * thus need to release the reference to the CRTC acquired in prepare() * here. */ - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); return ret; } @@ -514,6 +532,24 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) drm_vblank_put(dev, rcrtc->index); } +static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) +{ + struct rcar_du_crtc *rcrtc = arg; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = rcar_du_crtc_read(rcrtc, DSSR); + rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); + + if (status & DSSR_VBK) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) @@ -551,10 +587,29 @@ static const struct drm_crtc_funcs crtc_funcs = { int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) { + struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; + unsigned int irqflags; + char clk_name[5]; + char *name; + int irq; int ret; + /* Get the CRTC clock. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + sprintf(clk_name, "du.%u", index); + name = clk_name; + } else { + name = NULL; + } + + rcrtc->clock = devm_clk_get(rcdu->dev, name); + if (IS_ERR(rcrtc->clock)) { + dev_err(rcdu->dev, "no clock for CRTC %u\n", index); + return PTR_ERR(rcrtc->clock); + } + rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; rcrtc->index = index; rcrtc->dpms = DRM_MODE_DPMS_OFF; @@ -568,6 +623,28 @@ int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) drm_crtc_helper_add(crtc, &crtc_helper_funcs); + /* Register the interrupt handler. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + irq = platform_get_irq(pdev, index); + irqflags = 0; + } else { + irq = platform_get_irq(pdev, 0); + irqflags = IRQF_SHARED; + } + + if (irq < 0) { + dev_err(rcdu->dev, "no IRQ for CRTC %u\n", index); + return ret; + } + + ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags, + dev_name(rcdu->dev), rcrtc); + if (ret < 0) { + dev_err(rcdu->dev, + "failed to register IRQ for CRTC %u\n", index); + return ret; + } + return 0; } @@ -580,16 +657,3 @@ void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable) rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE); } } - -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc) -{ - u32 status; - - status = rcar_du_crtc_read(rcrtc, DSSR); - rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); - - if (status & DSSR_VBK) { - drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); - rcar_du_crtc_finish_page_flip(rcrtc); - } -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 2a0365b..5b69e98 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -25,6 +25,7 @@ struct rcar_du_plane; struct rcar_du_crtc { struct drm_crtc crtc; + struct clk *clock; unsigned int mmio_offset; unsigned int index; bool started; @@ -38,7 +39,6 @@ struct rcar_du_crtc { int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, struct drm_file *file); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index bb7193d..5f82e04 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -35,8 +35,8 @@ /* * rcar_du_get - Acquire a reference to the DU * - * Acquiring a reference enables the device clock and setup core registers. A - * reference must be held before accessing any hardware registers. + * Acquiring the first reference setups core registers. A reference must be + * held before accessing any hardware registers. * * This function must be called with the DRM mode_config lock held. * @@ -44,16 +44,9 @@ */ int rcar_du_get(struct rcar_du_device *rcdu) { - int ret; - if (rcdu->use_count) goto done; - /* Enable clocks before accessing the hardware. */ - ret = clk_prepare_enable(rcdu->clock); - if (ret < 0) - return ret; - /* Enable extended features */ rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); @@ -74,16 +67,11 @@ done: /* * rcar_du_put - Release a reference to the DU * - * Releasing the last reference disables the device clock. - * * This function must be called with the DRM mode_config lock held. */ void rcar_du_put(struct rcar_du_device *rcdu) { - if (--rcdu->use_count) - return; - - clk_disable_unprepare(rcdu->clock); + --rcdu->use_count; } /* ----------------------------------------------------------------------------- @@ -95,8 +83,8 @@ static int rcar_du_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); - drm_irq_uninstall(dev); + dev->irq_enabled = 0; dev->dev_private = NULL; return 0; @@ -127,18 +115,12 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) rcdu->ddev = dev; dev->dev_private = rcdu; - /* I/O resources and clocks */ + /* I/O resources */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(rcdu->mmio)) return PTR_ERR(rcdu->mmio); - rcdu->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(rcdu->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return -ENOENT; - } - /* DRM/KMS objects */ ret = rcar_du_modeset_init(rcdu); if (ret < 0) { @@ -146,18 +128,14 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) goto done; } - /* IRQ and vblank handling */ + /* vblank handling */ ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); if (ret < 0) { dev_err(&pdev->dev, "failed to initialize vblank\n"); goto done; } - ret = drm_irq_install(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to install IRQ handler\n"); - goto done; - } + dev->irq_enabled = 1; platform_set_drvdata(pdev, rcdu); @@ -177,18 +155,6 @@ static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); } -static irqreturn_t rcar_du_irq(int irq, void *arg) -{ - struct drm_device *dev = arg; - struct rcar_du_device *rcdu = dev->dev_private; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) - rcar_du_crtc_irq(&rcdu->crtcs[i]); - - return IRQ_HANDLED; -} - static int rcar_du_enable_vblank(struct drm_device *dev, int crtc) { struct rcar_du_device *rcdu = dev->dev_private; @@ -221,12 +187,10 @@ static const struct file_operations rcar_du_fops = { }; static struct drm_driver rcar_du_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET - | DRIVER_PRIME, + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME, .load = rcar_du_load, .unload = rcar_du_unload, .preclose = rcar_du_preclose, - .irq_handler = rcar_du_irq, .get_vblank_counter = drm_vblank_count, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 06dbf4f..7d2320f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -25,6 +25,8 @@ struct clk; struct device; struct drm_device; +#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ + /* * struct rcar_du_device_info - DU model-specific information * @features: device features (RCAR_DU_FEATURE_*) @@ -39,7 +41,6 @@ struct rcar_du_device { const struct rcar_du_device_info *info; void __iomem *mmio; - struct clk *clock; unsigned int use_count; struct drm_device *ddev; -- 2.7.4