From 0e211da8d83fafdd27b94d4f2a8f8d5b96b2a552 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 17:46:14 +0200 Subject: [PATCH 01/32] drm: rcar-du: Don't disable unused functions at init time All encoders and CRTCs start disabled, re-disabling them is a no-op. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index cc9136e8ee9c..1ea2c1e9cb79 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -448,8 +448,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); - drm_helper_disable_unused_functions(dev); - fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, dev->mode_config.num_connector); if (IS_ERR(fbdev)) From df78222d78f0f985ffb7917df4a058113e227561 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 22:35:52 +0200 Subject: [PATCH 02/32] drm: rcar-du: Remove drm_fbdev_cma_restore_mode() call at init time The function is meant to restore the fbdev mode in the lastclose handler, not to be called at init time. Remove the call. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 1ea2c1e9cb79..aebbcde82ddf 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -453,10 +453,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (IS_ERR(fbdev)) return PTR_ERR(fbdev); -#ifndef CONFIG_FRAMEBUFFER_CONSOLE - drm_fbdev_cma_restore_mode(fbdev); -#endif - rcdu->fbdev = fbdev; return 0; From 8b37c3264788a13861f318a8bfc7d73f0d0986ce Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 13:22:44 +0200 Subject: [PATCH 03/32] drm: rcar-du: Don't set connector->encoder at init time The drm_connector encoder field points to the encoder driving the connector. No such association exists at init time, as all pipelines are disabled. Don't set the field. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index ca94b029ac80..3896cb20ad51 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -116,7 +116,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - connector->encoder = encoder; rcon->encoder = renc; return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 6d9811c052c4..7c0b08fe0774 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -125,7 +125,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - connector->encoder = encoder; lvdscon->connector.encoder = renc; return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 9d4879921cc7..ae8b304e6fea 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -84,7 +84,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - connector->encoder = encoder; rcon->encoder = renc; return 0; From 17f6b8a0270f7a40b3dbe07dbcb6cb7f67ce570a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 18 Feb 2015 13:42:40 +0200 Subject: [PATCH 04/32] drm: rcar-du: Reorder CRTC functions The next commit will need functions to be reordered to avoid forward declarations. Do it separately to help review. This only moves functions without any change to the code. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 142 +++++++++++++++---------- 1 file changed, 83 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 25c7a998fc2c..e0562f2b3261 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -99,6 +99,10 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) clk_disable_unprepare(rcrtc->clock); } +/* ----------------------------------------------------------------------------- + * Hardware Setup + */ + static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { const struct drm_display_mode *mode = &rcrtc->crtc.mode; @@ -256,6 +260,55 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) dspr); } +/* ----------------------------------------------------------------------------- + * Page Flip + */ + +void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, + struct drm_file *file) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + /* Destroy the pending vertical blanking event associated with the + * pending page flip, if any, and disable vertical blanking interrupts. + */ + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + if (event && event->base.file_priv == file) { + rcrtc->event = NULL; + event->base.destroy(&event->base); + drm_vblank_put(dev, rcrtc->index); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + rcrtc->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (event == NULL) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, rcrtc->index, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + + drm_vblank_put(dev, rcrtc->index); +} + +/* ----------------------------------------------------------------------------- + * Start/Stop and Suspend/Resume + */ + static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; @@ -350,6 +403,10 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) rcar_du_plane_update_base(rcrtc->plane); } +/* ----------------------------------------------------------------------------- + * CRTC Functions + */ + static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); @@ -485,65 +542,6 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .disable = rcar_du_crtc_disable, }; -void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, - struct drm_file *file) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - /* Destroy the pending vertical blanking event associated with the - * pending page flip, if any, and disable vertical blanking interrupts. - */ - spin_lock_irqsave(&dev->event_lock, flags); - event = rcrtc->event; - if (event && event->base.file_priv == file) { - rcrtc->event = NULL; - event->base.destroy(&event->base); - drm_vblank_put(dev, rcrtc->index); - } - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - event = rcrtc->event; - rcrtc->event = NULL; - spin_unlock_irqrestore(&dev->event_lock, flags); - - if (event == NULL) - return; - - spin_lock_irqsave(&dev->event_lock, flags); - drm_send_vblank_event(dev, rcrtc->index, event); - spin_unlock_irqrestore(&dev->event_lock, flags); - - 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_FRM) { - 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, @@ -580,6 +578,32 @@ static const struct drm_crtc_funcs crtc_funcs = { .page_flip = rcar_du_crtc_page_flip, }; +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +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_FRM) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + ret = IRQ_HANDLED; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { static const unsigned int mmio_offsets[] = { From 36693f3c3254d9361095f6b225d5e69bd8da5c32 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 18 Feb 2015 13:21:56 +0200 Subject: [PATCH 05/32] drm: rcar-du: Wait for page flip completion when turning the CRTC off Turning a CRTC off will prevent a queued page flip from ever completing, potentially confusing userspace. Wait for queued page flips to complete before turning the CRTC off to avoid this. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 35 ++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 3 +++ 2 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index e0562f2b3261..7f5ae0269a61 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -300,11 +300,39 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) spin_lock_irqsave(&dev->event_lock, flags); drm_send_vblank_event(dev, rcrtc->index, event); + wake_up(&rcrtc->flip_wait); spin_unlock_irqrestore(&dev->event_lock, flags); drm_vblank_put(dev, rcrtc->index); } +static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc) +{ + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + bool pending; + + spin_lock_irqsave(&dev->event_lock, flags); + pending = rcrtc->event != NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return pending; +} + +static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->group->dev; + + if (wait_event_timeout(rcrtc->flip_wait, + !rcar_du_crtc_page_flip_pending(rcrtc), + msecs_to_jiffies(50))) + return; + + dev_warn(rcdu->dev, "page flip timeout\n"); + + rcar_du_crtc_finish_page_flip(rcrtc); +} + /* ----------------------------------------------------------------------------- * Start/Stop and Suspend/Resume */ @@ -365,6 +393,11 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) if (!rcrtc->started) return; + /* Wait for page flip completion before stopping the CRTC as userspace + * excepts page flips to eventually complete. + */ + rcar_du_crtc_wait_page_flip(rcrtc); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = false; rcar_du_crtc_update_planes(crtc); @@ -644,6 +677,8 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) return -EPROBE_DEFER; } + init_waitqueue_head(&rcrtc->flip_wait); + rcrtc->group = rgrp; rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index d2f89f7d2e5e..fb39e040f17b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -15,6 +15,7 @@ #define __RCAR_DU_CRTC_H__ #include <linux/mutex.h> +#include <linux/wait.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -32,6 +33,8 @@ struct rcar_du_crtc { bool started; struct drm_pending_vblank_event *event; + wait_queue_head_t flip_wait; + unsigned int outputs; int dpms; From 0cd90a542885a1272b58f96bbe4010588105e1bc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 18 Feb 2015 13:14:46 +0200 Subject: [PATCH 06/32] drm: rcar-du: Turn vblank on/off when enabling/disabling CRTC The DRM core vblank handling mechanism requires drivers to forcefully turn vblank reporting off when disabling the CRTC, and to restore the vblank reporting status when enabling the CRTC. Implement this using the drm_crtc_vblank_on/off helpers. When disabling vblank we must first wait for page flips to complete, so implement page flip completion wait as well. Finally, drm_crtc_vblank_off() must be called at startup to synchronize the state of the vblank core code with the hardware, which is initially disabled. This is performed at CRTC creation time, requiring vertical blanking to be initialized before creating CRTCs. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 18 +++++++++++++----- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 16 +++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 7f5ae0269a61..5cf2cac75146 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -279,7 +279,7 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, if (event && event->base.file_priv == file) { rcrtc->event = NULL; event->base.destroy(&event->base); - drm_vblank_put(dev, rcrtc->index); + drm_crtc_vblank_put(&rcrtc->crtc); } spin_unlock_irqrestore(&dev->event_lock, flags); } @@ -303,7 +303,7 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) wake_up(&rcrtc->flip_wait); spin_unlock_irqrestore(&dev->event_lock, flags); - drm_vblank_put(dev, rcrtc->index); + drm_crtc_vblank_put(&rcrtc->crtc); } static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc) @@ -383,6 +383,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_group_start_stop(rcrtc->group, true); + /* Turn vertical blanking interrupt reporting back on. */ + drm_crtc_vblank_on(crtc); + rcrtc->started = true; } @@ -393,10 +396,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) if (!rcrtc->started) return; - /* Wait for page flip completion before stopping the CRTC as userspace - * excepts page flips to eventually complete. + /* Disable vertical blanking interrupt reporting. We first need to wait + * for page flip completion before stopping the CRTC as userspace + * expects page flips to eventually complete. */ rcar_du_crtc_wait_page_flip(rcrtc); + drm_crtc_vblank_off(crtc); mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = false; @@ -596,7 +601,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, if (event) { event->pipe = rcrtc->index; - drm_vblank_get(dev, rcrtc->index); + drm_crtc_vblank_get(crtc); spin_lock_irqsave(&dev->event_lock, flags); rcrtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags); @@ -693,6 +698,9 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) drm_crtc_helper_add(crtc, &crtc_helper_funcs); + /* Start with vertical blanking interrupt reporting disabled. */ + drm_crtc_vblank_off(crtc); + /* Register the interrupt handler. */ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { irq = platform_get_irq(pdev, index); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index e0d74f821416..8ab4c69c599b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -175,6 +175,15 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) if (IS_ERR(rcdu->mmio)) return PTR_ERR(rcdu->mmio); + /* Initialize vertical blanking interrupts handling. Start with vblank + * disabled for all CRTCs. + */ + ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize vblank\n"); + goto done; + } + /* DRM/KMS objects */ ret = rcar_du_modeset_init(rcdu); if (ret < 0) { @@ -182,13 +191,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) goto done; } - /* 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; - } - dev->irq_enabled = 1; platform_set_drvdata(pdev, rcdu); From 931b7336214bec8538babfde5eaa6833e179c804 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Tue, 24 Feb 2015 03:51:26 +0200 Subject: [PATCH 07/32] drm: rcar-du: Disable fbdev emulation when no connector is present fbdev emulation requires at least one connector, and will fail to initialize if no connector has been successfully instantiated. Disable it in that case and print an informational message instead of failing probe with a confusing fbdev emulation error message. It could be argued that probe should fail when no connector is present, but the DU could still be useful in that case with the to-be-implemented memory write-back support. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index aebbcde82ddf..888df404932e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -448,12 +448,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); - fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, - dev->mode_config.num_connector); - if (IS_ERR(fbdev)) - return PTR_ERR(fbdev); + if (dev->mode_config.num_connector) { + fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, + dev->mode_config.num_connector); + if (IS_ERR(fbdev)) + return PTR_ERR(fbdev); - rcdu->fbdev = fbdev; + rcdu->fbdev = fbdev; + } else { + dev_info(rcdu->dev, + "no connector found, disabling fbdev emulation\n"); + } return 0; } From a64b9c7e56590248445ee23f835309ede500d019 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 25 Feb 2015 18:21:12 +0200 Subject: [PATCH 08/32] drm: rcar-du: Define macros for the max number of groups, CRTCs and LVDS Let's avoid magic constants. Beside increasing code readability, it will also ensure that no location will be forgotten when raising the maximum number of groups, CRTCs or LVDS encoders Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index c5b9ea6a7eaa..d1ede13cb1d5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -64,6 +64,10 @@ struct rcar_du_device_info { unsigned int num_lvds; }; +#define RCAR_DU_MAX_CRTCS 3 +#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2) +#define RCAR_DU_MAX_LVDS 2 + struct rcar_du_device { struct device *dev; const struct rcar_du_device_info *info; @@ -73,13 +77,13 @@ struct rcar_du_device { struct drm_device *ddev; struct drm_fbdev_cma *fbdev; - struct rcar_du_crtc crtcs[3]; + struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; - struct rcar_du_group groups[2]; + struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; unsigned int dpad0_source; - struct rcar_du_lvdsenc *lvds[2]; + struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS]; }; static inline bool rcar_du_has(struct rcar_du_device *rcdu, From 917de180379da229c8c37fa790b76d0353576581 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Tue, 17 Feb 2015 18:34:17 +0200 Subject: [PATCH 09/32] drm: rcar-du: Implement universal plane support Explicitly create the CRTC primary plane instead of relying on the core helpers to do so. This simplifies the plane logic by merging the KMS and software planes. Reject plane API operations on the primary planes for now, as that code will anyway be refactored when implementing support for atomic updates. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 3 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 7 --- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 65 ++++++++++++------------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 17 +++---- 4 files changed, 40 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 5cf2cac75146..c2ca2a302f44 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -692,7 +692,8 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) rcrtc->plane->crtc = crtc; - ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs); + ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, &rcrtc->plane->plane, + NULL, &crtc_funcs); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 888df404932e..413145de3db3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -439,13 +439,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) encoder->possible_clones = (1 << num_encoders) - 1; } - /* Now that the CRTCs have been initialized register the planes. */ - for (i = 0; i < num_groups; ++i) { - ret = rcar_du_planes_register(&rcdu->groups[i]); - if (ret < 0) - return ret; - } - drm_kms_helper_poll_init(dev); if (dev->mode_config.num_connector) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 50f2f2b20d39..242db1e1a1e4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -26,14 +26,9 @@ #define RCAR_DU_COLORKEY_SOURCE (1 << 24) #define RCAR_DU_COLORKEY_MASK (1 << 24) -struct rcar_du_kms_plane { - struct drm_plane plane; - struct rcar_du_plane *hwplane; -}; - static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) { - return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane; + return container_of(plane, struct rcar_du_plane, plane); } static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, @@ -299,6 +294,9 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, unsigned int nplanes; int ret; + if (plane->type != DRM_PLANE_TYPE_OVERLAY) + return -EINVAL; + format = rcar_du_format_info(fb->pixel_format); if (format == NULL) { dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, @@ -348,6 +346,9 @@ static int rcar_du_plane_disable(struct drm_plane *plane) { struct rcar_du_plane *rplane = to_rcar_plane(plane); + if (plane->type != DRM_PLANE_TYPE_OVERLAY) + return -EINVAL; + if (!rplane->enabled) return 0; @@ -453,7 +454,11 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) { struct rcar_du_planes *planes = &rgrp->planes; struct rcar_du_device *rcdu = rgrp->dev; + unsigned int num_planes; + unsigned int num_crtcs; + unsigned int crtcs; unsigned int i; + int ret; mutex_init(&planes->lock); planes->free = 0xff; @@ -478,45 +483,35 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) if (planes->zpos == NULL) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) { + /* Create one primary plane per in this group CRTC and seven overlay + * planes. + */ + num_crtcs = min(rcdu->num_crtcs - 2 * rgrp->index, 2U); + num_planes = num_crtcs + 7; + + crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); + + for (i = 0; i < num_planes; ++i) { + enum drm_plane_type type = i < num_crtcs + ? DRM_PLANE_TYPE_PRIMARY + : DRM_PLANE_TYPE_OVERLAY; struct rcar_du_plane *plane = &planes->planes[i]; plane->group = rgrp; plane->hwindex = -1; plane->alpha = 255; plane->colorkey = RCAR_DU_COLORKEY_NONE; - plane->zpos = 0; - } + plane->zpos = type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; - return 0; -} - -int rcar_du_planes_register(struct rcar_du_group *rgrp) -{ - struct rcar_du_planes *planes = &rgrp->planes; - struct rcar_du_device *rcdu = rgrp->dev; - unsigned int crtcs; - unsigned int i; - int ret; - - crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); - - for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { - struct rcar_du_kms_plane *plane; - - plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL); - if (plane == NULL) - return -ENOMEM; - - plane->hwplane = &planes->planes[i + 2]; - plane->hwplane->zpos = 1; - - ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs, - &rcar_du_plane_funcs, formats, - ARRAY_SIZE(formats), false); + ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, + &rcar_du_plane_funcs, formats, + ARRAY_SIZE(formats), type); if (ret < 0) return ret; + if (type == DRM_PLANE_TYPE_PRIMARY) + continue; + drm_object_attach_property(&plane->plane.base, planes->alpha, 255); drm_object_attach_property(&plane->plane.base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 3021288b1a89..813b3212392a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -22,17 +22,17 @@ struct rcar_du_format_info; struct rcar_du_group; -/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As - * using KMS planes requires at least one of the CRTCs being enabled, no more - * than 7 KMS planes can be available. We thus create 7 KMS planes and - * 9 software planes (one for each KMS planes and one for each CRTC). +/* The RCAR DU has 8 hardware planes, shared between primary and overlay planes. + * As using overlay planes requires at least one of the CRTCs being enabled, no + * more than 7 overlay planes can be available. We thus create 1 primary plane + * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes. */ - -#define RCAR_DU_NUM_KMS_PLANES 7 +#define RCAR_DU_NUM_KMS_PLANES 9 #define RCAR_DU_NUM_HW_PLANES 8 -#define RCAR_DU_NUM_SW_PLANES 9 struct rcar_du_plane { + struct drm_plane plane; + struct rcar_du_group *group; struct drm_crtc *crtc; @@ -58,7 +58,7 @@ struct rcar_du_plane { }; struct rcar_du_planes { - struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; + struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES]; unsigned int free; struct mutex lock; @@ -68,7 +68,6 @@ struct rcar_du_planes { }; int rcar_du_planes_init(struct rcar_du_group *rgrp); -int rcar_du_planes_register(struct rcar_du_group *rgrp); void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_update_base(struct rcar_du_plane *plane); From 3053460482af5f43d6967f34c8497a615969e604 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 25 Feb 2015 18:38:25 +0200 Subject: [PATCH 10/32] drm: rcar-du: Fix hardware plane allocation The hardware plane allocator loops over all planes to find free candidates. However, instead of looping over the number of hardware planes, it loops over the number of software planes, which happens to be larger by one unit. This has no effect in practise as the extra plane is always cleared in the mask of free planes, but it should still be fixed for correctness. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 242db1e1a1e4..4a0669fd8176 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -54,7 +54,7 @@ int rcar_du_plane_reserve(struct rcar_du_plane *plane, mutex_lock(&rgrp->planes.lock); - for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { + for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) { if (!(rgrp->planes.free & (1 << i))) continue; @@ -63,7 +63,7 @@ int rcar_du_plane_reserve(struct rcar_du_plane *plane, break; } - if (i == ARRAY_SIZE(rgrp->planes.planes)) + if (i == RCAR_DU_NUM_HW_PLANES) goto done; rgrp->planes.free &= ~(1 << i); From 920888a2d56f0ef7117bf3456cacb49c6801d8de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 18 Feb 2015 12:18:05 +0200 Subject: [PATCH 11/32] drm: rcar-du: Implement planes atomic operations Implement the CRTC .atomic_begin() and .atomic_flush() operations, the plane .atomic_check(), .atomic_update() and operations, and use the transitional atomic helpers to implement the plane update and disable operations on top of the new atomic operations. The plane setup code can't be moved out of the CRTC start function completely yet, as the atomic code paths are not taken every time the CRTC needs to be started. This results in some code duplication that will be fixed after switching to atomic updates completely. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 37 +++++- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 154 ++++++++++++++++-------- 2 files changed, 142 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index c2ca2a302f44..3d862a894b17 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -357,12 +357,21 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_crtc_set_display_timing(rcrtc); rcar_du_group_set_routing(rcrtc->group); + /* FIXME: Commit the planes state. This is required here as the CRTC can + * be started from the DPMS and system resume handler, which don't go + * through .atomic_plane_update() and .atomic_flush() to commit plane + * state. Similarly a mode set operation without any update to planes + * will not go through atomic plane configuration either. Additionally, + * given that the plane state atomic commit occurs between CRTC disable + * and enable, the hardware state could also be lost due to runtime PM, + * requiring a full commit here. This will be fixed later after + * switching to atomic updates completely. + */ mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = true; rcar_du_crtc_update_planes(crtc); mutex_unlock(&rcrtc->group->planes.lock); - /* Setup planes. */ for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; @@ -570,6 +579,30 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc) rcar_du_plane_release(rcrtc->plane); } +static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We need to access the hardware during atomic update, acquire a + * reference to the CRTC. + */ + rcar_du_crtc_get(rcrtc); +} + +static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We're done, apply the configuration and drop the reference acquired + * in .atomic_begin(). + */ + mutex_lock(&rcrtc->group->planes.lock); + rcar_du_crtc_update_planes(crtc); + mutex_unlock(&rcrtc->group->planes.lock); + + rcar_du_crtc_put(rcrtc); +} + static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .dpms = rcar_du_crtc_dpms, .mode_fixup = rcar_du_crtc_mode_fixup, @@ -578,6 +611,8 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .mode_set = rcar_du_crtc_mode_set, .mode_set_base = rcar_du_crtc_mode_set_base, .disable = rcar_du_crtc_disable, + .atomic_begin = rcar_du_crtc_atomic_begin, + .atomic_flush = rcar_du_crtc_atomic_flush, }; static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 4a0669fd8176..03995c15bbe3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -16,6 +16,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> #include "rcar_du_drv.h" #include "rcar_du_kms.h" @@ -45,6 +46,38 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, data); } +static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format) +{ + struct rcar_du_group *rgrp = plane->group; + unsigned int free; + unsigned int i; + int ret; + + mutex_lock(&rgrp->planes.lock); + + free = rgrp->planes.free; + + if (plane->hwindex != -1) { + free |= 1 << plane->hwindex; + if (plane->format->planes == 2) + free |= 1 << ((plane->hwindex + 1) % 8); + } + + for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { + if (!(free & (1 << i))) + continue; + + if (format->planes == 1 || free & (1 << ((i + 1) % 8))) + break; + } + + ret = i == ARRAY_SIZE(rgrp->planes.planes) ? -EBUSY : 0; + + mutex_unlock(&rgrp->planes.lock); + return ret; +} + int rcar_du_plane_reserve(struct rcar_du_plane *plane, const struct rcar_du_format_info *format) { @@ -281,12 +314,8 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane) rcar_du_plane_update_base(plane); } -static int -rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) +static int rcar_du_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_device *rcdu = rplane->group->dev; @@ -294,63 +323,43 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, unsigned int nplanes; int ret; - if (plane->type != DRM_PLANE_TYPE_OVERLAY) - return -EINVAL; + if (!state->fb || !state->crtc) + return 0; - format = rcar_du_format_info(fb->pixel_format); - if (format == NULL) { - dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, - fb->pixel_format); + if (state->src_w >> 16 != state->crtc_w || + state->src_h >> 16 != state->crtc_h) { + dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); return -EINVAL; } - if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { - dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); + format = rcar_du_format_info(state->fb->pixel_format); + if (format == NULL) { + dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, + state->fb->pixel_format); return -EINVAL; } nplanes = rplane->format ? rplane->format->planes : 0; - /* Reallocate hardware planes if the number of required planes has - * changed. + /* If the number of required planes has changed we will need to + * reallocate hardware planes. Ensure free planes are available. */ if (format->planes != nplanes) { - rcar_du_plane_release(rplane); - ret = rcar_du_plane_reserve(rplane, format); - if (ret < 0) + ret = rcar_du_plane_reserve_check(rplane, format); + if (ret < 0) { + dev_dbg(rcdu->dev, "%s: no available hardware plane\n", + __func__); return ret; + } } - rplane->crtc = crtc; - rplane->format = format; - - rplane->src_x = src_x >> 16; - rplane->src_y = src_y >> 16; - rplane->dst_x = crtc_x; - rplane->dst_y = crtc_y; - rplane->width = crtc_w; - rplane->height = crtc_h; - - rcar_du_plane_compute_base(rplane, fb); - rcar_du_plane_setup(rplane); - - mutex_lock(&rplane->group->planes.lock); - rplane->enabled = true; - rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rplane->group->planes.lock); - return 0; } -static int rcar_du_plane_disable(struct drm_plane *plane) +static void rcar_du_plane_disable(struct rcar_du_plane *rplane) { - struct rcar_du_plane *rplane = to_rcar_plane(plane); - - if (plane->type != DRM_PLANE_TYPE_OVERLAY) - return -EINVAL; - if (!rplane->enabled) - return 0; + return; mutex_lock(&rplane->group->planes.lock); rplane->enabled = false; @@ -361,10 +370,56 @@ static int rcar_du_plane_disable(struct drm_plane *plane) rplane->crtc = NULL; rplane->format = NULL; - - return 0; } +static void rcar_du_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct drm_plane_state *state = plane->state; + const struct rcar_du_format_info *format; + unsigned int nplanes; + + if (!state->crtc) { + rcar_du_plane_disable(rplane); + return; + } + + format = rcar_du_format_info(state->fb->pixel_format); + nplanes = rplane->format ? rplane->format->planes : 0; + + /* Reallocate hardware planes if the number of required planes has + * changed. + */ + if (format->planes != nplanes) { + rcar_du_plane_release(rplane); + rcar_du_plane_reserve(rplane, format); + } + + rplane->crtc = state->crtc; + rplane->format = format; + + rplane->src_x = state->src_x >> 16; + rplane->src_y = state->src_y >> 16; + rplane->dst_x = state->crtc_x; + rplane->dst_y = state->crtc_y; + rplane->width = state->crtc_w; + rplane->height = state->crtc_h; + + rcar_du_plane_compute_base(rplane, state->fb); + rcar_du_plane_setup(rplane); + + mutex_lock(&rplane->group->planes.lock); + rplane->enabled = true; + rcar_du_crtc_update_planes(rplane->crtc); + mutex_unlock(&rplane->group->planes.lock); +} + +static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { + .atomic_check = rcar_du_plane_atomic_check, + .atomic_update = rcar_du_plane_atomic_update, +}; + /* Both the .set_property and the .update_plane operations are called with the * mode_config lock held. There is this no need to explicitly protect access to * the alpha and colorkey fields and the mode register. @@ -431,8 +486,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, } static const struct drm_plane_funcs rcar_du_plane_funcs = { - .update_plane = rcar_du_plane_update, - .disable_plane = rcar_du_plane_disable, + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, .set_property = rcar_du_plane_set_property, .destroy = drm_plane_cleanup, }; @@ -509,6 +564,9 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) if (ret < 0) return ret; + drm_plane_helper_add(&plane->plane, + &rcar_du_plane_helper_funcs); + if (type == DRM_PLANE_TYPE_PRIMARY) continue; From 845f46356ba490b654194b8d5c26032841719a78 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 18 Feb 2015 15:47:27 +0200 Subject: [PATCH 12/32] drm: rcar-du: Handle primary plane config through atomic plane ops Use the new CRTC atomic transitional helpers drm_helper_crtc_mode_set() and drm_helper_crtc_mode_set_base() to implement the CRTC .mode_set and .mode_set_base operations. This delegates primary plane configuration to the plane .atomic_update and .atomic_disable operations, removing duplicate code from the CRTC implementation. There is now no code path available to the driver in which to drop the reference to the CRTC acquired in the .prepare() operation if an error then occurs. The driver thus now leaks a reference if an error occurs during mode set. So be it, this will be fixed in a further step of the atomic update transition. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 81 ++++--------------------- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +-- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 3 - 3 files changed, 14 insertions(+), 78 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 3d862a894b17..169558a3ab40 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -105,7 +105,7 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { - const struct drm_display_mode *mode = &rcrtc->crtc.mode; + const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; unsigned long mode_clock = mode->clock * 1000; unsigned long clk; u32 value; @@ -368,7 +368,6 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) * switching to atomic updates completely. */ mutex_lock(&rcrtc->group->planes.lock); - rcrtc->plane->enabled = true; rcar_du_crtc_update_planes(crtc); mutex_unlock(&rcrtc->group->planes.lock); @@ -412,11 +411,6 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) rcar_du_crtc_wait_page_flip(rcrtc); drm_crtc_vblank_off(crtc); - mutex_lock(&rcrtc->group->planes.lock); - rcrtc->plane->enabled = false; - rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcrtc->group->planes.lock); - /* Select switch sync mode. This stops display operation and configures * the HSYNC and VSYNC signals as inputs. */ @@ -492,58 +486,20 @@ static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) */ rcar_du_crtc_get(rcrtc); - /* Stop the CRTC and release the plane. Force the DPMS mode to off as a - * result. - */ + /* Stop the CRTC, force the DPMS mode to off as a result. */ rcar_du_crtc_stop(rcrtc); - rcar_du_plane_release(rcrtc->plane); rcrtc->dpms = DRM_MODE_DPMS_OFF; + rcrtc->outputs = 0; } -static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static void rcar_du_crtc_mode_set_nofb(struct drm_crtc *crtc) { - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct rcar_du_device *rcdu = rcrtc->group->dev; - const struct rcar_du_format_info *format; - int ret; - - format = rcar_du_format_info(crtc->primary->fb->pixel_format); - if (format == NULL) { - dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n", - crtc->primary->fb->pixel_format); - ret = -EINVAL; - goto error; - } - - ret = rcar_du_plane_reserve(rcrtc->plane, format); - if (ret < 0) - goto error; - - rcrtc->plane->format = format; - - rcrtc->plane->src_x = x; - rcrtc->plane->src_y = y; - rcrtc->plane->width = mode->hdisplay; - rcrtc->plane->height = mode->vdisplay; - - rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb); - - rcrtc->outputs = 0; - - return 0; - -error: - /* There's no rollback/abort operation to clean up in case of error. We - * thus need to release the reference to the CRTC acquired in prepare() - * here. + /* No-op. We should configure the display timings here, but as we're + * called with the CRTC disabled clocks might be off, and we thus can't + * access the hardware. Let's just configure everything when enabling + * the CRTC. */ - rcar_du_crtc_put(rcrtc); - return ret; } static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) @@ -558,25 +514,9 @@ static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) rcrtc->dpms = DRM_MODE_DPMS_ON; } -static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - - rcrtc->plane->src_x = x; - rcrtc->plane->src_y = y; - - rcar_du_crtc_update_base(rcrtc); - - return 0; -} - static void rcar_du_crtc_disable(struct drm_crtc *crtc) { - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - rcar_du_plane_release(rcrtc->plane); } static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) @@ -608,8 +548,9 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .mode_fixup = rcar_du_crtc_mode_fixup, .prepare = rcar_du_crtc_mode_prepare, .commit = rcar_du_crtc_mode_commit, - .mode_set = rcar_du_crtc_mode_set, - .mode_set_base = rcar_du_crtc_mode_set_base, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = rcar_du_crtc_mode_set_nofb, + .mode_set_base = drm_helper_crtc_mode_set_base, .disable = rcar_du_crtc_disable, .atomic_begin = rcar_du_crtc_atomic_begin, .atomic_flush = rcar_du_crtc_atomic_flush, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 03995c15bbe3..d4682ac7db03 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -78,8 +78,8 @@ static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, return ret; } -int rcar_du_plane_reserve(struct rcar_du_plane *plane, - const struct rcar_du_format_info *format) +static int rcar_du_plane_reserve(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format) { struct rcar_du_group *rgrp = plane->group; unsigned int i; @@ -112,7 +112,7 @@ done: return ret; } -void rcar_du_plane_release(struct rcar_du_plane *plane) +static void rcar_du_plane_release(struct rcar_du_plane *plane) { struct rcar_du_group *rgrp = plane->group; @@ -363,7 +363,6 @@ static void rcar_du_plane_disable(struct rcar_du_plane *rplane) mutex_lock(&rplane->group->planes.lock); rplane->enabled = false; - rcar_du_crtc_update_planes(rplane->crtc); mutex_unlock(&rplane->group->planes.lock); rcar_du_plane_release(rplane); @@ -411,7 +410,6 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, mutex_lock(&rplane->group->planes.lock); rplane->enabled = true; - rcar_du_crtc_update_planes(rplane->crtc); mutex_unlock(&rplane->group->planes.lock); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 813b3212392a..55d2f55faece 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -73,8 +73,5 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_update_base(struct rcar_du_plane *plane); void rcar_du_plane_compute_base(struct rcar_du_plane *plane, struct drm_framebuffer *fb); -int rcar_du_plane_reserve(struct rcar_du_plane *plane, - const struct rcar_du_format_info *format); -void rcar_du_plane_release(struct rcar_du_plane *plane); #endif /* __RCAR_DU_PLANE_H__ */ From 3e8da87d0c04e585f4af0f223376128f923cf7bc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 11:30:59 +0200 Subject: [PATCH 13/32] drm: rcar-du: Wire up atomic state object scaffolding Hook up the default .reset(), .atomic_duplicate_state() and .atomic_free_state() helpers to ensure that state objects are properly created and destroyed, and call drm_mode_config_reset() at init time to create the initial state objects. Framebuffer reference count also gets maintained automatically by the transitional helpers except for the legacy page flip operation. Maintain it explicitly there. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++ drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c | 4 ++++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 ++ drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 4 ++++ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 4 ++++ drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 4 ++++ 6 files changed, 25 insertions(+) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 169558a3ab40..6e7266c1a47b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -15,6 +15,8 @@ #include <linux/mutex.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> @@ -572,6 +574,8 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, } spin_unlock_irqrestore(&dev->event_lock, flags); + drm_atomic_set_fb_for_plane(crtc->primary->state, fb); + crtc->primary->fb = fb; rcar_du_crtc_update_base(rcrtc); @@ -587,9 +591,12 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, } static const struct drm_crtc_funcs crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config, .page_flip = rcar_du_crtc_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 3896cb20ad51..9378c283cb58 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_encoder_slave.h> @@ -75,9 +76,12 @@ rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) static const struct drm_connector_funcs connector_funcs = { .dpms = drm_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = rcar_du_hdmi_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 413145de3db3..94ff8fcc7268 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -439,6 +439,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) encoder->possible_clones = (1 << num_encoders) - 1; } + drm_mode_config_reset(dev); + drm_kms_helper_poll_init(dev); if (dev->mode_config.num_connector) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 7c0b08fe0774..4bfa3ce34ab3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -75,9 +76,12 @@ rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) static const struct drm_connector_funcs connector_funcs = { .dpms = drm_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_lvds_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = rcar_du_lvds_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index d4682ac7db03..c4e4fd293eee 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> @@ -486,8 +487,11 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, static const struct drm_plane_funcs rcar_du_plane_funcs = { .update_plane = drm_plane_helper_update, .disable_plane = drm_plane_helper_disable, + .reset = drm_atomic_helper_plane_reset, .set_property = rcar_du_plane_set_property, .destroy = drm_plane_cleanup, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; static const uint32_t formats[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index ae8b304e6fea..a110cf22f3b0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -44,9 +45,12 @@ rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) static const struct drm_connector_funcs connector_funcs = { .dpms = drm_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_vga_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = rcar_du_vga_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, From 287bdf03033709becec8b7d4a38f044939946abd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 15:58:38 +0200 Subject: [PATCH 14/32] drm: rcar-du: Remove private copy of plane size and position The plane source and destination size and positions are stored in the plane state, and a private copy is kept in the rcar_du_plane objects. Remove the private copy as it just duplicates the state. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 25 ++++++++++--------------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 8 -------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index c4e4fd293eee..f247b1fcf791 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -132,6 +132,8 @@ static void rcar_du_plane_release(struct rcar_du_plane *plane) void rcar_du_plane_update_base(struct rcar_du_plane *plane) { struct rcar_du_group *rgrp = plane->group; + unsigned int src_x = plane->plane.state->src_x >> 16; + unsigned int src_y = plane->plane.state->src_y >> 16; unsigned int index = plane->hwindex; bool interlaced; u32 mwr; @@ -163,8 +165,8 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) * require a halved Y position value, in both progressive and interlaced * modes. */ - rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (!interlaced && plane->format->bpp == 32 ? 2 : 1)); rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]); @@ -173,8 +175,8 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) rcar_du_plane_write(rgrp, index, PnMWR, plane->pitch); - rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (plane->format->bpp == 16 ? 2 : 1) / 2); rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]); } @@ -294,10 +296,10 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); /* Destination position and size */ - rcar_du_plane_write(rgrp, index, PnDSXR, plane->width); - rcar_du_plane_write(rgrp, index, PnDSYR, plane->height); - rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x); - rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y); + rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w); + rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h); + rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x); + rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y); /* Wrap-around and blinking, disabled */ rcar_du_plane_write(rgrp, index, PnWASPR, 0); @@ -399,13 +401,6 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, rplane->crtc = state->crtc; rplane->format = format; - rplane->src_x = state->src_x >> 16; - rplane->src_y = state->src_y >> 16; - rplane->dst_x = state->crtc_x; - rplane->dst_y = state->crtc_y; - rplane->width = state->crtc_w; - rplane->height = state->crtc_h; - rcar_du_plane_compute_base(rplane, state->fb); rcar_du_plane_setup(rplane); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 55d2f55faece..3420ff07f2c6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -47,14 +47,6 @@ struct rcar_du_plane { unsigned long dma[2]; unsigned int pitch; - - unsigned int width; - unsigned int height; - - unsigned int src_x; - unsigned int src_y; - unsigned int dst_x; - unsigned int dst_y; }; struct rcar_du_planes { From 44ef7ed5ac94ac823c0efce4c2fb6ab809c8a838 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 14:59:58 +0200 Subject: [PATCH 15/32] drm: rcar-du: Replace LVDS encoder DPMS by enable/disable The LVDS encoder doesn't support DPMS states, replace the DPMS operation by enable/disable to avoid propagating DPMS states down to the encoder code. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ++++-------- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 6 ++++-- drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 18 +++++++++--------- drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h | 8 ++++---- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 279167f783f6..6243af310368 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -46,11 +46,9 @@ static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; - if (renc->lvds) - rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); + rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, + mode == DRM_MODE_DPMS_ON); } static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, @@ -110,8 +108,7 @@ static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) struct rcar_du_encoder *renc = to_rcar_encoder(encoder); if (renc->lvds) - rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, - DRM_MODE_DPMS_OFF); + rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); } static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) @@ -119,8 +116,7 @@ static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) struct rcar_du_encoder *renc = to_rcar_encoder(encoder); if (renc->lvds) - rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, - DRM_MODE_DPMS_ON); + rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); } static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 221f0a17fd6a..89a7c0ecc935 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -44,13 +44,15 @@ static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) return; if (mode == DRM_MODE_DPMS_ON && hdmienc->renc->lvds) - rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode); + rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, + true); if (sfuncs->dpms) sfuncs->dpms(encoder, mode); if (mode != DRM_MODE_DPMS_ON && hdmienc->renc->lvds) - rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode); + rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, + false); hdmienc->dpms = mode; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c index 7cfb48ce1791..85043c5bad03 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c @@ -28,7 +28,7 @@ struct rcar_du_lvdsenc { unsigned int index; void __iomem *mmio; struct clk *clock; - int dpms; + bool enabled; enum rcar_lvds_input input; }; @@ -48,7 +48,7 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, u32 pllcr; int ret; - if (lvds->dpms == DRM_MODE_DPMS_ON) + if (lvds->enabled) return 0; ret = clk_prepare_enable(lvds->clock); @@ -110,13 +110,13 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, lvdcr0 |= LVDCR0_LVRES; rcar_lvds_write(lvds, LVDCR0, lvdcr0); - lvds->dpms = DRM_MODE_DPMS_ON; + lvds->enabled = true; return 0; } static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) { - if (lvds->dpms == DRM_MODE_DPMS_OFF) + if (!lvds->enabled) return; rcar_lvds_write(lvds, LVDCR0, 0); @@ -124,13 +124,13 @@ static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) clk_disable_unprepare(lvds->clock); - lvds->dpms = DRM_MODE_DPMS_OFF; + lvds->enabled = false; } -int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, - struct drm_crtc *crtc, int mode) +int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc, + bool enable) { - if (mode == DRM_MODE_DPMS_OFF) { + if (!enable) { rcar_du_lvdsenc_stop(lvds); return 0; } else if (crtc) { @@ -179,7 +179,7 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) lvds->dev = rcdu; lvds->index = i; lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; - lvds->dpms = DRM_MODE_DPMS_OFF; + lvds->enabled = false; ret = rcar_du_lvdsenc_get_resources(lvds, pdev); if (ret < 0) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h index f65aabda0796..9a6001c07303 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h @@ -28,15 +28,15 @@ enum rcar_lvds_input { #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); -int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, - struct drm_crtc *crtc, int mode); +int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, bool enable); #else static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) { return 0; } -static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, - struct drm_crtc *crtc, int mode) +static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, bool enable) { return 0; } From f6638b6abc298a9d4a13492a4d918e3c4ced3109 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 14:05:21 +0200 Subject: [PATCH 16/32] drm: rcar-du: Rework encoder enable/disable for atomic updates When using atomic updates the encoder .enable() and .disable() helper operations are preferred over the (then legacy) .prepare() and .commit() operations. Implement .enable() and .disable() and rework .prepare(), .commit() and .dpms() as wrappers around .enable() and .disable(), easing their future removal. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 6243af310368..74a744f5dfdb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -42,13 +42,28 @@ rcar_du_connector_best_encoder(struct drm_connector *connector) * Encoder */ -static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) +static void rcar_du_encoder_disable(struct drm_encoder *encoder) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); if (renc->lvds) - rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, - mode == DRM_MODE_DPMS_ON); + rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); +} + +static void rcar_du_encoder_enable(struct drm_encoder *encoder) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); +} + +static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + if (mode == DRM_MODE_DPMS_ON) + rcar_du_encoder_enable(encoder); + else + rcar_du_encoder_disable(encoder); } static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, @@ -105,18 +120,12 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) { - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - - if (renc->lvds) - rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); + rcar_du_encoder_disable(encoder); } static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) { - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - - if (renc->lvds) - rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); + rcar_du_encoder_enable(encoder); } static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, @@ -134,6 +143,8 @@ static const struct drm_encoder_helper_funcs encoder_helper_funcs = { .prepare = rcar_du_encoder_mode_prepare, .commit = rcar_du_encoder_mode_commit, .mode_set = rcar_du_encoder_mode_set, + .disable = rcar_du_encoder_disable, + .enable = rcar_du_encoder_enable, }; static const struct drm_encoder_funcs encoder_funcs = { From 58706b88c4a8f7423be3e521b457fdb88516aa30 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 14:05:21 +0200 Subject: [PATCH 17/32] drm: rcar-du: Rework HDMI encoder enable/disable for atomic updates When using atomic updates the encoder .enable() and .disable() helper operations are preferred over the (then legacy) .prepare() and .commit() operations. Implement .enable() and .disable() and rework .prepare(), .commit() and .dpms() as wrappers around .enable() and .disable(), easing their future removal. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 48 +++++++++++++++++------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 89a7c0ecc935..04e21b5f96d4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -26,35 +26,54 @@ struct rcar_du_hdmienc { struct rcar_du_encoder *renc; struct device *dev; - int dpms; + bool enabled; }; #define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi) #define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) -static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) +static void rcar_du_hdmienc_disable(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; + if (sfuncs->dpms) + sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF); - if (hdmienc->dpms == mode) - return; + if (hdmienc->renc->lvds) + rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, + false); - if (mode == DRM_MODE_DPMS_ON && hdmienc->renc->lvds) + hdmienc->enabled = false; +} + +static void rcar_du_hdmienc_enable(struct drm_encoder *encoder) +{ + struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); + + if (hdmienc->renc->lvds) rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, true); if (sfuncs->dpms) - sfuncs->dpms(encoder, mode); + sfuncs->dpms(encoder, DRM_MODE_DPMS_ON); - if (mode != DRM_MODE_DPMS_ON && hdmienc->renc->lvds) - rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, - false); + hdmienc->enabled = true; +} - hdmienc->dpms = mode; +static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) +{ + struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); + bool enable = mode == DRM_MODE_DPMS_ON; + + if (hdmienc->enabled == enable) + return; + + if (enable) + rcar_du_hdmienc_enable(encoder); + else + rcar_du_hdmienc_disable(encoder); } static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, @@ -106,13 +125,16 @@ static const struct drm_encoder_helper_funcs encoder_helper_funcs = { .prepare = rcar_du_hdmienc_mode_prepare, .commit = rcar_du_hdmienc_mode_commit, .mode_set = rcar_du_hdmienc_mode_set, + .disable = rcar_du_hdmienc_disable, + .enable = rcar_du_hdmienc_enable, }; static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF); + if (hdmienc->enabled) + rcar_du_hdmienc_disable(encoder); drm_encoder_cleanup(encoder); put_device(hdmienc->dev); From beff155a7eae6d376b391de5e95fd91d23664397 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 14:05:21 +0200 Subject: [PATCH 18/32] drm: rcar-du: Rework CRTC enable/disable for atomic updates When using atomic updates the CRTC .enable() and .disable() helper operations are preferred over the (then legacy) .prepare() and .commit() operations. Implement .enable() and rework .disable() to not depend on DPMS, easing DPMS removal later on. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 61 +++++++++++++++----------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 6e7266c1a47b..7fdb968756ba 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -431,7 +431,7 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { - if (rcrtc->dpms != DRM_MODE_DPMS_ON) + if (!rcrtc->enabled) return; rcar_du_crtc_get(rcrtc); @@ -450,25 +450,38 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) * CRTC Functions */ -static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) +static void rcar_du_crtc_enable(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; - - if (rcrtc->dpms == mode) + if (rcrtc->enabled) return; - if (mode == DRM_MODE_DPMS_ON) { - rcar_du_crtc_get(rcrtc); - rcar_du_crtc_start(rcrtc); - } else { - rcar_du_crtc_stop(rcrtc); - rcar_du_crtc_put(rcrtc); - } + rcar_du_crtc_get(rcrtc); + rcar_du_crtc_start(rcrtc); - rcrtc->dpms = mode; + rcrtc->enabled = true; +} + +static void rcar_du_crtc_disable(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + if (!rcrtc->enabled) + return; + + rcar_du_crtc_stop(rcrtc); + rcar_du_crtc_put(rcrtc); + + rcrtc->enabled = false; +} + +static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + if (mode == DRM_MODE_DPMS_ON) + rcar_du_crtc_enable(crtc); + else + rcar_du_crtc_disable(crtc); } static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, @@ -488,10 +501,10 @@ static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) */ rcar_du_crtc_get(rcrtc); - /* Stop the CRTC, force the DPMS mode to off as a result. */ + /* Stop the CRTC, force enabled to false as a result. */ rcar_du_crtc_stop(rcrtc); - rcrtc->dpms = DRM_MODE_DPMS_OFF; + rcrtc->enabled = false; rcrtc->outputs = 0; } @@ -508,17 +521,12 @@ static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - /* We're done, restart the CRTC and set the DPMS mode to on. The - * reference to the DU acquired at prepare() time will thus be released - * by the DPMS handler (possibly called by the disable() handler). + /* We're done, restart the CRTC and set enabled to true. The reference + * to the DU acquired at prepare() time will thus be released by the + * disable() handler. */ rcar_du_crtc_start(rcrtc); - rcrtc->dpms = DRM_MODE_DPMS_ON; -} - -static void rcar_du_crtc_disable(struct drm_crtc *crtc) -{ - rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + rcrtc->enabled = true; } static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) @@ -554,6 +562,7 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .mode_set_nofb = rcar_du_crtc_mode_set_nofb, .mode_set_base = drm_helper_crtc_mode_set_base, .disable = rcar_du_crtc_disable, + .enable = rcar_du_crtc_enable, .atomic_begin = rcar_du_crtc_atomic_begin, .atomic_flush = rcar_du_crtc_atomic_flush, }; @@ -670,7 +679,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) rcrtc->group = rgrp; rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; - rcrtc->dpms = DRM_MODE_DPMS_OFF; + rcrtc->enabled = false; rcrtc->plane = &rgrp->planes.planes[index % 2]; rcrtc->plane->crtc = crtc; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index fb39e040f17b..f50fbafd609f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -36,7 +36,7 @@ struct rcar_du_crtc { wait_queue_head_t flip_wait; unsigned int outputs; - int dpms; + bool enabled; struct rcar_du_group *group; struct rcar_du_plane *plane; From 336d04a18018c34f9a2cfd0adb24c3f8b7bedc45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 13:18:56 +0200 Subject: [PATCH 19/32] drm: rcar-du: Switch plane update to atomic helpers This removes the legacy plane update code. Wire up the default atomic check and atomic commit mode config helpers as needed by the plane update atomic helpers. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 3 +++ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 94ff8fcc7268..2b36f4f2980f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> @@ -188,6 +189,8 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, .output_poll_changed = rcar_du_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, }; static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index f247b1fcf791..ff087f7de88e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -480,8 +480,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, } static const struct drm_plane_funcs rcar_du_plane_funcs = { - .update_plane = drm_plane_helper_update, - .disable_plane = drm_plane_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .reset = drm_atomic_helper_plane_reset, .set_property = rcar_du_plane_set_property, .destroy = drm_plane_cleanup, From cf1cc6f24941bea868172120082fa2c4e11eb3a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Fri, 20 Feb 2015 15:16:55 +0200 Subject: [PATCH 20/32] drm: rcar-du: Switch mode config to atomic helpers This removes the legacy mode config code. The CRTC and encoder prepare and commit operations are not used anymore, remove them. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 55 +++-------------------- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ----- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 12 ----- 3 files changed, 6 insertions(+), 73 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 7fdb968756ba..f82893884a8a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -362,12 +362,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) /* FIXME: Commit the planes state. This is required here as the CRTC can * be started from the DPMS and system resume handler, which don't go * through .atomic_plane_update() and .atomic_flush() to commit plane - * state. Similarly a mode set operation without any update to planes - * will not go through atomic plane configuration either. Additionally, - * given that the plane state atomic commit occurs between CRTC disable - * and enable, the hardware state could also be lost due to runtime PM, - * requiring a full commit here. This will be fixed later after - * switching to atomic updates completely. + * state. Additionally, given that the plane state atomic commit occurs + * between CRTC disable and enable, the hardware state could also be + * lost due to runtime PM, requiring a full commit here. This will be + * fixed later after switching to atomic updates completely. */ mutex_lock(&rcrtc->group->planes.lock); rcar_du_crtc_update_planes(crtc); @@ -474,6 +472,7 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc) rcar_du_crtc_put(rcrtc); rcrtc->enabled = false; + rcrtc->outputs = 0; } static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) @@ -492,43 +491,6 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - - /* We need to access the hardware during mode set, acquire a reference - * to the CRTC. - */ - rcar_du_crtc_get(rcrtc); - - /* Stop the CRTC, force enabled to false as a result. */ - rcar_du_crtc_stop(rcrtc); - - rcrtc->enabled = false; - rcrtc->outputs = 0; -} - -static void rcar_du_crtc_mode_set_nofb(struct drm_crtc *crtc) -{ - /* No-op. We should configure the display timings here, but as we're - * called with the CRTC disabled clocks might be off, and we thus can't - * access the hardware. Let's just configure everything when enabling - * the CRTC. - */ -} - -static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - - /* We're done, restart the CRTC and set enabled to true. The reference - * to the DU acquired at prepare() time will thus be released by the - * disable() handler. - */ - rcar_du_crtc_start(rcrtc); - rcrtc->enabled = true; -} - static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); @@ -556,11 +518,6 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .dpms = rcar_du_crtc_dpms, .mode_fixup = rcar_du_crtc_mode_fixup, - .prepare = rcar_du_crtc_mode_prepare, - .commit = rcar_du_crtc_mode_commit, - .mode_set = drm_helper_crtc_mode_set, - .mode_set_nofb = rcar_du_crtc_mode_set_nofb, - .mode_set_base = drm_helper_crtc_mode_set_base, .disable = rcar_du_crtc_disable, .enable = rcar_du_crtc_enable, .atomic_begin = rcar_du_crtc_atomic_begin, @@ -602,7 +559,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, - .set_config = drm_crtc_helper_set_config, + .set_config = drm_atomic_helper_set_config, .page_flip = rcar_du_crtc_page_flip, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 74a744f5dfdb..76b201f2f75e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -118,16 +118,6 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, return true; } -static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) -{ - rcar_du_encoder_disable(encoder); -} - -static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) -{ - rcar_du_encoder_enable(encoder); -} - static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -140,8 +130,6 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs encoder_helper_funcs = { .dpms = rcar_du_encoder_dpms, .mode_fixup = rcar_du_encoder_mode_fixup, - .prepare = rcar_du_encoder_mode_prepare, - .commit = rcar_du_encoder_mode_commit, .mode_set = rcar_du_encoder_mode_set, .disable = rcar_du_encoder_disable, .enable = rcar_du_encoder_enable, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 04e21b5f96d4..a88ef2d6800e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -96,16 +96,6 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, return sfuncs->mode_fixup(encoder, mode, adjusted_mode); } -static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder) -{ - rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF); -} - -static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder) -{ - rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON); -} - static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -122,8 +112,6 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs encoder_helper_funcs = { .dpms = rcar_du_hdmienc_dpms, .mode_fixup = rcar_du_hdmienc_mode_fixup, - .prepare = rcar_du_hdmienc_mode_prepare, - .commit = rcar_du_hdmienc_mode_commit, .mode_set = rcar_du_hdmienc_mode_set, .disable = rcar_du_hdmienc_disable, .enable = rcar_du_hdmienc_enable, From f348323240b63f351b4ade84d75150e09fb9ff32 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Sun, 22 Feb 2015 01:49:11 +0200 Subject: [PATCH 21/32] drm: rcar-du: Switch connector DPMS to atomic helpers The atomic connector DPMS helper implements the connector DPMS operation using atomic commit, removing the need for DPMS helper operations on CRTCs and encoders. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 +---------- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 9 --------- drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c | 4 ++-- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 15 --------------- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 4 ++-- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 4 ++-- 6 files changed, 7 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f82893884a8a..8a44d37f1716 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -360,7 +360,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_group_set_routing(rcrtc->group); /* FIXME: Commit the planes state. This is required here as the CRTC can - * be started from the DPMS and system resume handler, which don't go + * be started from the system resume handler, which don't go * through .atomic_plane_update() and .atomic_flush() to commit plane * state. Additionally, given that the plane state atomic commit occurs * between CRTC disable and enable, the hardware state could also be @@ -475,14 +475,6 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc) rcrtc->outputs = 0; } -static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - if (mode == DRM_MODE_DPMS_ON) - rcar_du_crtc_enable(crtc); - else - rcar_du_crtc_disable(crtc); -} - static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -516,7 +508,6 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { - .dpms = rcar_du_crtc_dpms, .mode_fixup = rcar_du_crtc_mode_fixup, .disable = rcar_du_crtc_disable, .enable = rcar_du_crtc_enable, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 76b201f2f75e..d4a70228ce50 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -58,14 +58,6 @@ static void rcar_du_encoder_enable(struct drm_encoder *encoder) rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); } -static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - if (mode == DRM_MODE_DPMS_ON) - rcar_du_encoder_enable(encoder); - else - rcar_du_encoder_disable(encoder); -} - static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -128,7 +120,6 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_encoder_dpms, .mode_fixup = rcar_du_encoder_mode_fixup, .mode_set = rcar_du_encoder_mode_set, .disable = rcar_du_encoder_disable, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 9378c283cb58..96f2eb43713c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -75,7 +75,7 @@ rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -112,7 +112,7 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index a88ef2d6800e..3c29250c8abd 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -62,20 +62,6 @@ static void rcar_du_hdmienc_enable(struct drm_encoder *encoder) hdmienc->enabled = true; } -static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) -{ - struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - bool enable = mode == DRM_MODE_DPMS_ON; - - if (hdmienc->enabled == enable) - return; - - if (enable) - rcar_du_hdmienc_enable(encoder); - else - rcar_du_hdmienc_disable(encoder); -} - static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -110,7 +96,6 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_hdmienc_dpms, .mode_fixup = rcar_du_hdmienc_mode_fixup, .mode_set = rcar_du_hdmienc_mode_set, .disable = rcar_du_hdmienc_disable, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 4bfa3ce34ab3..0c43032fc693 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -75,7 +75,7 @@ rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_lvds_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -121,7 +121,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index a110cf22f3b0..e0a5d8f93963 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -44,7 +44,7 @@ rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_vga_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -80,7 +80,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); From ede7714f2e5ee1c7032d2865f6cc73158ff39f45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Sun, 22 Feb 2015 21:02:39 +0200 Subject: [PATCH 22/32] drm: rcar-du: Replace encoder mode_fixup with atomic_check The encoder .mode_fixup() operation is legacy, atomic updates uses the new .atomic_check() operation. Convert the encoders drivers. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 35 ++++++++--------------- drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 14 +++++---- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index d4a70228ce50..d0ae1e8009c6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -58,35 +58,24 @@ static void rcar_du_encoder_enable(struct drm_encoder *encoder) rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); } -static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + const struct drm_display_mode *mode = &crtc_state->mode; const struct drm_display_mode *panel_mode; + struct drm_connector *connector = conn_state->connector; struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - bool found = false; /* DAC encoders have currently no restriction on the mode. */ if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) - return true; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - found = true; - break; - } - } - - if (!found) { - dev_dbg(dev->dev, "mode_fixup: no connector found\n"); - return false; - } + return 0; if (list_empty(&connector->modes)) { - dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); - return false; + dev_dbg(dev->dev, "encoder: empty modes list\n"); + return -EINVAL; } panel_mode = list_first_entry(&connector->modes, @@ -95,7 +84,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, /* We're not allowed to modify the resolution. */ if (mode->hdisplay != panel_mode->hdisplay || mode->vdisplay != panel_mode->vdisplay) - return false; + return -EINVAL; /* The flat panel mode is fixed, just copy it to the adjusted mode. */ drm_mode_copy(adjusted_mode, panel_mode); @@ -107,7 +96,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, adjusted_mode->clock = clamp(adjusted_mode->clock, 30000, 150000); - return true; + return 0; } static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, @@ -120,10 +109,10 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .mode_fixup = rcar_du_encoder_mode_fixup, .mode_set = rcar_du_encoder_mode_set, .disable = rcar_du_encoder_disable, .enable = rcar_du_encoder_enable, + .atomic_check = rcar_du_encoder_atomic_check, }; static const struct drm_encoder_funcs encoder_funcs = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 3c29250c8abd..81da8419282b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -62,12 +62,14 @@ static void rcar_du_hdmienc_enable(struct drm_encoder *encoder) hdmienc->enabled = true; } -static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + const struct drm_display_mode *mode = &crtc_state->mode; /* The internal LVDS encoder has a clock frequency operating range of * 30MHz to 150MHz. Clamp the clock accordingly. @@ -77,9 +79,9 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, 30000, 150000); if (sfuncs->mode_fixup == NULL) - return true; + return 0; - return sfuncs->mode_fixup(encoder, mode, adjusted_mode); + return sfuncs->mode_fixup(encoder, mode, adjusted_mode) ? 0 : -EINVAL; } static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, @@ -96,10 +98,10 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .mode_fixup = rcar_du_hdmienc_mode_fixup, .mode_set = rcar_du_hdmienc_mode_set, .disable = rcar_du_hdmienc_disable, .enable = rcar_du_hdmienc_enable, + .atomic_check = rcar_du_hdmienc_atomic_check, }; static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder) From 8d3f9b22891acbc1fd06df80c62f5ac0e3b3d3a5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 01:02:15 +0200 Subject: [PATCH 23/32] drm: rcar-du: Implement asynchronous commit support Implement a custom .atomic_commit() handler that supports asynchronous commits using a work queue. This can be used for userspace-driven asynchronous commits, as well as for an atomic page flip implementation. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 3 + drivers/gpu/drm/rcar-du/rcar_du_drv.h | 6 ++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 105 +++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 8ab4c69c599b..1d9e4f8568ae 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/slab.h> +#include <linux/wait.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -163,6 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) return -ENOMEM; } + init_waitqueue_head(&rcdu->commit.wait); + rcdu->dev = &pdev->dev; rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data : (void *)platform_get_device_id(pdev)->driver_data; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index d1ede13cb1d5..c7c538dd2e68 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -15,6 +15,7 @@ #define __RCAR_DU_DRV_H__ #include <linux/kernel.h> +#include <linux/wait.h> #include "rcar_du_crtc.h" #include "rcar_du_group.h" @@ -84,6 +85,11 @@ struct rcar_du_device { unsigned int dpad0_source; struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS]; + + struct { + wait_queue_head_t wait; + u32 pending; + } commit; }; static inline bool rcar_du_has(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 2b36f4f2980f..cec35e405248 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -19,6 +20,7 @@ #include <drm/drm_gem_cma_helper.h> #include <linux/of_graph.h> +#include <linux/wait.h> #include "rcar_du_crtc.h" #include "rcar_du_drv.h" @@ -186,11 +188,112 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(rcdu->fbdev); } +/* ----------------------------------------------------------------------------- + * Atomic Updates + */ + +struct rcar_du_commit { + struct work_struct work; + struct drm_device *dev; + struct drm_atomic_state *state; + u32 crtcs; +}; + +static void rcar_du_atomic_complete(struct rcar_du_commit *commit) +{ + struct drm_device *dev = commit->dev; + struct rcar_du_device *rcdu = dev->dev_private; + struct drm_atomic_state *old_state = commit->state; + + /* Apply the atomic update. */ + drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_planes(dev, old_state); + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_wait_for_vblanks(dev, old_state); + + drm_atomic_helper_cleanup_planes(dev, old_state); + + drm_atomic_state_free(old_state); + + /* Complete the commit, wake up any waiter. */ + spin_lock(&rcdu->commit.wait.lock); + rcdu->commit.pending &= ~commit->crtcs; + wake_up_all_locked(&rcdu->commit.wait); + spin_unlock(&rcdu->commit.wait.lock); + + kfree(commit); +} + +static void rcar_du_atomic_work(struct work_struct *work) +{ + struct rcar_du_commit *commit = + container_of(work, struct rcar_du_commit, work); + + rcar_du_atomic_complete(commit); +} + +static int rcar_du_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, bool async) +{ + struct rcar_du_device *rcdu = dev->dev_private; + struct rcar_du_commit *commit; + unsigned int i; + int ret; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + /* Allocate the commit object. */ + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (commit == NULL) + return -ENOMEM; + + INIT_WORK(&commit->work, rcar_du_atomic_work); + commit->dev = dev; + commit->state = state; + + /* Wait until all affected CRTCs have completed previous commits and + * mark them as pending. + */ + for (i = 0; i < dev->mode_config.num_crtc; ++i) { + if (state->crtcs[i]) + commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); + } + + spin_lock(&rcdu->commit.wait.lock); + ret = wait_event_interruptible_locked(rcdu->commit.wait, + !(rcdu->commit.pending & commit->crtcs)); + if (ret == 0) + rcdu->commit.pending |= commit->crtcs; + spin_unlock(&rcdu->commit.wait.lock); + + if (ret) { + kfree(commit); + return ret; + } + + /* Swap the state, this is the point of no return. */ + drm_atomic_helper_swap_state(dev, state); + + if (async) + schedule_work(&commit->work); + else + rcar_du_atomic_complete(commit); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, .output_poll_changed = rcar_du_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = rcar_du_atomic_commit, }; static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, From d5746642f480c134e8e76a3104b987c08f790283 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 01:04:21 +0200 Subject: [PATCH 24/32] drm: rcar-du: Switch page flip to atomic helpers The atomic page flip helper implements the page flip operation using asynchronous commits. As the legacy page flip was the last CRTC operation that needed direct access to plane setup, the plane setup functions can now become private to the plane implementation. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 55 +++++++------------------ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 6 +-- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 3 -- 3 files changed, 17 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 8a44d37f1716..2a1119b52c2a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -436,14 +436,6 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) rcar_du_crtc_start(rcrtc); } -static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) -{ - struct drm_crtc *crtc = &rcrtc->crtc; - - rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb); - rcar_du_plane_update_base(rcrtc->plane); -} - /* ----------------------------------------------------------------------------- * CRTC Functions */ @@ -485,12 +477,25 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) { + struct drm_pending_vblank_event *event = crtc->state->event; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; /* We need to access the hardware during atomic update, acquire a * reference to the CRTC. */ rcar_du_crtc_get(rcrtc); + + if (event) { + event->pipe = rcrtc->index; + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&dev->event_lock, flags); + rcrtc->event = event; + spin_unlock_irqrestore(&dev->event_lock, flags); + } } static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) @@ -515,43 +520,11 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .atomic_flush = rcar_du_crtc_atomic_flush, }; -static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - if (rcrtc->event != NULL) { - spin_unlock_irqrestore(&dev->event_lock, flags); - return -EBUSY; - } - spin_unlock_irqrestore(&dev->event_lock, flags); - - drm_atomic_set_fb_for_plane(crtc->primary->state, fb); - - crtc->primary->fb = fb; - rcar_du_crtc_update_base(rcrtc); - - if (event) { - event->pipe = rcrtc->index; - drm_crtc_vblank_get(crtc); - spin_lock_irqsave(&dev->event_lock, flags); - rcrtc->event = event; - spin_unlock_irqrestore(&dev->event_lock, flags); - } - - return 0; -} - static const struct drm_crtc_funcs crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, - .page_flip = rcar_du_crtc_page_flip, + .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index ff087f7de88e..9176ac451438 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -129,7 +129,7 @@ static void rcar_du_plane_release(struct rcar_du_plane *plane) plane->hwindex = -1; } -void rcar_du_plane_update_base(struct rcar_du_plane *plane) +static void rcar_du_plane_update_base(struct rcar_du_plane *plane) { struct rcar_du_group *rgrp = plane->group; unsigned int src_x = plane->plane.state->src_x >> 16; @@ -182,8 +182,8 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) } } -void rcar_du_plane_compute_base(struct rcar_du_plane *plane, - struct drm_framebuffer *fb) +static void rcar_du_plane_compute_base(struct rcar_du_plane *plane, + struct drm_framebuffer *fb) { struct drm_gem_cma_object *gem; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 3420ff07f2c6..9b77a638863f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -62,8 +62,5 @@ struct rcar_du_planes { int rcar_du_planes_init(struct rcar_du_group *rgrp); void rcar_du_plane_setup(struct rcar_du_plane *plane); -void rcar_du_plane_update_base(struct rcar_du_plane *plane); -void rcar_du_plane_compute_base(struct rcar_du_plane *plane, - struct drm_framebuffer *fb); #endif /* __RCAR_DU_PLANE_H__ */ From 4407cc02c9251b0ce6bad3718211353a7dba93ef Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 02:36:31 +0200 Subject: [PATCH 25/32] drm: rcar-du: Switch plane set_property to atomic helpers Allow setting up plane properties atomically using the plane set_property atomic helper. The properties are now stored in the plane state (requiring subclassing it) and applied when updating the planes. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 9 +- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 1 - drivers/gpu/drm/rcar-du/rcar_du_plane.c | 131 ++++++++++++++---------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 17 ++- 4 files changed, 98 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 2a1119b52c2a..faa46ed23a62 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -193,7 +193,12 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, rcdu->dpad0_source = rcrtc->index; } -void rcar_du_crtc_update_planes(struct drm_crtc *crtc) +static unsigned int plane_zpos(struct rcar_du_plane *plane) +{ + return to_rcar_du_plane_state(plane->plane.state)->zpos; +} + +static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; @@ -212,7 +217,7 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) /* Insert the plane in the sorted planes array. */ for (j = num_planes++; j > 0; --j) { - if (planes[j-1]->zpos <= plane->zpos) + if (plane_zpos(planes[j-1]) <= plane_zpos(plane)) break; planes[j] = planes[j-1]; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index f50fbafd609f..0a3dcca58fd7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -62,6 +62,5 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_route_output(struct drm_crtc *crtc, enum rcar_du_output output); -void rcar_du_crtc_update_planes(struct drm_crtc *crtc); #endif /* __RCAR_DU_CRTC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 9176ac451438..7e0e2731ea27 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -201,6 +201,8 @@ static void rcar_du_plane_compute_base(struct rcar_du_plane *plane, static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, unsigned int index) { + struct rcar_du_plane_state *state = + to_rcar_du_plane_state(plane->plane.state); struct rcar_du_group *rgrp = plane->group; u32 colorkey; u32 pnmr; @@ -218,7 +220,7 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); else rcar_du_plane_write(rgrp, index, PnALPHAR, - PnALPHAR_ABIT_X | plane->alpha); + PnALPHAR_ABIT_X | state->alpha); pnmr = PnMR_BM_MD | plane->format->pnmr; @@ -226,7 +228,7 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying * automatically. */ - if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) + if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) pnmr |= PnMR_SPIM_TP_OFF; /* For packed YUV formats we need to select the U/V order. */ @@ -237,24 +239,24 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, switch (plane->format->fourcc) { case DRM_FORMAT_RGB565: - colorkey = ((plane->colorkey & 0xf80000) >> 8) - | ((plane->colorkey & 0x00fc00) >> 5) - | ((plane->colorkey & 0x0000f8) >> 3); + colorkey = ((state->colorkey & 0xf80000) >> 8) + | ((state->colorkey & 0x00fc00) >> 5) + | ((state->colorkey & 0x0000f8) >> 3); rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break; case DRM_FORMAT_ARGB1555: case DRM_FORMAT_XRGB1555: - colorkey = ((plane->colorkey & 0xf80000) >> 9) - | ((plane->colorkey & 0x00f800) >> 6) - | ((plane->colorkey & 0x0000f8) >> 3); + colorkey = ((state->colorkey & 0xf80000) >> 9) + | ((state->colorkey & 0x00f800) >> 6) + | ((state->colorkey & 0x0000f8) >> 3); rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: rcar_du_plane_write(rgrp, index, PnTC3R, - PnTC3R_CODE | (plane->colorkey & 0xffffff)); + PnTC3R_CODE | (state->colorkey & 0xffffff)); break; } } @@ -414,65 +416,87 @@ static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { .atomic_update = rcar_du_plane_atomic_update, }; -/* Both the .set_property and the .update_plane operations are called with the - * mode_config lock held. There is this no need to explicitly protect access to - * the alpha and colorkey fields and the mode register. - */ -static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha) +static void rcar_du_plane_reset(struct drm_plane *plane) { - if (plane->alpha == alpha) + struct rcar_du_plane_state *state; + + if (plane->state && plane->state->fb) + drm_framebuffer_unreference(plane->state->fb); + + kfree(plane->state); + plane->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) return; - plane->alpha = alpha; - if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555) - return; + state->alpha = 255; + state->colorkey = RCAR_DU_COLORKEY_NONE; + state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; - rcar_du_plane_setup_mode(plane, plane->hwindex); + plane->state = &state->state; + plane->state->plane = plane; } -static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane, - u32 colorkey) +static struct drm_plane_state * +rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane) { - if (plane->colorkey == colorkey) - return; + struct rcar_du_plane_state *state; + struct rcar_du_plane_state *copy; - plane->colorkey = colorkey; - if (!plane->enabled) - return; + state = to_rcar_du_plane_state(plane->state); + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (copy == NULL) + return NULL; - rcar_du_plane_setup_mode(plane, plane->hwindex); + if (copy->state.fb) + drm_framebuffer_reference(copy->state.fb); + + return ©->state; } -static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, - unsigned int zpos) +static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) { - mutex_lock(&plane->group->planes.lock); - if (plane->zpos == zpos) - goto done; - - plane->zpos = zpos; - if (!plane->enabled) - goto done; - - rcar_du_crtc_update_planes(plane->crtc); - -done: - mutex_unlock(&plane->group->planes.lock); + kfree(to_rcar_du_plane_state(state)); } -static int rcar_du_plane_set_property(struct drm_plane *plane, - struct drm_property *property, - uint64_t value) +static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) { + struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group; if (property == rgrp->planes.alpha) - rcar_du_plane_set_alpha(rplane, value); + rstate->alpha = val; else if (property == rgrp->planes.colorkey) - rcar_du_plane_set_colorkey(rplane, value); + rstate->colorkey = val; else if (property == rgrp->planes.zpos) - rcar_du_plane_set_zpos(rplane, value); + rstate->zpos = val; + else + return -EINVAL; + + return 0; +} + +static int rcar_du_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, struct drm_property *property, + uint64_t *val) +{ + const struct rcar_du_plane_state *rstate = + container_of(state, const struct rcar_du_plane_state, state); + struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct rcar_du_group *rgrp = rplane->group; + + if (property == rgrp->planes.alpha) + *val = rstate->alpha; + else if (property == rgrp->planes.colorkey) + *val = rstate->colorkey; + else if (property == rgrp->planes.zpos) + *val = rstate->zpos; else return -EINVAL; @@ -482,11 +506,13 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, static const struct drm_plane_funcs rcar_du_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, - .set_property = rcar_du_plane_set_property, + .reset = rcar_du_plane_reset, + .set_property = drm_atomic_helper_plane_set_property, .destroy = drm_plane_cleanup, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state, + .atomic_destroy_state = rcar_du_plane_atomic_destroy_state, + .atomic_set_property = rcar_du_plane_atomic_set_property, + .atomic_get_property = rcar_du_plane_atomic_get_property, }; static const uint32_t formats[] = { @@ -551,9 +577,6 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) plane->group = rgrp; plane->hwindex = -1; - plane->alpha = 255; - plane->colorkey = RCAR_DU_COLORKEY_NONE; - plane->zpos = type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, &rcar_du_plane_funcs, formats, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 9b77a638863f..d291e85896ef 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -39,9 +39,6 @@ struct rcar_du_plane { bool enabled; int hwindex; /* 0-based, -1 means unused */ - unsigned int alpha; - unsigned int colorkey; - unsigned int zpos; const struct rcar_du_format_info *format; @@ -59,6 +56,20 @@ struct rcar_du_planes { struct drm_property *zpos; }; +struct rcar_du_plane_state { + struct drm_plane_state state; + + unsigned int alpha; + unsigned int colorkey; + unsigned int zpos; +}; + +static inline struct rcar_du_plane_state * +to_rcar_du_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct rcar_du_plane_state, state); +} + int rcar_du_planes_init(struct rcar_du_group *rgrp); void rcar_du_plane_setup(struct rcar_du_plane *plane); From f398f344eb59cd9803091ee08bee77b4e473971e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 01:25:19 +0200 Subject: [PATCH 26/32] drm: rcar-du: Rework plane setup code Now that the plane setup code isn't called outside of the plane implementation, it can be simplified by merging the rcar_du_plane_compute_base() and rcar_du_plane_update_base() functions. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 34 +++++++++---------------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 3 --- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 7e0e2731ea27..f28d13dc6fc9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -129,12 +129,14 @@ static void rcar_du_plane_release(struct rcar_du_plane *plane) plane->hwindex = -1; } -static void rcar_du_plane_update_base(struct rcar_du_plane *plane) +static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) { + struct drm_framebuffer *fb = plane->plane.state->fb; struct rcar_du_group *rgrp = plane->group; unsigned int src_x = plane->plane.state->src_x >> 16; unsigned int src_y = plane->plane.state->src_y >> 16; unsigned int index = plane->hwindex; + struct drm_gem_cma_object *gem; bool interlaced; u32 mwr; @@ -144,9 +146,9 @@ static void rcar_du_plane_update_base(struct rcar_du_plane *plane) * operation with 32bpp formats. */ if (plane->format->planes == 2) - mwr = plane->pitch; + mwr = fb->pitches[0]; else - mwr = plane->pitch * 8 / plane->format->bpp; + mwr = fb->pitches[0] * 8 / plane->format->bpp; if (interlaced && plane->format->bpp == 32) mwr *= 2; @@ -168,33 +170,22 @@ static void rcar_du_plane_update_base(struct rcar_du_plane *plane) rcar_du_plane_write(rgrp, index, PnSPXR, src_x); rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (!interlaced && plane->format->bpp == 32 ? 2 : 1)); - rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]); + + gem = drm_fb_cma_get_gem_obj(fb, 0); + rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]); if (plane->format->planes == 2) { index = (index + 1) % 8; - rcar_du_plane_write(rgrp, index, PnMWR, plane->pitch); + rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]); rcar_du_plane_write(rgrp, index, PnSPXR, src_x); rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (plane->format->bpp == 16 ? 2 : 1) / 2); - rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]); - } -} -static void rcar_du_plane_compute_base(struct rcar_du_plane *plane, - struct drm_framebuffer *fb) -{ - struct drm_gem_cma_object *gem; - - plane->pitch = fb->pitches[0]; - - gem = drm_fb_cma_get_gem_obj(fb, 0); - plane->dma[0] = gem->paddr + fb->offsets[0]; - - if (plane->format->planes == 2) { gem = drm_fb_cma_get_gem_obj(fb, 1); - plane->dma[1] = gem->paddr + fb->offsets[1]; + rcar_du_plane_write(rgrp, index, PnDSA0R, + gem->paddr + fb->offsets[1]); } } @@ -316,7 +307,7 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane) if (plane->format->planes == 2) __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8); - rcar_du_plane_update_base(plane); + rcar_du_plane_setup_fb(plane); } static int rcar_du_plane_atomic_check(struct drm_plane *plane, @@ -403,7 +394,6 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, rplane->crtc = state->crtc; rplane->format = format; - rcar_du_plane_compute_base(rplane, state->fb); rcar_du_plane_setup(rplane); mutex_lock(&rplane->group->planes.lock); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index d291e85896ef..012f2185ca1f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -41,9 +41,6 @@ struct rcar_du_plane { int hwindex; /* 0-based, -1 means unused */ const struct rcar_du_format_info *format; - - unsigned long dma[2]; - unsigned int pitch; }; struct rcar_du_planes { From 47094194f070a5099854141bdbd54eae68e5ffd5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Sun, 22 Feb 2015 19:24:59 +0200 Subject: [PATCH 27/32] drm: rcar-du: Replace plane crtc and enabled fields by plane state The crtc and enabled fields duplicates information stored in the plane state. Use the plane state instead and remove the fields. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 6 ++---- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 15 +++------------ drivers/gpu/drm/rcar-du/rcar_du_plane.h | 3 --- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index faa46ed23a62..3f64707412e6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -212,7 +212,7 @@ static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; unsigned int j; - if (plane->crtc != &rcrtc->crtc || !plane->enabled) + if (plane->plane.state->crtc != &rcrtc->crtc) continue; /* Insert the plane in the sorted planes array. */ @@ -379,7 +379,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; - if (plane->crtc != crtc || !plane->enabled) + if (plane->plane.state->crtc != crtc) continue; rcar_du_plane_setup(plane); @@ -608,8 +608,6 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) rcrtc->enabled = false; rcrtc->plane = &rgrp->planes.planes[index % 2]; - rcrtc->plane->crtc = crtc; - ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, &rcrtc->plane->plane, NULL, &crtc_funcs); if (ret < 0) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index f28d13dc6fc9..c5cb2ade2722 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -140,7 +140,8 @@ static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) bool interlaced; u32 mwr; - interlaced = plane->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE; + interlaced = plane->plane.state->crtc->state->adjusted_mode.flags + & DRM_MODE_FLAG_INTERLACE; /* Memory pitch (expressed in pixels). Must be doubled for interlaced * operation with 32bpp formats. @@ -354,16 +355,11 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, static void rcar_du_plane_disable(struct rcar_du_plane *rplane) { - if (!rplane->enabled) + if (!rplane->plane.state->crtc) return; - mutex_lock(&rplane->group->planes.lock); - rplane->enabled = false; - mutex_unlock(&rplane->group->planes.lock); - rcar_du_plane_release(rplane); - rplane->crtc = NULL; rplane->format = NULL; } @@ -391,14 +387,9 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, rcar_du_plane_reserve(rplane, format); } - rplane->crtc = state->crtc; rplane->format = format; rcar_du_plane_setup(rplane); - - mutex_lock(&rplane->group->planes.lock); - rplane->enabled = true; - mutex_unlock(&rplane->group->planes.lock); } static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 012f2185ca1f..7050fc1e11ec 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -34,9 +34,6 @@ struct rcar_du_plane { struct drm_plane plane; struct rcar_du_group *group; - struct drm_crtc *crtc; - - bool enabled; int hwindex; /* 0-based, -1 means unused */ From 53dff601790ebc3d4caffef12b2c4754f5886c64 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 03:20:39 +0200 Subject: [PATCH 28/32] drm: rcar-du: Remove unneeded rcar_du_crtc plane field The rcar_du_crtc plane field is only used to check for an error that can't occur. Remove it. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 ++----- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 3f64707412e6..c70f73036056 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -353,9 +353,6 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) if (rcrtc->started) return; - if (WARN_ON(rcrtc->plane->format == NULL)) - return; - /* Set display off and background to black */ rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0)); rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0)); @@ -606,9 +603,9 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; rcrtc->enabled = false; - rcrtc->plane = &rgrp->planes.planes[index % 2]; - ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, &rcrtc->plane->plane, + ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, + &rgrp->planes.planes[index % 2].plane, NULL, &crtc_funcs); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 0a3dcca58fd7..5d9aa9b33769 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -21,7 +21,6 @@ #include <drm/drm_crtc.h> struct rcar_du_group; -struct rcar_du_plane; struct rcar_du_crtc { struct drm_crtc crtc; @@ -39,7 +38,6 @@ struct rcar_du_crtc { bool enabled; struct rcar_du_group *group; - struct rcar_du_plane *plane; }; #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) From 5bfcbce0eaeb884e258648e5ceb74a61cfb80f3c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 02:59:35 +0200 Subject: [PATCH 29/32] drm: rcar-du: Move plane format to plane state The format stored in the rcar_du_plane structure is part of the plane state. Move it to the rcar_du_plane_state structure and precompute it in the .atomic_check() handler. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 ++- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 107 ++++++++++++++---------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 6 +- 3 files changed, 71 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index c70f73036056..98f770622b2d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -198,6 +198,12 @@ static unsigned int plane_zpos(struct rcar_du_plane *plane) return to_rcar_du_plane_state(plane->plane.state)->zpos; } +static const struct rcar_du_format_info * +plane_format(struct rcar_du_plane *plane) +{ + return to_rcar_du_plane_state(plane->plane.state)->format; +} + static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); @@ -223,7 +229,7 @@ static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) } planes[j] = plane; - prio += plane->format->planes * 4; + prio += plane_format(plane)->planes * 4; } for (i = 0; i < num_planes; ++i) { @@ -234,7 +240,7 @@ static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) dspr |= (index + 1) << prio; dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index); - if (plane->format->planes == 2) { + if (plane_format(plane)->planes == 2) { index = (index + 1) % 8; prio -= 4; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index c5cb2ade2722..58b9e02e6910 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -48,7 +48,8 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, } static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, - const struct rcar_du_format_info *format) + const struct rcar_du_format_info *cur_format, + const struct rcar_du_format_info *new_format) { struct rcar_du_group *rgrp = plane->group; unsigned int free; @@ -61,7 +62,7 @@ static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, if (plane->hwindex != -1) { free |= 1 << plane->hwindex; - if (plane->format->planes == 2) + if (cur_format->planes == 2) free |= 1 << ((plane->hwindex + 1) % 8); } @@ -69,7 +70,7 @@ static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, if (!(free & (1 << i))) continue; - if (format->planes == 1 || free & (1 << ((i + 1) % 8))) + if (new_format->planes == 1 || free & (1 << ((i + 1) % 8))) break; } @@ -113,7 +114,8 @@ done: return ret; } -static void rcar_du_plane_release(struct rcar_du_plane *plane) +static void rcar_du_plane_release(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format) { struct rcar_du_group *rgrp = plane->group; @@ -122,7 +124,7 @@ static void rcar_du_plane_release(struct rcar_du_plane *plane) mutex_lock(&rgrp->planes.lock); rgrp->planes.free |= 1 << plane->hwindex; - if (plane->format->planes == 2) + if (format->planes == 2) rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8); mutex_unlock(&rgrp->planes.lock); @@ -131,27 +133,29 @@ static void rcar_du_plane_release(struct rcar_du_plane *plane) static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) { + struct rcar_du_plane_state *state = + to_rcar_du_plane_state(plane->plane.state); struct drm_framebuffer *fb = plane->plane.state->fb; struct rcar_du_group *rgrp = plane->group; - unsigned int src_x = plane->plane.state->src_x >> 16; - unsigned int src_y = plane->plane.state->src_y >> 16; + unsigned int src_x = state->state.src_x >> 16; + unsigned int src_y = state->state.src_y >> 16; unsigned int index = plane->hwindex; struct drm_gem_cma_object *gem; bool interlaced; u32 mwr; - interlaced = plane->plane.state->crtc->state->adjusted_mode.flags + interlaced = state->state.crtc->state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE; /* Memory pitch (expressed in pixels). Must be doubled for interlaced * operation with 32bpp formats. */ - if (plane->format->planes == 2) + if (state->format->planes == 2) mwr = fb->pitches[0]; else - mwr = fb->pitches[0] * 8 / plane->format->bpp; + mwr = fb->pitches[0] * 8 / state->format->bpp; - if (interlaced && plane->format->bpp == 32) + if (interlaced && state->format->bpp == 32) mwr *= 2; rcar_du_plane_write(rgrp, index, PnMWR, mwr); @@ -170,19 +174,19 @@ static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) */ rcar_du_plane_write(rgrp, index, PnSPXR, src_x); rcar_du_plane_write(rgrp, index, PnSPYR, src_y * - (!interlaced && plane->format->bpp == 32 ? 2 : 1)); + (!interlaced && state->format->bpp == 32 ? 2 : 1)); gem = drm_fb_cma_get_gem_obj(fb, 0); rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]); - if (plane->format->planes == 2) { + if (state->format->planes == 2) { index = (index + 1) % 8; rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]); rcar_du_plane_write(rgrp, index, PnSPXR, src_x); rcar_du_plane_write(rgrp, index, PnSPYR, src_y * - (plane->format->bpp == 16 ? 2 : 1) / 2); + (state->format->bpp == 16 ? 2 : 1) / 2); gem = drm_fb_cma_get_gem_obj(fb, 1); rcar_du_plane_write(rgrp, index, PnDSA0R, @@ -208,13 +212,13 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, * For XRGB, set the alpha value to the plane-wide alpha value and * enable alpha-blending regardless of the X bit value. */ - if (plane->format->fourcc != DRM_FORMAT_XRGB1555) + if (state->format->fourcc != DRM_FORMAT_XRGB1555) rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); else rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_X | state->alpha); - pnmr = PnMR_BM_MD | plane->format->pnmr; + pnmr = PnMR_BM_MD | state->format->pnmr; /* Disable color keying when requested. YUV formats have the * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying @@ -224,12 +228,12 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, pnmr |= PnMR_SPIM_TP_OFF; /* For packed YUV formats we need to select the U/V order. */ - if (plane->format->fourcc == DRM_FORMAT_YUYV) + if (state->format->fourcc == DRM_FORMAT_YUYV) pnmr |= PnMR_YCDF_YUYV; rcar_du_plane_write(rgrp, index, PnMR, pnmr); - switch (plane->format->fourcc) { + switch (state->format->fourcc) { case DRM_FORMAT_RGB565: colorkey = ((state->colorkey & 0xf80000) >> 8) | ((state->colorkey & 0x00fc00) >> 5) @@ -256,6 +260,8 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, static void __rcar_du_plane_setup(struct rcar_du_plane *plane, unsigned int index) { + struct rcar_du_plane_state *state = + to_rcar_du_plane_state(plane->plane.state); struct rcar_du_group *rgrp = plane->group; u32 ddcr2 = PnDDCR2_CODE; u32 ddcr4; @@ -267,17 +273,17 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, */ ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); ddcr4 &= ~PnDDCR4_EDF_MASK; - ddcr4 |= plane->format->edf | PnDDCR4_CODE; + ddcr4 |= state->format->edf | PnDDCR4_CODE; rcar_du_plane_setup_mode(plane, index); - if (plane->format->planes == 2) { + if (state->format->planes == 2) { if (plane->hwindex != index) { - if (plane->format->fourcc == DRM_FORMAT_NV12 || - plane->format->fourcc == DRM_FORMAT_NV21) + if (state->format->fourcc == DRM_FORMAT_NV12 || + state->format->fourcc == DRM_FORMAT_NV21) ddcr2 |= PnDDCR2_Y420; - if (plane->format->fourcc == DRM_FORMAT_NV21) + if (state->format->fourcc == DRM_FORMAT_NV21) ddcr2 |= PnDDCR2_NV21; ddcr2 |= PnDDCR2_DIVU; @@ -304,8 +310,11 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, void rcar_du_plane_setup(struct rcar_du_plane *plane) { + struct rcar_du_plane_state *state = + to_rcar_du_plane_state(plane->plane.state); + __rcar_du_plane_setup(plane, plane->hwindex); - if (plane->format->planes == 2) + if (state->format->planes == 2) __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8); rcar_du_plane_setup_fb(plane); @@ -314,10 +323,11 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane) static int rcar_du_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { + struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_device *rcdu = rplane->group->dev; - const struct rcar_du_format_info *format; - unsigned int nplanes; + const struct rcar_du_format_info *cur_format; + const struct rcar_du_format_info *new_format; int ret; if (!state->fb || !state->crtc) @@ -329,20 +339,20 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - format = rcar_du_format_info(state->fb->pixel_format); - if (format == NULL) { + cur_format = to_rcar_du_plane_state(plane->state)->format; + new_format = rcar_du_format_info(state->fb->pixel_format); + if (new_format == NULL) { dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, state->fb->pixel_format); return -EINVAL; } - nplanes = rplane->format ? rplane->format->planes : 0; - /* If the number of required planes has changed we will need to * reallocate hardware planes. Ensure free planes are available. */ - if (format->planes != nplanes) { - ret = rcar_du_plane_reserve_check(rplane, format); + if (!cur_format || new_format->planes != cur_format->planes) { + ret = rcar_du_plane_reserve_check(rplane, cur_format, + new_format); if (ret < 0) { dev_dbg(rcdu->dev, "%s: no available hardware plane\n", __func__); @@ -350,17 +360,23 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, } } + rstate->format = new_format; + return 0; } -static void rcar_du_plane_disable(struct rcar_du_plane *rplane) +static void rcar_du_plane_disable(struct rcar_du_plane *rplane, + const struct rcar_du_format_info *cur_format) { + struct rcar_du_plane_state *rstate = + to_rcar_du_plane_state(rplane->plane.state); + if (!rplane->plane.state->crtc) return; - rcar_du_plane_release(rplane); + rcar_du_plane_release(rplane, cur_format); - rplane->format = NULL; + rstate->format = NULL; } static void rcar_du_plane_atomic_update(struct drm_plane *plane, @@ -368,27 +384,26 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct drm_plane_state *state = plane->state; - const struct rcar_du_format_info *format; - unsigned int nplanes; + struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); + const struct rcar_du_format_info *cur_format; + const struct rcar_du_format_info *new_format; + + cur_format = to_rcar_du_plane_state(old_state)->format; + new_format = rstate->format; if (!state->crtc) { - rcar_du_plane_disable(rplane); + rcar_du_plane_disable(rplane, cur_format); return; } - format = rcar_du_format_info(state->fb->pixel_format); - nplanes = rplane->format ? rplane->format->planes : 0; - /* Reallocate hardware planes if the number of required planes has * changed. */ - if (format->planes != nplanes) { - rcar_du_plane_release(rplane); - rcar_du_plane_reserve(rplane, format); + if (!cur_format || new_format->planes != cur_format->planes) { + rcar_du_plane_release(rplane, cur_format); + rcar_du_plane_reserve(rplane, new_format); } - rplane->format = format; - rcar_du_plane_setup(rplane); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 7050fc1e11ec..0941c4830edd 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -32,12 +32,8 @@ struct rcar_du_group; struct rcar_du_plane { struct drm_plane plane; - struct rcar_du_group *group; - int hwindex; /* 0-based, -1 means unused */ - - const struct rcar_du_format_info *format; }; struct rcar_du_planes { @@ -53,6 +49,8 @@ struct rcar_du_planes { struct rcar_du_plane_state { struct drm_plane_state state; + const struct rcar_du_format_info *format; + unsigned int alpha; unsigned int colorkey; unsigned int zpos; From 52055bafa1ffcd24525f72f5bc35bc14eae1c449 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 01:39:13 +0200 Subject: [PATCH 30/32] drm: rcar-du: Move plane commit code from CRTC start to CRTC resume As the DRM core will commit plane states when performing atomic updates, those don't need to be committed manually when the CRTC is started except in the system resume code path. However, the atomic plane commit step is currently performed between mode set disable and mode set enable to mimick the legacy mode setting operations order. This causes the device clocks to be disabled after applying plane settings and reenabled when enabling the CRTC, potentially losing hardware in between. Reorder the operations to enable the CRTC first and only then apply plane settings, removing the need to manage clocks in the atomic begin and flush handlers. We can then move the plane state commit code out of the CRTC start handler to the system resume handler. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 54 ++++++++++---------------- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 98f770622b2d..29bbb44eecc9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -204,9 +204,8 @@ plane_format(struct rcar_du_plane *plane) return to_rcar_du_plane_state(plane->plane.state)->format; } -static void rcar_du_crtc_update_planes(struct drm_crtc *crtc) +static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) { - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; unsigned int num_planes = 0; unsigned int prio = 0; @@ -354,7 +353,6 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; bool interlaced; - unsigned int i; if (rcrtc->started) return; @@ -367,26 +365,8 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_crtc_set_display_timing(rcrtc); rcar_du_group_set_routing(rcrtc->group); - /* FIXME: Commit the planes state. This is required here as the CRTC can - * be started from the system resume handler, which don't go - * through .atomic_plane_update() and .atomic_flush() to commit plane - * state. Additionally, given that the plane state atomic commit occurs - * between CRTC disable and enable, the hardware state could also be - * lost due to runtime PM, requiring a full commit here. This will be - * fixed later after switching to atomic updates completely. - */ - mutex_lock(&rcrtc->group->planes.lock); - rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcrtc->group->planes.lock); - - for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; - - if (plane->plane.state->crtc != crtc) - continue; - - rcar_du_plane_setup(plane); - } + /* Start with all planes disabled. */ + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0); /* Select master sync mode. This enables display operation in master * sync mode (with the HSYNC and VSYNC signals configured as outputs and @@ -437,11 +417,27 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { + unsigned int i; + if (!rcrtc->enabled) return; rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); + + /* Commit the planes state. */ + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; + + if (plane->plane.state->crtc != &rcrtc->crtc) + continue; + + rcar_du_plane_setup(plane); + } + + mutex_lock(&rcrtc->group->planes.lock); + rcar_du_crtc_update_planes(rcrtc); + mutex_unlock(&rcrtc->group->planes.lock); } /* ----------------------------------------------------------------------------- @@ -490,11 +486,6 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) struct drm_device *dev = rcrtc->crtc.dev; unsigned long flags; - /* We need to access the hardware during atomic update, acquire a - * reference to the CRTC. - */ - rcar_du_crtc_get(rcrtc); - if (event) { event->pipe = rcrtc->index; @@ -510,14 +501,9 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - /* We're done, apply the configuration and drop the reference acquired - * in .atomic_begin(). - */ mutex_lock(&rcrtc->group->planes.lock); - rcar_du_crtc_update_planes(crtc); + rcar_du_crtc_update_planes(rcrtc); mutex_unlock(&rcrtc->group->planes.lock); - - rcar_du_crtc_put(rcrtc); } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index cec35e405248..8bc7242ba979 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -207,8 +207,8 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state); + drm_atomic_helper_commit_planes(dev, old_state); drm_atomic_helper_wait_for_vblanks(dev, old_state); From 48596d502e93a62fd1adab42b0a923709cd1c115 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Mon, 23 Feb 2015 16:55:56 +0200 Subject: [PATCH 31/32] drm: rcar-du: Move group locking inside rcar_du_crtc_update_planes() Only the planes to CRTCs association control register DPTSR needs to be protected by custom locking, don't hold the mutex around the whole code. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 29bbb44eecc9..8459aaee8add 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -252,8 +252,6 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) * with superposition controller 2. */ if (rcrtc->index % 2) { - u32 value = rcar_du_group_read(rcrtc->group, DPTSR); - /* The DPTSR register is updated when the display controller is * stopped. We thus need to restart the DU. Once again, sorry * for the flicker. One way to mitigate the issue would be to @@ -261,11 +259,13 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) * split, or through a module parameter). Flicker would then * occur only if we need to break the pre-association. */ - if (value != dptsr) { + mutex_lock(&rcrtc->group->planes.lock); + if (rcar_du_group_read(rcrtc->group, DPTSR) != dptsr) { rcar_du_group_write(rcrtc->group, DPTSR, dptsr); if (rcrtc->group->used_crtcs) rcar_du_group_restart(rcrtc->group); } + mutex_unlock(&rcrtc->group->planes.lock); } rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, @@ -435,9 +435,7 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) rcar_du_plane_setup(plane); } - mutex_lock(&rcrtc->group->planes.lock); rcar_du_crtc_update_planes(rcrtc); - mutex_unlock(&rcrtc->group->planes.lock); } /* ----------------------------------------------------------------------------- @@ -501,9 +499,7 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - mutex_lock(&rcrtc->group->planes.lock); rcar_du_crtc_update_planes(rcrtc); - mutex_unlock(&rcrtc->group->planes.lock); } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { From 5ee5a81df57ea3a19a5e306fdf8244ab696c8916 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Date: Wed, 25 Feb 2015 18:27:19 +0200 Subject: [PATCH 32/32] drm: rcar-du: Fix race condition in hardware plane allocator The plane allocator has been inherently racy since the beginning of the transition to atomic updates, as the allocator lock is released between free plane check (at .atomic_check() time) and the reservation (at .atomic_update() time). To fix it, create a new allocator solely based on the atomic plane states without keeping any external state and perform allocation in the .atomic_check() handler. The core idea is to replace the free planes bitmask with a collective knowledge based on the allocated hardware plane(s) for each KMS plane. The allocator then loops over all plane states to compute the free planes bitmask, allocates hardware planes based on that bitmask, and stores the result back in the plane states. For this to work we need to access the current state of planes not touched by the atomic update. To ensure that it won't be modified, we need to lock all planes using drm_atomic_get_plane_state(). This effectively serializes atomic updates from .atomic_check() up to completion, either when swapping the states if the check step has succeeded, or when freeing the states if the check step has failed. Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +- drivers/gpu/drm/rcar-du/rcar_du_group.h | 5 + drivers/gpu/drm/rcar-du/rcar_du_kms.c | 203 +++++++++++++++++++++++- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 166 ++----------------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 11 +- 5 files changed, 228 insertions(+), 164 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 8459aaee8add..9e72133bb64b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -233,7 +233,8 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) for (i = 0; i < num_planes; ++i) { struct rcar_du_plane *plane = planes[i]; - unsigned int index = plane->hwindex; + struct drm_plane_state *state = plane->plane.state; + unsigned int index = to_rcar_du_plane_state(state)->hwindex; prio -= 4; dspr |= (index + 1) << prio; @@ -259,13 +260,13 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) * split, or through a module parameter). Flicker would then * occur only if we need to break the pre-association. */ - mutex_lock(&rcrtc->group->planes.lock); + mutex_lock(&rcrtc->group->lock); if (rcar_du_group_read(rcrtc->group, DPTSR) != dptsr) { rcar_du_group_write(rcrtc->group, DPTSR, dptsr); if (rcrtc->group->used_crtcs) rcar_du_group_restart(rcrtc->group); } - mutex_unlock(&rcrtc->group->planes.lock); + mutex_unlock(&rcrtc->group->lock); } rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index 0c38cdcda4ca..ed36433fbe84 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -14,6 +14,8 @@ #ifndef __RCAR_DU_GROUP_H__ #define __RCAR_DU_GROUP_H__ +#include <linux/mutex.h> + #include "rcar_du_plane.h" struct rcar_du_device; @@ -25,6 +27,7 @@ struct rcar_du_device; * @index: group index * @use_count: number of users of the group (rcar_du_group_(get|put)) * @used_crtcs: number of CRTCs currently in use + * @lock: protects the DPTSR register * @planes: planes handled by the group */ struct rcar_du_group { @@ -35,6 +38,8 @@ struct rcar_du_group { unsigned int use_count; unsigned int used_crtcs; + struct mutex lock; + struct rcar_du_planes planes; }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 8bc7242ba979..fb052bca574f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -189,9 +189,206 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) } /* ----------------------------------------------------------------------------- - * Atomic Updates + * Atomic Check and Update */ +/* + * Atomic hardware plane allocator + * + * The hardware plane allocator is solely based on the atomic plane states + * without keeping any external state to avoid races between .atomic_check() + * and .atomic_commit(). + * + * The core idea is to avoid using a free planes bitmask that would need to be + * shared between check and commit handlers with a collective knowledge based on + * the allocated hardware plane(s) for each KMS plane. The allocator then loops + * over all plane states to compute the free planes bitmask, allocates hardware + * planes based on that bitmask, and stores the result back in the plane states. + * + * For this to work we need to access the current state of planes not touched by + * the atomic update. To ensure that it won't be modified, we need to lock all + * planes using drm_atomic_get_plane_state(). This effectively serializes atomic + * updates from .atomic_check() up to completion (when swapping the states if + * the check step has succeeded) or rollback (when freeing the states if the + * check step has failed). + * + * Allocation is performed in the .atomic_check() handler and applied + * automatically when the core swaps the old and new states. + */ + +static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane, + struct rcar_du_plane_state *state) +{ + const struct rcar_du_format_info *cur_format; + + cur_format = to_rcar_du_plane_state(plane->plane.state)->format; + + /* Lowering the number of planes doesn't strictly require reallocation + * as the extra hardware plane will be freed when committing, but doing + * so could lead to more fragmentation. + */ + return !cur_format || cur_format->planes != state->format->planes; +} + +static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) +{ + unsigned int mask; + + if (state->hwindex == -1) + return 0; + + mask = 1 << state->hwindex; + if (state->format->planes == 2) + mask |= 1 << ((state->hwindex + 1) % 8); + + return mask; +} + +static int rcar_du_plane_hwalloc(unsigned int num_planes, unsigned int free) +{ + unsigned int i; + + for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) { + if (!(free & (1 << i))) + continue; + + if (num_planes == 1 || free & (1 << ((i + 1) % 8))) + break; + } + + return i == RCAR_DU_NUM_HW_PLANES ? -EBUSY : i; +} + +static int rcar_du_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct rcar_du_device *rcdu = dev->dev_private; + unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; + unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; + bool needs_realloc = false; + unsigned int groups = 0; + unsigned int i; + int ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret < 0) + return ret; + + /* Check if hardware planes need to be reallocated. */ + for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + struct rcar_du_plane_state *plane_state; + struct rcar_du_plane *plane; + unsigned int index; + + if (!state->planes[i]) + continue; + + plane = to_rcar_plane(state->planes[i]); + plane_state = to_rcar_du_plane_state(state->plane_states[i]); + + /* If the plane is being disabled we don't need to go through + * the full reallocation procedure. Just mark the hardware + * plane(s) as freed. + */ + if (!plane_state->format) { + index = plane - plane->group->planes.planes; + group_freed_planes[plane->group->index] |= 1 << index; + plane_state->hwindex = -1; + continue; + } + + /* If the plane needs to be reallocated mark it as such, and + * mark the hardware plane(s) as free. + */ + if (rcar_du_plane_needs_realloc(plane, plane_state)) { + groups |= 1 << plane->group->index; + needs_realloc = true; + + index = plane - plane->group->planes.planes; + group_freed_planes[plane->group->index] |= 1 << index; + plane_state->hwindex = -1; + } + } + + if (!needs_realloc) + return 0; + + /* Grab all plane states for the groups that need reallocation to ensure + * locking and avoid racy updates. This serializes the update operation, + * but there's not much we can do about it as that's the hardware + * design. + * + * Compute the used planes mask for each group at the same time to avoid + * looping over the planes separately later. + */ + while (groups) { + unsigned int index = ffs(groups) - 1; + struct rcar_du_group *group = &rcdu->groups[index]; + unsigned int used_planes = 0; + + for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { + struct rcar_du_plane *plane = &group->planes.planes[i]; + struct rcar_du_plane_state *plane_state; + struct drm_plane_state *s; + + s = drm_atomic_get_plane_state(state, &plane->plane); + if (IS_ERR(s)) + return PTR_ERR(s); + + /* If the plane has been freed in the above loop its + * hardware planes must not be added to the used planes + * bitmask. However, the current state doesn't reflect + * the free state yet, as we've modified the new state + * above. Use the local freed planes list to check for + * that condition instead. + */ + if (group_freed_planes[index] & (1 << i)) + continue; + + plane_state = to_rcar_du_plane_state(plane->plane.state); + used_planes |= rcar_du_plane_hwmask(plane_state); + } + + group_free_planes[index] = 0xff & ~used_planes; + groups &= ~(1 << index); + } + + /* Reallocate hardware planes for each plane that needs it. */ + for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + struct rcar_du_plane_state *plane_state; + struct rcar_du_plane *plane; + int idx; + + if (!state->planes[i]) + continue; + + plane = to_rcar_plane(state->planes[i]); + plane_state = to_rcar_du_plane_state(state->plane_states[i]); + + /* Skip planes that are being disabled or don't need to be + * reallocated. + */ + if (!plane_state->format || + !rcar_du_plane_needs_realloc(plane, plane_state)) + continue; + + idx = rcar_du_plane_hwalloc(plane_state->format->planes, + group_free_planes[plane->group->index]); + if (idx < 0) { + dev_dbg(rcdu->dev, "%s: no available hardware plane\n", + __func__); + return idx; + } + + plane_state->hwindex = idx; + + group_free_planes[plane->group->index] &= + ~rcar_du_plane_hwmask(plane_state); + } + + return 0; +} + struct rcar_du_commit { struct work_struct work; struct drm_device *dev; @@ -292,7 +489,7 @@ static int rcar_du_atomic_commit(struct drm_device *dev, static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, .output_poll_changed = rcar_du_output_poll_changed, - .atomic_check = drm_atomic_helper_check, + .atomic_check = rcar_du_atomic_check, .atomic_commit = rcar_du_atomic_commit, }; @@ -498,6 +695,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) for (i = 0; i < num_groups; ++i) { struct rcar_du_group *rgrp = &rcdu->groups[i]; + mutex_init(&rgrp->lock); + rgrp->dev = rcdu; rgrp->mmio_offset = mmio_offsets[i]; rgrp->index = i; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 58b9e02e6910..35a2f04ab799 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -28,11 +28,6 @@ #define RCAR_DU_COLORKEY_SOURCE (1 << 24) #define RCAR_DU_COLORKEY_MASK (1 << 24) -static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) -{ - return container_of(plane, struct rcar_du_plane, plane); -} - static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, unsigned int index, u32 reg) { @@ -47,90 +42,6 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, data); } -static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, - const struct rcar_du_format_info *cur_format, - const struct rcar_du_format_info *new_format) -{ - struct rcar_du_group *rgrp = plane->group; - unsigned int free; - unsigned int i; - int ret; - - mutex_lock(&rgrp->planes.lock); - - free = rgrp->planes.free; - - if (plane->hwindex != -1) { - free |= 1 << plane->hwindex; - if (cur_format->planes == 2) - free |= 1 << ((plane->hwindex + 1) % 8); - } - - for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { - if (!(free & (1 << i))) - continue; - - if (new_format->planes == 1 || free & (1 << ((i + 1) % 8))) - break; - } - - ret = i == ARRAY_SIZE(rgrp->planes.planes) ? -EBUSY : 0; - - mutex_unlock(&rgrp->planes.lock); - return ret; -} - -static int rcar_du_plane_reserve(struct rcar_du_plane *plane, - const struct rcar_du_format_info *format) -{ - struct rcar_du_group *rgrp = plane->group; - unsigned int i; - int ret = -EBUSY; - - mutex_lock(&rgrp->planes.lock); - - for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) { - if (!(rgrp->planes.free & (1 << i))) - continue; - - if (format->planes == 1 || - rgrp->planes.free & (1 << ((i + 1) % 8))) - break; - } - - if (i == RCAR_DU_NUM_HW_PLANES) - goto done; - - rgrp->planes.free &= ~(1 << i); - if (format->planes == 2) - rgrp->planes.free &= ~(1 << ((i + 1) % 8)); - - plane->hwindex = i; - - ret = 0; - -done: - mutex_unlock(&rgrp->planes.lock); - return ret; -} - -static void rcar_du_plane_release(struct rcar_du_plane *plane, - const struct rcar_du_format_info *format) -{ - struct rcar_du_group *rgrp = plane->group; - - if (plane->hwindex == -1) - return; - - mutex_lock(&rgrp->planes.lock); - rgrp->planes.free |= 1 << plane->hwindex; - if (format->planes == 2) - rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8); - mutex_unlock(&rgrp->planes.lock); - - plane->hwindex = -1; -} - static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) { struct rcar_du_plane_state *state = @@ -139,7 +50,7 @@ static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) struct rcar_du_group *rgrp = plane->group; unsigned int src_x = state->state.src_x >> 16; unsigned int src_y = state->state.src_y >> 16; - unsigned int index = plane->hwindex; + unsigned int index = state->hwindex; struct drm_gem_cma_object *gem; bool interlaced; u32 mwr; @@ -278,7 +189,7 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, rcar_du_plane_setup_mode(plane, index); if (state->format->planes == 2) { - if (plane->hwindex != index) { + if (state->hwindex != index) { if (state->format->fourcc == DRM_FORMAT_NV12 || state->format->fourcc == DRM_FORMAT_NV21) ddcr2 |= PnDDCR2_Y420; @@ -313,9 +224,9 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane) struct rcar_du_plane_state *state = to_rcar_du_plane_state(plane->plane.state); - __rcar_du_plane_setup(plane, plane->hwindex); + __rcar_du_plane_setup(plane, state->hwindex); if (state->format->planes == 2) - __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8); + __rcar_du_plane_setup(plane, (state->hwindex + 1) % 8); rcar_du_plane_setup_fb(plane); } @@ -326,12 +237,11 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_device *rcdu = rplane->group->dev; - const struct rcar_du_format_info *cur_format; - const struct rcar_du_format_info *new_format; - int ret; - if (!state->fb || !state->crtc) + if (!state->fb || !state->crtc) { + rstate->format = NULL; return 0; + } if (state->src_w >> 16 != state->crtc_w || state->src_h >> 16 != state->crtc_h) { @@ -339,72 +249,23 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - cur_format = to_rcar_du_plane_state(plane->state)->format; - new_format = rcar_du_format_info(state->fb->pixel_format); - if (new_format == NULL) { + rstate->format = rcar_du_format_info(state->fb->pixel_format); + if (rstate->format == NULL) { dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, state->fb->pixel_format); return -EINVAL; } - /* If the number of required planes has changed we will need to - * reallocate hardware planes. Ensure free planes are available. - */ - if (!cur_format || new_format->planes != cur_format->planes) { - ret = rcar_du_plane_reserve_check(rplane, cur_format, - new_format); - if (ret < 0) { - dev_dbg(rcdu->dev, "%s: no available hardware plane\n", - __func__); - return ret; - } - } - - rstate->format = new_format; - return 0; } -static void rcar_du_plane_disable(struct rcar_du_plane *rplane, - const struct rcar_du_format_info *cur_format) -{ - struct rcar_du_plane_state *rstate = - to_rcar_du_plane_state(rplane->plane.state); - - if (!rplane->plane.state->crtc) - return; - - rcar_du_plane_release(rplane, cur_format); - - rstate->format = NULL; -} - static void rcar_du_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct rcar_du_plane *rplane = to_rcar_plane(plane); - struct drm_plane_state *state = plane->state; - struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); - const struct rcar_du_format_info *cur_format; - const struct rcar_du_format_info *new_format; - cur_format = to_rcar_du_plane_state(old_state)->format; - new_format = rstate->format; - - if (!state->crtc) { - rcar_du_plane_disable(rplane, cur_format); - return; - } - - /* Reallocate hardware planes if the number of required planes has - * changed. - */ - if (!cur_format || new_format->planes != cur_format->planes) { - rcar_du_plane_release(rplane, cur_format); - rcar_du_plane_reserve(rplane, new_format); - } - - rcar_du_plane_setup(rplane); + if (plane->state->crtc) + rcar_du_plane_setup(rplane); } static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { @@ -426,6 +287,7 @@ static void rcar_du_plane_reset(struct drm_plane *plane) if (state == NULL) return; + state->hwindex = -1; state->alpha = 255; state->colorkey = RCAR_DU_COLORKEY_NONE; state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; @@ -534,9 +396,6 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) unsigned int i; int ret; - mutex_init(&planes->lock); - planes->free = 0xff; - planes->alpha = drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); if (planes->alpha == NULL) @@ -572,7 +431,6 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) struct rcar_du_plane *plane = &planes->planes[i]; plane->group = rgrp; - plane->hwindex = -1; ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, &rcar_du_plane_funcs, formats, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 0941c4830edd..abff0ebeb195 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -14,8 +14,6 @@ #ifndef __RCAR_DU_PLANE_H__ #define __RCAR_DU_PLANE_H__ -#include <linux/mutex.h> - #include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -33,13 +31,15 @@ struct rcar_du_group; struct rcar_du_plane { struct drm_plane plane; struct rcar_du_group *group; - int hwindex; /* 0-based, -1 means unused */ }; +static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) +{ + return container_of(plane, struct rcar_du_plane, plane); +} + struct rcar_du_planes { struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES]; - unsigned int free; - struct mutex lock; struct drm_property *alpha; struct drm_property *colorkey; @@ -50,6 +50,7 @@ struct rcar_du_plane_state { struct drm_plane_state state; const struct rcar_du_format_info *format; + int hwindex; /* 0-based, -1 means unused */ unsigned int alpha; unsigned int colorkey;