Message ID | 1464872258-18468-1-git-send-email-ykk@rock-chips.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jun 02, 2016 at 08:57:38PM +0800, Yakir Yang wrote: > 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@rock-chips.com> > --- > Changes in v2: > - Remove vblank notify out (Daniel) > - Create a psr_active() callback in vop data struct. Still contains a notifier. Still doesn't contain a proper fb->dirty callback. Please don't just act on review without understanding the deeper implications, since I didn't ask you to remove the vblank logic (you probably still need that), I suggested to implement it differently (withotu notifiers). -Daniel > > 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@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 > > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Daniel, Thanks for your fast respond. On 06/02/2016 09:18 PM, Daniel Vetter wrote: > On Thu, Jun 02, 2016 at 08:57:38PM +0800, Yakir Yang wrote: >> 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@rock-chips.com> >> --- >> Changes in v2: >> - Remove vblank notify out (Daniel) >> - Create a psr_active() callback in vop data struct. > Still contains a notifier. Still doesn't contain a proper fb->dirty > callback. Please don't just act on review without understanding the deeper > implications, since I didn't ask you to remove the vblank logic (you > probably still need that), I suggested to implement it differently > (withotu notifiers). > -Daniel Yes, I misunderstand what you comment before, I thought only the vblank notify is bad, so i create a callback entry in vop. But the vact_end event is still an evil too, specific to a given crtc. Hmmm, after i abandon the notify, i need to found a good way for eDP to get the vact_end or vblank event. That would be a question, have no idea now..... As for why i ignore the the fb->dirty callback in this case, please see my previous reply to you. Thanks, - Yakir >> 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@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 >> >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/dri-devel
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@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);
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@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