[RFC PATCH v2 3/3] drm/rockchip: analogix_dp: add PSR support

Yakir Yang ykk at rock-chips.com
Thu Jun 2 05:57:38 PDT 2016


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





More information about the Linux-rockchip mailing list