| From a803274d612a7b0e0abd78e6016954b3e582a8f6 Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Date: Fri, 14 Jun 2013 14:15:01 +0200 |
| Subject: 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 <laurent.pinchart+renesas@ideasonboard.com> |
| (cherry picked from commit f66ee304ae8990bd31fa639b775a840d6757d746) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| 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 24183fb93592..aefc8a0cbcbc 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 2a0365bcbd14..5b69e98a3b92 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 60836b8d6053..9b89dbf3fb32 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 06dbf4ff139c..7d2320fb948d 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; |
| -- |
| 1.8.5.rc3 |
| |