The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
[0]: https://patchwork.kernel.org/patch/8886041/ [1]: https://patchwork.kernel.org/patch/9132713/
- Yakir Thanks,
Yakir Yang (2): drm/rockchip: add a notify event about vblank enable/disable drm/rockchip: analogix: add eDP PSR function
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 69 ++++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 5 + drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 54 +++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 28 ++++++ drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 65 +++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 105 +++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 33 +++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 96 +++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 + include/drm/bridge/analogix_dp.h | 3 + 12 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.h
EDP PSR function is interesting in vblank enable or disable event, so it would be great introduce a way to notify encoder about this event.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 66 ++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 29 +++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) 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/rockchip_drm_notify.c b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c new file mode 100644 index 0000000..84111d9 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c @@ -0,0 +1,66 @@ +#include "rockchip_drm_notify.h" + +static RAW_NOTIFIER_HEAD(vblank_chain); +static DEFINE_MUTEX(vblank_lock); + +/** + * rockchip_drm_vblank_enable - Send Vblank enable event. Will only enable send + * Vblank if there are 1 or fewer notifiers. + */ +void rockchip_drm_vblank_enable(void) +{ + mutex_lock(&vblank_lock); + raw_notifier_call_chain(&vblank_chain, VBLANK_ENABLE, NULL); + mutex_unlock(&vblank_lock); +} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_enable); + +/** + * rockchip_drm_vblank_disable - Send VBlank disable event. + */ +void rockchip_drm_vblank_disable(void) +{ + mutex_lock(&vblank_lock); + raw_notifier_call_chain(&vblank_chain, VBLANK_DISABLE, NULL); + mutex_unlock(&vblank_lock); +} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_disable); + +/** + * rockchip_drm_vblank_register_notifier - Add notifier to Vblank notifiers. + * + * Enable notifiers are called when we enable/disable vblank. This can be done + * through rockchip_drm_vblank_enable/disable or when there is more than one + * sync notifier. Must call rockchip_drm_vblank_lock before calling this. + * @nb The notifier to add + */ +int rockchip_drm_vblank_register_notifier(struct notifier_block *nb) +{ + int ret = 0; + + if (!nb) + return -EINVAL; + + ret = raw_notifier_chain_register(&vblank_chain, nb); + return ret; +} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_register_notifier); + +/** + * rockchip_drm_vblank_unregister_notifier - Remove notifier from Vblank + * notifiers. + * + * Must call rockchip_drm_vblank_lock before calling this. + * @nb The notifier to remove. + */ +int rockchip_drm_vblank_unregister_notifier(struct notifier_block *nb) +{ + int ret = 0; + + if (!nb) + return -EINVAL; + + ret = raw_notifier_chain_unregister(&vblank_chain, nb); + return ret; +} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_unregister_notifier); 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..2f28afa --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h @@ -0,0 +1,29 @@ +/* + * 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> + +enum vblank_enable_op { + VBLANK_ENABLE = 0, + VBLANK_DISABLE, +}; + +void rockchip_drm_vblank_enable(void); +void rockchip_drm_vblank_disable(void); +int rockchip_drm_vblank_register_notifier(struct notifier_block *nb); +int rockchip_drm_vblank_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 257501f..fb6ce4b 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) @@ -851,6 +852,8 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
+ rockchip_drm_vblank_enable(); + return 0; }
@@ -867,6 +870,8 @@ 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); + + rockchip_drm_vblank_disable(); }
static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
On Tue, May 31, 2016 at 09:39:19PM +0800, Yakir Yang wrote:
EDP PSR function is interesting in vblank enable or disable event, so it would be great introduce a way to notify encoder about this event.
Signed-off-by: Yakir Yang ykk@rock-chips.com
notifiers considered evil, especially if you add a global notifier for something that's very specific to a given crtc.
I think the better fix would be add a suitable hook to drm_bridge to allow encoders to go into self refresh, and to wake them up from self-refresh again. -Daniel
drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 66 ++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 29 +++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) 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/rockchip_drm_notify.c b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c new file mode 100644 index 0000000..84111d9 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c @@ -0,0 +1,66 @@ +#include "rockchip_drm_notify.h"
+static RAW_NOTIFIER_HEAD(vblank_chain); +static DEFINE_MUTEX(vblank_lock);
+/**
- rockchip_drm_vblank_enable - Send Vblank enable event. Will only enable send
- Vblank if there are 1 or fewer notifiers.
- */
+void rockchip_drm_vblank_enable(void) +{
- mutex_lock(&vblank_lock);
- raw_notifier_call_chain(&vblank_chain, VBLANK_ENABLE, NULL);
- mutex_unlock(&vblank_lock);
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_enable);
+/**
- rockchip_drm_vblank_disable - Send VBlank disable event.
- */
+void rockchip_drm_vblank_disable(void) +{
- mutex_lock(&vblank_lock);
- raw_notifier_call_chain(&vblank_chain, VBLANK_DISABLE, NULL);
- mutex_unlock(&vblank_lock);
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_disable);
+/**
- rockchip_drm_vblank_register_notifier - Add notifier to Vblank notifiers.
- Enable notifiers are called when we enable/disable vblank. This can be done
- through rockchip_drm_vblank_enable/disable or when there is more than one
- sync notifier. Must call rockchip_drm_vblank_lock before calling this.
- @nb The notifier to add
- */
+int rockchip_drm_vblank_register_notifier(struct notifier_block *nb) +{
- int ret = 0;
- if (!nb)
return -EINVAL;
- ret = raw_notifier_chain_register(&vblank_chain, nb);
- return ret;
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_register_notifier);
+/**
- rockchip_drm_vblank_unregister_notifier - Remove notifier from Vblank
- notifiers.
- Must call rockchip_drm_vblank_lock before calling this.
- @nb The notifier to remove.
- */
+int rockchip_drm_vblank_unregister_notifier(struct notifier_block *nb) +{
- int ret = 0;
- if (!nb)
return -EINVAL;
- ret = raw_notifier_chain_unregister(&vblank_chain, nb);
- return ret;
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_unregister_notifier); 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..2f28afa --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h @@ -0,0 +1,29 @@ +/*
- 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>
+enum vblank_enable_op {
- VBLANK_ENABLE = 0,
- VBLANK_DISABLE,
+};
+void rockchip_drm_vblank_enable(void); +void rockchip_drm_vblank_disable(void); +int rockchip_drm_vblank_register_notifier(struct notifier_block *nb); +int rockchip_drm_vblank_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 257501f..fb6ce4b 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) @@ -851,6 +852,8 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
- rockchip_drm_vblank_enable();
- return 0;
}
@@ -867,6 +870,8 @@ 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);
- rockchip_drm_vblank_disable();
}
static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
1.9.1
Hi Daniel,
On 05/31/2016 10:36 PM, Daniel Vetter wrote:
On Tue, May 31, 2016 at 09:39:19PM +0800, Yakir Yang wrote:
EDP PSR function is interesting in vblank enable or disable event, so it would be great introduce a way to notify encoder about this event.
Signed-off-by: Yakir Yang ykk@rock-chips.com
notifiers considered evil, especially if you add a global notifier for something that's very specific to a given crtc.
Oops, I haven't realized that. Yes, you're right, this is very specific to a given crtc, that's bad :(
I think the better fix would be add a suitable hook to drm_bridge to allow encoders to go into self refresh, and to wake them up from self-refresh again.
Sounds good, need to found a better way.
Thanks, - Yakir
-Daniel
drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 66 ++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 29 +++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) 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/rockchip_drm_notify.c b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c new file mode 100644 index 0000000..84111d9 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c @@ -0,0 +1,66 @@ +#include "rockchip_drm_notify.h"
+static RAW_NOTIFIER_HEAD(vblank_chain); +static DEFINE_MUTEX(vblank_lock);
+/**
- rockchip_drm_vblank_enable - Send Vblank enable event. Will only enable send
- Vblank if there are 1 or fewer notifiers.
- */
+void rockchip_drm_vblank_enable(void) +{
- mutex_lock(&vblank_lock);
- raw_notifier_call_chain(&vblank_chain, VBLANK_ENABLE, NULL);
- mutex_unlock(&vblank_lock);
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_enable);
+/**
- rockchip_drm_vblank_disable - Send VBlank disable event.
- */
+void rockchip_drm_vblank_disable(void) +{
- mutex_lock(&vblank_lock);
- raw_notifier_call_chain(&vblank_chain, VBLANK_DISABLE, NULL);
- mutex_unlock(&vblank_lock);
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_disable);
+/**
- rockchip_drm_vblank_register_notifier - Add notifier to Vblank notifiers.
- Enable notifiers are called when we enable/disable vblank. This can be done
- through rockchip_drm_vblank_enable/disable or when there is more than one
- sync notifier. Must call rockchip_drm_vblank_lock before calling this.
- @nb The notifier to add
- */
+int rockchip_drm_vblank_register_notifier(struct notifier_block *nb) +{
- int ret = 0;
- if (!nb)
return -EINVAL;
- ret = raw_notifier_chain_register(&vblank_chain, nb);
- return ret;
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_register_notifier);
+/**
- rockchip_drm_vblank_unregister_notifier - Remove notifier from Vblank
- notifiers.
- Must call rockchip_drm_vblank_lock before calling this.
- @nb The notifier to remove.
- */
+int rockchip_drm_vblank_unregister_notifier(struct notifier_block *nb) +{
- int ret = 0;
- if (!nb)
return -EINVAL;
- ret = raw_notifier_chain_unregister(&vblank_chain, nb);
- return ret;
+} +EXPORT_SYMBOL_GPL(rockchip_drm_vblank_unregister_notifier); 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..2f28afa --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h @@ -0,0 +1,29 @@ +/*
- 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>
+enum vblank_enable_op {
- VBLANK_ENABLE = 0,
- VBLANK_DISABLE,
+};
+void rockchip_drm_vblank_enable(void); +void rockchip_drm_vblank_disable(void); +int rockchip_drm_vblank_register_notifier(struct notifier_block *nb); +int rockchip_drm_vblank_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 257501f..fb6ce4b 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) @@ -851,6 +852,8 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
- rockchip_drm_vblank_enable();
- return 0; }
@@ -867,6 +870,8 @@ 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);
rockchip_drm_vblank_disable(); }
static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
-- 1.9.1
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 69 ++++++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 5 ++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 54 +++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 28 +++++++ drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 65 ++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 39 ++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 4 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 91 ++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 + include/drm/bridge/analogix_dp.h | 3 + 11 files changed, 363 insertions(+)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 5af9ce4..a66ccb6 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -39,6 +39,72 @@ struct bridge_init { struct device_node *node; };
+int analogix_dp_actice_psr(struct device *dev) +{ + struct analogix_dp_device *dp = dev_get_drvdata(dev); + + if (!dp->psr_support) + return -EINVAL; + + analogix_dp_send_vsc(dp, EDP_VSC_PSR_STATE_ACTIVE | + EDP_VSC_PSR_CRC_VALUES_VALID); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_actice_psr); + +int analogix_dp_inactice_psr(struct device *dev) +{ + struct analogix_dp_device *dp = dev_get_drvdata(dev); + + if (!dp->psr_support) + return -EINVAL; + + analogix_dp_send_vsc(dp, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_inactice_psr); + +int analogix_dp_enable_psr(struct analogix_dp_device *dp) +{ + unsigned char psr_version, psr_caps; + unsigned char psr_en; + + /* disable psr function */ + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en); + psr_en &= ~DP_PSR_ENABLE; + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en); + + /* check panel psr version */ + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version); + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_CAPS, &psr_caps); + dev_info(dp->dev, "Panel PSR version : %x.%x\n", psr_version, psr_caps); + + if (!(psr_version & DP_PSR_IS_SUPPORTED)) + return -1; + + /* Main-Link transmitter remains active during PSR active states */ + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en); + psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en); + + /* enable PSR function */ + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en); + psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE | + DP_PSR_CRC_VERIFICATION; + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en); + + /* read sink psr state */ + analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en); + dev_info(dp->dev, "DP_PSR_EN_CFG: %x\n", psr_en); + + analogix_dp_enable_psr_crc(dp); + dp->psr_support = true; + + return 0; +} + static void analogix_dp_init_dp(struct analogix_dp_device *dp) { analogix_dp_reset(dp); @@ -921,6 +987,9 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
/* Enable video */ analogix_dp_start_video(dp); + + /* Enable PSR support */ + analogix_dp_enable_psr(dp); }
int analogix_dp_get_modes(struct drm_connector *connector) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index b456380..948e59a 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -178,6 +178,8 @@ struct analogix_dp_device { bool force_hpd; unsigned char edid[EDID_BLOCK_LENGTH * 2];
+ bool psr_support; + struct analogix_dp_plat_data *plat_data; };
@@ -278,4 +280,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp); +void analogix_dp_send_vsc(struct analogix_dp_device *dp, int db1); + #endif /* _ANALOGIX_DP_CORE_H */ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 31366bf..8d8c37a 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp) reg |= SCRAMBLING_DISABLE; writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET); } + +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp) +{ + writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE, + dp->reg_base + ANALOGIX_DP_CRC_CON); + + usleep_range(10, 20); + + writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON); +} + +void analogix_dp_send_vsc(struct analogix_dp_device *dp, int db1) +{ + unsigned int val; + + /* don't send info frame */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val &= ~IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + + /* configure single frame update mode */ + writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL); + + /* configure VSC HB0 ~ HB3 */ + writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0); + writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1); + writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2); + writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3); + + /* configure VSC HB0 ~ HB3 */ + writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0); + writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1); + writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2); + writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3); + + /* configure DB0 / DB1 values */ + writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0); + writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1); + + /* set reuse spd inforframe */ + val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); + val |= REUSE_SPD_EN; + writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); + + /* mark info frame update */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val = (val | IF_UP) & ~IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + + /* send info frame */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val |= IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); +} diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h index cdcc6c5..a2698e4 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h @@ -22,6 +22,8 @@ #define ANALOGIX_DP_VIDEO_CTL_8 0x3C #define ANALOGIX_DP_VIDEO_CTL_10 0x44
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0 0xD8 + #define ANALOGIX_DP_PLL_REG_1 0xfc #define ANALOGIX_DP_PLL_REG_2 0x9e4 #define ANALOGIX_DP_PLL_REG_3 0x9e8 @@ -30,6 +32,21 @@
#define ANALOGIX_DP_PD 0x12c
+#define ANALOGIX_DP_IF_TYPE 0x244 +#define ANALOGIX_DP_IF_PKT_DB1 0x254 +#define ANALOGIX_DP_IF_PKT_DB2 0x258 +#define ANALOGIX_DP_SPD_HB0 0x2F8 +#define ANALOGIX_DP_SPD_HB1 0x2FC +#define ANALOGIX_DP_SPD_HB2 0x300 +#define ANALOGIX_DP_SPD_HB3 0x304 +#define ANALOGIX_DP_SPD_PB0 0x308 +#define ANALOGIX_DP_SPD_PB1 0x30C +#define ANALOGIX_DP_SPD_PB2 0x310 +#define ANALOGIX_DP_SPD_PB3 0x314 +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL 0x318 +#define ANALOGIX_DP_VSC_SHADOW_DB0 0x31C +#define ANALOGIX_DP_VSC_SHADOW_DB1 0x320 + #define ANALOGIX_DP_LANE_MAP 0x35C
#define ANALOGIX_DP_ANALOG_CTL_1 0x370 @@ -103,6 +120,8 @@
#define ANALOGIX_DP_SOC_GENERAL_CTL 0x800
+#define ANALOGIX_DP_CRC_CON 0x890 + /* ANALOGIX_DP_TX_SW_RESET */ #define RESET_DP_TX (0x1 << 0)
@@ -151,6 +170,7 @@ #define VID_CHK_UPDATE_TYPE_SHIFT (4) #define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) #define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) +#define REUSE_SPD_EN (0x1 << 3)
/* ANALOGIX_DP_VIDEO_CTL_8 */ #define VID_HRES_TH(x) (((x) & 0xf) << 4) @@ -376,4 +396,12 @@ #define VIDEO_MODE_SLAVE_MODE (0x1 << 0) #define VIDEO_MODE_MASTER_MODE (0x0 << 0)
+/* ANALOGIX_DP_PKT_SEND_CTL */ +#define IF_UP (0x1 << 4) +#define IF_EN (0x1 << 0) + +/* ANALOGIX_DP_CRC_CON */ +#define PSR_VID_CRC_FLUSH (0x1 << 2) +#define PSR_VID_CRC_ENABLE (0x1 << 0) + #endif /* _ANALOGIX_DP_REG_H */ diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index eb1b8ac..44a7a27 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,12 @@ struct rockchip_dp_device { struct regmap *grf; struct reset_control *rst;
+ struct workqueue_struct *dp_workq; + struct work_struct psr_work; + struct notifier_block psr_nb; + unsigned int psr_state; + struct mutex psr_state_mutex; + const struct rockchip_dp_chip_data *data;
struct analogix_dp_plat_data plat_data; @@ -97,6 +104,50 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data) return 0; }
+static int psr_notify(struct notifier_block *psr_nb, unsigned long action, + void *data) +{ + struct rockchip_dp_device *dp = to_dp(psr_nb); + + switch (action) { + case VBLANK_ENABLE: + mutex_lock(&dp->psr_state_mutex); + dp->psr_state = 0; + mutex_unlock(&dp->psr_state_mutex); + break; + + case VBLANK_DISABLE: + mutex_lock(&dp->psr_state_mutex); + dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE; + mutex_unlock(&dp->psr_state_mutex); + 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, @@ -131,6 +182,7 @@ static void rockchip_dp_drm_encoder_mode_set(struct drm_encoder *encoder, /* do nothing */ }
+ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder) { struct rockchip_dp_device *dp = to_dp(encoder); @@ -322,6 +374,19 @@ 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->psr_nb.notifier_call = psr_notify; + rockchip_drm_vblank_register_notifier(&dp->psr_nb); + + 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; + mutex_init(&dp->psr_state_mutex); + 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_notify.c b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c index 84111d9..652f03f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_notify.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.c @@ -1,7 +1,9 @@ #include "rockchip_drm_notify.h"
static RAW_NOTIFIER_HEAD(vblank_chain); +static RAW_NOTIFIER_HEAD(psr_ready_chain); static DEFINE_MUTEX(vblank_lock); +static DEFINE_MUTEX(psr_ready_lock);
/** * rockchip_drm_vblank_enable - Send Vblank enable event. Will only enable send @@ -64,3 +66,40 @@ int rockchip_drm_vblank_unregister_notifier(struct notifier_block *nb) return ret; } EXPORT_SYMBOL_GPL(rockchip_drm_vblank_unregister_notifier); + + +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 index 2f28afa..34614fc 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_notify.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_notify.h @@ -26,4 +26,8 @@ void rockchip_drm_vblank_disable(void); int rockchip_drm_vblank_register_notifier(struct notifier_block *nb); int rockchip_drm_vblank_unregister_notifier(struct notifier_block *nb);
+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 fb6ce4b..53d1809 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -118,6 +118,9 @@ struct vop { struct completion wait_update_complete; struct drm_pending_vblank_event *event;
+ struct notifier_block psr_prepare_nb; + struct completion line_flag_completion; + const struct vop_data *data;
uint32_t *regsbak; @@ -428,6 +431,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(&vop->irq_lock, flags); }
+/* + * (1) each frame starts at the start of the Vsync pulse which is signaled by + * the "FRAME_SYNC" interrupt. + * (2) the active data region of each frame ends at dsp_vact_end + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, + * to get "LINE_FLAG" interrupt at the end of the active on screen data. + * + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end + * Interrupts + * LINE_FLAG -------------------------------+ + * FRAME_SYNC ----+ | + * | | + * v v + * | Vsync | Vbp | Vactive | Vfp | + * ^ ^ ^ ^ + * | | | | + * | | | | + * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END + * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END + * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END + * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END + */ + +static void vop_line_flag_irq_enable(struct vop *vop, int line_num) +{ + unsigned long flags; + + if (WARN_ON(!vop->is_enabled)) + return; + + spin_lock_irqsave(&vop->irq_lock, flags); + + VOP_CTRL_SET(vop, line_flag_num_0, line_num); + VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); + vop_cfg_done(vop); + + spin_unlock_irqrestore(&vop->irq_lock, flags); +} + +static void vop_line_flag_irq_disable(struct vop *vop) +{ + unsigned long flags; + + if (WARN_ON(!vop->is_enabled)) + return; + + spin_lock_irqsave(&vop->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0); + + spin_unlock_irqrestore(&vop->irq_lock, flags); +} + static void vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); @@ -1128,6 +1184,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; @@ -1159,6 +1239,13 @@ static irqreturn_t vop_isr(int irq, void *data) ret = IRQ_HANDLED; }
+ if (active_irqs & LINE_FLAG_INTR) { + if (!completion_done(&vop->line_flag_completion)) + complete(&vop->line_flag_completion); + active_irqs &= ~LINE_FLAG_INTR; + ret = IRQ_HANDLED; + } + if (active_irqs & FS_INTR) { drm_crtc_handle_vblank(crtc); vop_handle_vblank(vop); @@ -1255,8 +1342,12 @@ 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); crtc->port = port; rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index ff4f52e..34fcd03 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -61,6 +61,9 @@ struct vop_ctrl { struct vop_reg hpost_st_end; struct vop_reg vpost_st_end;
+ struct vop_reg line_flag_num_0; + struct vop_reg line_flag_num_1; + struct vop_reg cfg_done; };
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 6f42e56..bf78892 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -299,6 +299,8 @@ static const struct vop_ctrl rk3399_ctrl_data = { .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0), .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0), .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), + .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0), };
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 9ef89de..dd38527 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -41,6 +41,9 @@ struct analogix_dp_plat_data { struct drm_display_mode *); };
+int analogix_dp_actice_psr(struct device *dev); +int analogix_dp_inactice_psr(struct device *dev); + int analogix_dp_resume(struct device *dev); int analogix_dp_suspend(struct device *dev);
On Tue, May 31, 2016 at 09:37:36PM +0800, Yakir Yang wrote:
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
Looks like you didn't wire up the drm_framebuffer->funcs->dirty callback for manual upload of simple clients like bootsplash or fbdev. I think that's needed. At least it's needed for every other manual upload dsi and edp psr implementation. -Daniel
- Yakir
Thanks,
Yakir Yang (2): drm/rockchip: add a notify event about vblank enable/disable drm/rockchip: analogix: add eDP PSR function
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 69 ++++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 5 + drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 54 +++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 28 ++++++ drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 65 +++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 105 +++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 33 +++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 96 +++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 + include/drm/bridge/analogix_dp.h | 3 + 12 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.h
-- 1.9.1
Hi Daniel,
On 05/31/2016 10:38 PM, Daniel Vetter wrote:
On Tue, May 31, 2016 at 09:37:36PM +0800, Yakir Yang wrote:
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
Looks like you didn't wire up the drm_framebuffer->funcs->dirty callback for manual upload of simple clients like bootsplash or fbdev. I think that's needed. At least it's needed for every other manual upload dsi and edp psr implementation. -Daniel
That's great, thanks for your remind. Seems like userspace which does frontbuffer rendering must call this ioctl to flush out the changes on manual-update display outputs. It's helpful to hook this callback to notify eDP refresh the eDP RFB(remote frame buffer).
But I think this is hard to used on Rockchip eDP controller, Rockchip eDP driver just used two modes, Sink Device PSR_S0 (PSR inactive), and Sink Device PSR_S2 (PSR active, display from RFB).
I think the "dirty" callback is only used when Sink device enter into PSR_S3 mode (PSR active, capture and display), need to update the remote frame buffer. But on Rockchip platform the panel would be very easy to lose frame in this PSR mode. I'm confused in this case, so I didn't enable that.
If we didn't enable the PSR_S3 mode, then we don't need to update the panel remote frame buffer, so this "->dirty" callback would be unused in this case.
Thanks, - Yakir
- Yakir
Thanks,
Yakir Yang (2): drm/rockchip: add a notify event about vblank enable/disable drm/rockchip: analogix: add eDP PSR function
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 69 ++++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 5 + drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 54 +++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 28 ++++++ drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 65 +++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 105 +++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 33 +++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 96 +++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 + include/drm/bridge/analogix_dp.h | 3 + 12 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.h
-- 1.9.1
Hi Daniel,
On 05/31/2016 10:38 PM, Daniel Vetter wrote:
On Tue, May 31, 2016 at 09:37:36PM +0800, Yakir Yang wrote:
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
Looks like you didn't wire up the drm_framebuffer->funcs->dirty callback for manual upload of simple clients like bootsplash or fbdev. I think that's needed. At least it's needed for every other manual upload dsi and edp psr implementation. -Daniel
That's great, thanks for your remind. Seems like userspace which does frontbuffer rendering must call this ioctl to flush out the changes on manual-update display outputs. It's helpful to hook this callback to notify eDP refresh the eDP RFB(remote frame buffer).
But I think this is hard to used on Rockchip eDP controller, Rockchip eDP driver just used two modes, Sink Device PSR_S0 (PSR inactive), and Sink Device PSR_S2 (PSR active, display from RFB).
I think the "dirty" callback is only used when Sink device enter into PSR_S3 mode (PSR active, capture and display), need to update the remote frame buffer. But on Rockchip platform the panel would be very easy to lose frame in this PSR mode. I'm confused in this case, so I didn't enable that.
If we didn't enable the PSR_S3 mode, then we don't need to update the panel remote frame buffer, so this "->dirty" callback would be unused in this case.
Thanks, - Yakir
- Yakir
Thanks,
Yakir Yang (2): drm/rockchip: add a notify event about vblank enable/disable drm/rockchip: analogix: add eDP PSR function
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 69 ++++++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 5 + drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 54 +++++++++++ drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 28 ++++++ drivers/gpu/drm/rockchip/Makefile | 2 +- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 65 +++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.c | 105 +++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_notify.h | 33 +++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 96 +++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 + include/drm/bridge/analogix_dp.h | 3 + 12 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_notify.h
-- 1.9.1
On Wed, Jun 01, 2016 at 10:54:09AM +0800, Yakir Yang wrote:
Hi Daniel,
On 05/31/2016 10:38 PM, Daniel Vetter wrote:
On Tue, May 31, 2016 at 09:37:36PM +0800, Yakir Yang wrote:
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
Looks like you didn't wire up the drm_framebuffer->funcs->dirty callback for manual upload of simple clients like bootsplash or fbdev. I think that's needed. At least it's needed for every other manual upload dsi and edp psr implementation. -Daniel
That's great, thanks for your remind. Seems like userspace which does frontbuffer rendering must call this ioctl to flush out the changes on manual-update display outputs. It's helpful to hook this callback to notify eDP refresh the eDP RFB(remote frame buffer).
But I think this is hard to used on Rockchip eDP controller, Rockchip eDP driver just used two modes, Sink Device PSR_S0 (PSR inactive), and Sink Device PSR_S2 (PSR active, display from RFB).
I think the "dirty" callback is only used when Sink device enter into PSR_S3 mode (PSR active, capture and display), need to update the remote frame buffer. But on Rockchip platform the panel would be very easy to lose frame in this PSR mode. I'm confused in this case, so I didn't enable that.
If we didn't enable the PSR_S3 mode, then we don't need to update the panel remote frame buffer, so this "->dirty" callback would be unused in this case.
Sorry missed your reply here somehow. Every time you don't scan out the drm_framebuffer directly, but from any kind of intermediate buffer, you _must_ implement the dirty callback. Usually the simplest way is to kick the device out of self-refresh and arm the time to re-enter self-refresh.
I'm not entirely clear where your RFB is, so no idea whether that is scanning out from the drm_framebuffer data directly, or whether that's some on-chip or in-sink framebuffer cache.
We have a ridiculously nasty testuite in i-g-t for all these cases (we use it to validate our framebuffer compression code, psr code and soon also dsi manual upload). Unfortunately it's not yet ported over to be a generic testcase. But at least for PSR that would be easy, since every PSR panel has eDP sink crc support. And the testcase does require some kind of display CRC support to validate actual screen contents, as opposed to what's in memory in the drm_framebuffer. -Daniel
Hi Daniel,
On 06/02/2016 10:19 PM, Daniel Vetter wrote:
On Wed, Jun 01, 2016 at 10:54:09AM +0800, Yakir Yang wrote:
Hi Daniel,
On 05/31/2016 10:38 PM, Daniel Vetter wrote:
On Tue, May 31, 2016 at 09:37:36PM +0800, Yakir Yang wrote:
The full name of PSR is Panel Self Refresh, panel device could refresh itself with the hardware framebuffer in panel, this would make a lots of sense to save the power consumption.
For example, when desktop haven't change the context for a long time, then we could refresh the data to the hardware framebuffer of panel, and then let panel enter into PSR mode. After that system could poweroff the LCDC controller and eDP controller, just let panel refresh the screen by itself.
It's hard to decide when panel should enter into PSR or exit from PSR, in this time I chose to let the drm_vblank enable/disable event driver the PSR.
This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP thread[1].
Looks like you didn't wire up the drm_framebuffer->funcs->dirty callback for manual upload of simple clients like bootsplash or fbdev. I think that's needed. At least it's needed for every other manual upload dsi and edp psr implementation. -Daniel
That's great, thanks for your remind. Seems like userspace which does frontbuffer rendering must call this ioctl to flush out the changes on manual-update display outputs. It's helpful to hook this callback to notify eDP refresh the eDP RFB(remote frame buffer).
But I think this is hard to used on Rockchip eDP controller, Rockchip eDP driver just used two modes, Sink Device PSR_S0 (PSR inactive), and Sink Device PSR_S2 (PSR active, display from RFB).
I think the "dirty" callback is only used when Sink device enter into PSR_S3 mode (PSR active, capture and display), need to update the remote frame buffer. But on Rockchip platform the panel would be very easy to lose frame in this PSR mode. I'm confused in this case, so I didn't enable that.
If we didn't enable the PSR_S3 mode, then we don't need to update the panel remote frame buffer, so this "->dirty" callback would be unused in this case.
Sorry missed your reply here somehow. Every time you don't scan out the drm_framebuffer directly, but from any kind of intermediate buffer, you _must_ implement the dirty callback. Usually the simplest way is to kick the device out of self-refresh and arm the time to re-enter self-refresh.
Sounds good, use timer to driver the PSR status, and abandon the vblank enable/disable event. Okay, I start to do some experiments.
I'm not entirely clear where your RFB is, so no idea whether that is scanning out from the drm_framebuffer data directly, or whether that's some on-chip or in-sink framebuffer cache.
RFB is an in-sink framebuffer cache. When Sink enter into PSR_State1 or PSR_State3, then it can capture the eDP timing from source to update the RFB.
We have a ridiculously nasty testuite in i-g-t for all these cases (we use it to validate our framebuffer compression code, psr code and soon also dsi manual upload). Unfortunately it's not yet ported over to be a generic testcase. But at least for PSR that would be easy, since every PSR panel has eDP sink crc support. And the testcase does require some kind of display CRC support to validate actual screen contents, as opposed to what's in memory in the drm_framebuffer. -Daniel
Sorry, I'm not very clear about what you mean, seems you have a reference code about PSR test ;)
Thanks, - Yakir
dri-devel@lists.freedesktop.org