[RFC,v2,3/3] drm/rockchip: analogix_dp: add PSR support
diff mbox

Message ID 1464872258-18468-1-git-send-email-ykk@rock-chips.com
State New
Headers show

Commit Message

Yakir Yang June 2, 2016, 12:57 p.m. UTC
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

Comments

Daniel Vetter June 2, 2016, 1:18 p.m. UTC | #1
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
Yakir Yang June 2, 2016, 1:37 p.m. UTC | #2
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

Patch
diff mbox

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);