drm/rcar-du: Support per-CRTC clock and IRQ
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 14 Jun 2013 12:15:01 +0000 (14:15 +0200)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 9 Aug 2013 21:17:41 +0000 (23:17 +0200)
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>
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
drivers/gpu/drm/rcar-du/rcar_du_drv.c
drivers/gpu/drm/rcar-du/rcar_du_drv.h

index 24183fb935927851034998ddca26e63a9e820ba3..aefc8a0cbcbc5e79b29a508112307712957ac603 100644 (file)
@@ -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);
-       }
-}
index 2a0365bcbd14c481bb19542ba9b577ebe20b9553..5b69e98a3b9278e5107102650b6ee4e3cd1a15c3 100644 (file)
@@ -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);
index bb7193d79dfe6065a0575c3f48046b648f4169b0..5f82e046f83e905c67bd6bac7453a8474739e1b4 100644 (file)
@@ -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.
  *
  */
 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,
index 06dbf4ff139c855110688abe2a30e86e0c6eb1d9..7d2320fb948da5068a2199cca8999c9b5a33ed30 100644 (file)
@@ -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;