Let VOP vblank status decide whether panle should enter into or
exit from PSR status. Before eDP start to change PSR status, it
need to wait for VOP vact_end event. In order to listen vact_end
event, I create a new file about PSR notify between eDP and VOP.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- Remove vblank notify out (Daniel)
- Create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/Makefile               |  2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 64 ++++++++++++++++++++++++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h     |  7 +++
 drivers/gpu/drm/rockchip/rockchip_drm_notify.c  | 54 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_notify.h  | 23 +++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c     | 41 ++++++++++++++++
 6 files changed, 189 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.h

diff --git a/drivers/gpu/drm/rockchip/Makefile 
b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..49fee8c 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.

 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-               rockchip_drm_gem.o rockchip_drm_vop.o
+               rockchip_drm_gem.o rockchip_drm_vop.o rockchip_drm_notify.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o

 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c 
b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 4b64964..25fb687 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -33,6 +33,7 @@

 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
+#include "rockchip_drm_notify.h"

 #define to_dp(nm)      container_of(nm, struct rockchip_dp_device, nm)

@@ -54,6 +55,10 @@ struct rockchip_dp_device {
        struct regmap            *grf;
        struct reset_control     *rst;

+       struct workqueue_struct  *dp_workq;
+       struct work_struct       psr_work;
+       unsigned int             psr_state;
+
        const struct rockchip_dp_chip_data *data;

        struct analogix_dp_plat_data plat_data;
@@ -97,6 +102,42 @@ static int rockchip_dp_powerdown(struct 
analogix_dp_plat_data *plat_data)
        return 0;
 }

+static int rockchip_dp_psr_active(enum psr_action action, void *priv)
+{
+       struct rockchip_dp_device *dp = (struct rockchip_dp_device *)priv;
+
+       switch (action) {
+       case PSR_INACTIVE:
+               dp->psr_state = 0;
+               break;
+       case PSR_ACTIVE:
+               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+               break;
+       default:
+               return 0;
+       }
+
+       queue_work(dp->dp_workq, &dp->psr_work);
+       return 0;
+}
+
+static void dp_psr_work(struct work_struct *psr_work)
+{
+       struct rockchip_dp_device *dp = to_dp(psr_work);
+       int psr_state = dp->psr_state;
+       int ret;
+
+       if (psr_state == EDP_VSC_PSR_STATE_ACTIVE) {
+               ret = rockchip_psr_ready_wait();
+               if (ret == 0)
+                       analogix_dp_actice_psr(dp->dev);
+       } else {
+               ret = rockchip_psr_ready_wait();
+               if (ret == 0)
+                       analogix_dp_inactice_psr(dp->dev);
+       }
+}
+
 static enum drm_mode_status
 rockchip_dp_mode_valid(struct analogix_dp_plat_data *plat_data,
                       struct drm_connector *connector,
@@ -128,9 +169,18 @@ static void rockchip_dp_drm_encoder_mode_set(struct 
drm_encoder *encoder,
                                             struct drm_display_mode *mode,
                                             struct drm_display_mode *adjusted)
 {
-       /* do nothing */
+       struct rockchip_dp_device *dp = to_dp(encoder);
+       struct drm_crtc *crtc = encoder->crtc;
+       struct rockchip_crtc_state *s;
+
+       if (crtc) {
+               s = to_rockchip_crtc_state(crtc->state);
+               s->psr_active = rockchip_dp_psr_active;
+               s->psr_priv = dp;
+       }
 }

+
 static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
 {
        struct rockchip_dp_device *dp = to_dp(encoder);
@@ -198,6 +248,9 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder 
*encoder,
                break;
        }

+       s->psr_active = rockchip_dp_psr_active;
+       s->psr_priv = dp;
+
        return 0;
 }

@@ -320,6 +373,15 @@ static int rockchip_dp_bind(struct device *dev, struct 
device *master,
        dp->plat_data.power_off = rockchip_dp_powerdown;
        dp->plat_data.mode_valid = rockchip_dp_mode_valid;

+       dp->dp_workq = create_singlethread_workqueue("dp");
+       if (!dp->dp_workq) {
+               dev_err(dp->dev, "failed to create workqueue\n");
+               return PTR_ERR(dp->dp_workq);
+       }
+
+       dp->psr_state = 0;
+       INIT_WORK(&dp->psr_work, dp_psr_work);
+
        return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h 
b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 56f43a3..f1ccc10 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -31,6 +31,11 @@
 struct drm_device;
 struct drm_connector;

+enum psr_action {
+       PSR_ACTIVE = 0,
+       PSR_INACTIVE,
+};
+
 /*
  * Rockchip drm private crtc funcs.
  * @enable_vblank: enable crtc vblank irq.
@@ -54,6 +59,8 @@ struct rockchip_crtc_state {
        struct drm_crtc_state base;
        int output_type;
        int output_mode;
+       int (*psr_active)(enum psr_action action, void *priv);
+       void *psr_priv;
 };
 #define to_rockchip_crtc_state(s) \
                container_of(s, struct rockchip_crtc_state, base)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_notify.c 
b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c
new file mode 100644
index 0000000..908e991
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_drm_notify.h"
+
+static RAW_NOTIFIER_HEAD(psr_ready_chain);
+static DEFINE_MUTEX(psr_ready_lock);
+
+int rockchip_drm_psr_ready_register_notifier(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       if (!nb)
+               return -EINVAL;
+
+       ret = raw_notifier_chain_register(&psr_ready_chain, nb);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_psr_ready_register_notifier);
+
+int rockchip_drm_psr_ready_unregister_notifier(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       if (!nb)
+               return -EINVAL;
+
+       ret = raw_notifier_chain_unregister(&psr_ready_chain, nb);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_psr_ready_unregister_notifier);
+
+int rockchip_psr_ready_wait(void)
+{
+       int ret;
+
+       ret = raw_notifier_call_chain(&psr_ready_chain, 0, 0);
+       if (ret == NOTIFY_BAD)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_psr_ready_wait);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_notify.h 
b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h
new file mode 100644
index 0000000..1b190e5
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ROCKCHIP_DRM_NOTIFY_H
+#define __ROCKCHIP_DRM_NOTIFY_H
+
+#include <linux/notifier.h>
+
+int rockchip_psr_ready_wait(void);
+int rockchip_drm_psr_ready_register_notifier(struct notifier_block *nb);
+int rockchip_drm_psr_ready_unregister_notifier(struct notifier_block *nb);
+
+#endif /* __ROCKCHIP_DRM_NOTIFY_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c 
b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index b2a36db..b5a015b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -35,6 +35,7 @@
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_vop.h"
+#include "rockchip_drm_notify.h"

 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
                vop_mask_write(x, off, mask, shift, v, write_mask, true)
@@ -117,6 +118,10 @@ struct vop {
        struct completion wait_update_complete;
        struct drm_pending_vblank_event *event;

+       /* eDP PSR callback */
+       int (*psr_active)(enum psr_action action, void *priv);
+       void *psr_priv;
+       struct notifier_block psr_prepare_nb;
        struct completion line_flag_completion;

        const struct vop_data *data;
@@ -906,6 +911,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)

        spin_unlock_irqrestore(&vop->irq_lock, flags);

+       if (vop->psr_active)
+               vop->psr_active(PSR_INACTIVE, vop->psr_priv);
+
        return 0;
 }

@@ -922,6 +930,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
        VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);

        spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+       if (vop->psr_active)
+               vop->psr_active(PSR_ACTIVE, vop->psr_priv);
 }

 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1066,6 +1077,9 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
        clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);

        VOP_CTRL_SET(vop, standby, 0);
+
+       vop->psr_active = s->psr_active;
+       vop->psr_priv = s->psr_priv;
 }

 static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
@@ -1178,6 +1192,30 @@ static void vop_handle_vblank(struct vop *vop)
                complete(&vop->wait_update_complete);
 }

+static int psr_prepare_notify(struct notifier_block *psr_prepare_nb,
+                             unsigned long action, void *data)
+{
+       struct vop *vop = container_of(psr_prepare_nb, struct vop,
+                                      psr_prepare_nb);
+       struct drm_display_mode *mode = &vop->crtc.mode;
+       int vact_end = mode->vtotal - mode->vsync_start + mode->vdisplay;
+       unsigned long jiffies_left;
+
+       reinit_completion(&vop->line_flag_completion);
+       vop_line_flag_irq_enable(vop, vact_end);
+
+       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+                                                  msecs_to_jiffies(200));
+       vop_line_flag_irq_disable(vop);
+
+       if (jiffies_left == 0) {
+               dev_err(vop->dev, "Timeout waiting for IRQ\n");
+               return NOTIFY_BAD;
+       }
+
+       return NOTIFY_STOP;
+}
+
 static irqreturn_t vop_isr(int irq, void *data)
 {
        struct vop *vop = data;
@@ -1312,6 +1350,9 @@ static int vop_create_crtc(struct vop *vop)
                goto err_cleanup_crtc;
        }

+       vop->psr_prepare_nb.notifier_call = psr_prepare_notify;
+       rockchip_drm_psr_ready_register_notifier(&vop->psr_prepare_nb);
+
        init_completion(&vop->dsp_hold_completion);
        init_completion(&vop->wait_update_complete);
        init_completion(&vop->line_flag_completion);
-- 
1.9.1


Reply via email to