mbox series

[v6,00/30] DRM Rockchip rk3399 (Kevin)

Message ID 20180405095000.9756-1-enric.balletbo@collabora.com
Headers show
Series DRM Rockchip rk3399 (Kevin) | expand

Message

Enric Balletbo i Serra April 5, 2018, 9:49 a.m. UTC
Hi,

This patchset includes cleanups, improvements, and bug fixes for
Rockchip DRM driver and PSR support.

This new version is the same as before removing some of the patches
already applied and fixing the Exynos issue due patch '[v4 15/38]
drm/bridge: analogix_dp: Ensure edp is disabled when shutting down
the panel' reported by Marek.

Regards,
Enric

Changes in v6:
- Removed the following patches as are already applied.
  [PATCH v5 01/36] drm/bridge: analogix_dp: detect Sink PSR state after
  configuring the PSR
  [PATCH v5 02/36] drm/rockchip: Remove analogix psr worker
  [PATCH v5 03/36] drm/bridge: analogix_dp: Don't change psr while
  bridge is disabled
- Explain in the commit message why we need to increase
  the delay in the timeout loop in
  [PATCH v5 07/36] drm/bridge: analogix_dp: Move enable video into
  config_video()
- Add Reviewed-by: Archit Taneja <architt@codeaurora.org> for the
  drm/bridge parts
- Add Reviewed-by: Heiko Stuebner <heiko@sntech.de> for
  [PATCH v5 19/36] drm/rockchip: Restore psr->state when enable/disable
  psr failed

Changes in v5:
- Removed the following patches as are already applied.
  [PATCH v4 01/38] drm/bridge: analogix_dp: set psr activate/deactivate
  when enable/disable bridge
  [PATCH v4 02/38] drm/rockchip: Don't use atomic constructs for psr
- Add Mareks tested-tag and including the missing people.
- [PATCH v4 15/38] move analogix_dp_set_analog_power_down() before
  phy_power_off() to fix Exynos issue.

Changes in v4:
- Rebased all on top of drm-misc-next
- Removed the following patches as are already applied.
  [PATCH v3 01/43] drm/rockchip: Get rid of unnecessary struct fields
  [PATCH v3 02/43] drm/rockchip: support prime import sg table
  [PATCH v3 03/43] drm/rockchip: Respect page offset for PRIME mmap
  calls
- Removed the following patches as now are part of another patchset
  [PATCH v3 05/43] drm/bridge: analogix_dp: Don't power bridge in
  analogix_dp_bind
  [PATCH v3 33/43] drm/panel: simple: Change mode for Sharp lq123p1jx31

Changes in v3:
- Addressed some of the comments from Sean on the v2

Changes in v2:
- A few patches have been replaced by newer and cleaner versions from
  the ChromeOS kernel gerrit, especially about disallowing PSR for the
  whole atomic commit.


Douglas Anderson (4):
  drm/bridge: analogix_dp: Reorder plat_data->power_off to happen sooner
  drm/bridge: analogix_dp: Properly log AUX CH errors
  drm/bridge: analogix_dp: Properly disable aux chan retries on rockchip
  drm/bridge: analogix_dp: Split the platform-specific poweron in two
    parts

Kristian H. Kristensen (1):
  drm/rockchip: Disable PSR on input events

Lin Huang (6):
  drm/bridge: analogix_dp: Move enable video into config_video()
  drm/bridge: analogix_dp: Check AUX_EN status when doing AUX transfer
  drm/bridge: analogix_dp: Ensure edp is disabled when shutting down the
    panel
  drm/bridge: analogix_dp: Extend hpd check time to 100ms
  drm/bridge: analogix_dp: Check dpcd write/read status
  drm/bridge: analogix_dp: Reset aux channel if an error occurred

Mark Yao (1):
  drm/rockchip: pre dither down when output bpc is 8bit

Tomasz Figa (7):
  drm/rockchip: analogix_dp: Do not call Analogix code before bind
  drm/rockchip: Cancel PSR enable work before changing the state
  drm/rockchip: psr: Avoid redundant calls to .set() callback
  drm/rockchip: psr: Sanitize semantics of allow/disallow API
  drm/rockchip: Disable PSR from reboot notifier
  drm/rockchip: Disallow PSR for the whole atomic commit
  drm/rockchip: psr: Remove flush by CRTC

zain wang (11):
  drm/bridge: analogix_dp: Don't use fast link training when panel just
    powered up
  drm/bridge: analogix_dp: Retry bridge enable when it failed
  drm/bridge: analogix_dp: Wait for HPD signal before configuring link
  drm/bridge: analogix_dp: Set PD_INC_BG first when powering up edp phy
  drm/bridge: analogix_dp: Fix incorrect usage of enhanced mode
  drm/bridge: analogix_dp: Fix AUX_PD bit for Rockchip
  drm/rockchip: Restore psr->state when enable/disable psr failed
  drm/bridge: analogix_dp: Don't use ANALOGIX_DP_PLL_CTL to control pll
  drm/bridge: analogix_dp: Fix timeout of video streamclk config
  drm/bridge: analogix_dp: Fix incorrect operations with register
    ANALOGIX_DP_FUNC_EN_1
  drm/bridge: analogix_dp: Move fast link training detect to set_bridge

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 331 +++++++++++++++------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   5 +-
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 236 +++++++++------
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |   7 +
 drivers/gpu/drm/exynos/exynos_dp.c                 |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  37 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  61 +++-
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 309 +++++++++++++------
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |   7 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   1 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   1 +
 include/drm/bridge/analogix_dp.h                   |   3 +-
 14 files changed, 710 insertions(+), 304 deletions(-)

Comments

Andrzej Hajda April 12, 2018, 9:54 a.m. UTC | #1
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> Driver callbacks, such as system suspend or resume can be called any
> time, specifically they can be called before the component bind
> callback. Let's use dp->adp pointer as a safeguard and skip calling
> Analogix entry points if it is an ERR_PTR().

From purity PoV I would store either 0 either valid pointer in dp->adp,
but functionally it should be the same.

Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej


>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index 23317a2269e1..6d45d62466b3 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -368,6 +368,8 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
>  	analogix_dp_unbind(dp->adp);
>  	rockchip_drm_psr_unregister(&dp->encoder);
>  	dp->encoder.funcs->destroy(&dp->encoder);
> +
> +	dp->adp = ERR_PTR(-ENODEV);
>  }
>  
>  static const struct component_ops rockchip_dp_component_ops = {
> @@ -391,6 +393,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	dp->dev = dev;
> +	dp->adp = ERR_PTR(-ENODEV);
>  	dp->plat_data.panel = panel;
>  
>  	ret = rockchip_dp_of_probe(dp);
> @@ -414,6 +417,9 @@ static int rockchip_dp_suspend(struct device *dev)
>  {
>  	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>  
> +	if (IS_ERR(dp->adp))
> +		return 0;
> +
>  	return analogix_dp_suspend(dp->adp);
>  }
>  
> @@ -421,6 +427,9 @@ static int rockchip_dp_resume(struct device *dev)
>  {
>  	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>  
> +	if (IS_ERR(dp->adp))
> +		return 0;
> +
>  	return analogix_dp_resume(dp->adp);
>  }
>  #endif
Andrzej Hajda April 16, 2018, 7:19 a.m. UTC | #2
+CC: linux-input list and maintainer


On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: "Kristian H. Kristensen" <hoegsberg@google.com>
>
> To improve PSR exit latency, we speculatively start exiting when we
> receive input events. Occasionally, this may lead to false positives,
> but most of the time we get a head start on coming out of PSR. Depending
> on how userspace takes to produce a new frame in response to the event,
> this can completely hide the exit latency. In case of Chrome OS, we
> typically get the input notifier 50ms or more before the dirty_fb
> triggered exit.

As I see from the code below, you just need notification from input
subsystem on user activity.
Maybe there is some simpler notification mechanism for such things?
If not, maybe such helper should be created in input subsystem, I
suppose it could be reused in other places as well.

Regards
Andrzej

>
> Signed-off-by: Kristian H. Kristensen <hoegsberg@google.com>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 134 ++++++++++++++++++++++++++++
>  1 file changed, 134 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index 9376f4396b6b..a107845ba97c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -12,6 +12,8 @@
>   * GNU General Public License for more details.
>   */
>  
> +#include <linux/input.h>
> +
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
>  
> @@ -35,6 +37,9 @@ struct psr_drv {
>  	enum psr_state		state;
>  
>  	struct delayed_work	flush_work;
> +	struct work_struct	disable_work;
> +
> +	struct input_handler    input_handler;
>  
>  	int (*set)(struct drm_encoder *encoder, bool enable);
>  };
> @@ -133,6 +138,18 @@ static void psr_flush_handler(struct work_struct *work)
>  	mutex_unlock(&psr->lock);
>  }
>  
> +static void psr_disable_handler(struct work_struct *work)
> +{
> +	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
> +
> +	/* If the state has changed since we initiated the flush, do nothing */
> +	mutex_lock(&psr->lock);
> +	if (psr->state == PSR_ENABLE)
> +		psr_set_state_locked(psr, PSR_FLUSH);
> +	mutex_unlock(&psr->lock);
> +	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
> +}
> +
>  /**
>   * rockchip_drm_psr_activate - activate PSR on the given pipe
>   * @encoder: encoder to obtain the PSR encoder
> @@ -173,6 +190,7 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
>  	psr->active = false;
>  	mutex_unlock(&psr->lock);
>  	cancel_delayed_work_sync(&psr->flush_work);
> +	cancel_work_sync(&psr->disable_work);
>  
>  	return 0;
>  }
> @@ -226,6 +244,95 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
>  
> +static void psr_input_event(struct input_handle *handle,
> +			    unsigned int type, unsigned int code,
> +			    int value)
> +{
> +	struct psr_drv *psr = handle->handler->private;
> +
> +	schedule_work(&psr->disable_work);
> +}
> +
> +static int psr_input_connect(struct input_handler *handler,
> +			     struct input_dev *dev,
> +			     const struct input_device_id *id)
> +{
> +	struct input_handle *handle;
> +	int error;
> +
> +	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
> +	if (!handle)
> +		return -ENOMEM;
> +
> +	handle->dev = dev;
> +	handle->handler = handler;
> +	handle->name = "rockchip-psr";
> +
> +	error = input_register_handle(handle);
> +	if (error)
> +		goto err2;
> +
> +	error = input_open_device(handle);
> +	if (error)
> +		goto err1;
> +
> +	return 0;
> +
> +err1:
> +	input_unregister_handle(handle);
> +err2:
> +	kfree(handle);
> +	return error;
> +}
> +
> +static void psr_input_disconnect(struct input_handle *handle)
> +{
> +	input_close_device(handle);
> +	input_unregister_handle(handle);
> +	kfree(handle);
> +}
> +
> +/* Same device ids as cpu-boost */
> +static const struct input_device_id psr_ids[] = {
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +			 INPUT_DEVICE_ID_MATCH_ABSBIT,
> +		.evbit = { BIT_MASK(EV_ABS) },
> +		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
> +			    BIT_MASK(ABS_MT_POSITION_X) |
> +			    BIT_MASK(ABS_MT_POSITION_Y) },
> +	}, /* multi-touch touchscreen */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_ABS) },
> +		.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) }
> +
> +	}, /* stylus or joystick device */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
> +	}, /* pointer (e.g. trackpad, mouse) */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(KEY_ESC)] = BIT_MASK(KEY_ESC) },
> +	}, /* keyboard */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
> +	}, /* joysticks not caught by ABS_X above */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
> +	}, /* gamepad */
> +	{ },
> +};
> +
>  /**
>   * rockchip_drm_psr_register - register encoder to psr driver
>   * @encoder: encoder that obtain the PSR function
> @@ -239,6 +346,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  {
>  	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>  	struct psr_drv *psr;
> +	int error;
>  
>  	if (!encoder || !psr_set)
>  		return -EINVAL;
> @@ -248,6 +356,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  		return -ENOMEM;
>  
>  	INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
> +	INIT_WORK(&psr->disable_work, psr_disable_handler);
>  	mutex_init(&psr->lock);
>  
>  	psr->active = true;
> @@ -255,11 +364,33 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  	psr->encoder = encoder;
>  	psr->set = psr_set;
>  
> +	psr->input_handler.event = psr_input_event;
> +	psr->input_handler.connect = psr_input_connect;
> +	psr->input_handler.disconnect = psr_input_disconnect;
> +	psr->input_handler.name =
> +		kasprintf(GFP_KERNEL, "rockchip-psr-%s", encoder->name);
> +	if (!psr->input_handler.name) {
> +		error = -ENOMEM;
> +		goto err2;
> +	}
> +	psr->input_handler.id_table = psr_ids;
> +	psr->input_handler.private = psr;
> +
> +	error = input_register_handler(&psr->input_handler);
> +	if (error)
> +		goto err1;
> +
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_add_tail(&psr->list, &drm_drv->psr_list);
>  	mutex_unlock(&drm_drv->psr_list_lock);
>  
>  	return 0;
> +
> + err1:
> +	kfree(psr->input_handler.name);
> + err2:
> +	kfree(psr);
> +	return error;
>  }
>  EXPORT_SYMBOL(rockchip_drm_psr_register);
>  
> @@ -279,8 +410,11 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
>  		if (psr->encoder == encoder) {
> +			input_unregister_handler(&psr->input_handler);
>  			cancel_delayed_work_sync(&psr->flush_work);
> +			cancel_work_sync(&psr->disable_work);
>  			list_del(&psr->list);
> +			kfree(psr->input_handler.name);
>  			kfree(psr);
>  		}
>  	}
Andrzej Hajda April 16, 2018, 8:55 a.m. UTC | #3
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> If we change the state first and reschedule later, we might have the
> work executed according to previous scheduled time and end up with PSR
> re-enabled instantly. Let's cancel the work before changing the state.
>
> While at it, consolidate psr_disable_handler() to just call
> rockchip_drm_do_flush(), as they are both supposed to do the same.
>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 20 ++++++++------------
>  1 file changed, 8 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index a107845ba97c..c8655e625ba2 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -138,18 +138,6 @@ static void psr_flush_handler(struct work_struct *work)
>  	mutex_unlock(&psr->lock);
>  }
>  
> -static void psr_disable_handler(struct work_struct *work)
> -{
> -	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
> -
> -	/* If the state has changed since we initiated the flush, do nothing */
> -	mutex_lock(&psr->lock);
> -	if (psr->state == PSR_ENABLE)
> -		psr_set_state_locked(psr, PSR_FLUSH);
> -	mutex_unlock(&psr->lock);
> -	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
> -}
> -
>  /**
>   * rockchip_drm_psr_activate - activate PSR on the given pipe
>   * @encoder: encoder to obtain the PSR encoder
> @@ -198,6 +186,7 @@ EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
>  
>  static void rockchip_drm_do_flush(struct psr_drv *psr)
>  {
> +	cancel_delayed_work_sync(&psr->flush_work);
>  	psr_set_state(psr, PSR_FLUSH);
>  	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);

I guess you can change it to schedule_delayed_work then.

Anyway:
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej


>  }
> @@ -244,6 +233,13 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
>  
> +static void psr_disable_handler(struct work_struct *work)
> +{
> +	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
> +
> +	rockchip_drm_do_flush(psr);
> +}
> +
>  static void psr_input_event(struct input_handle *handle,
>  			    unsigned int type, unsigned int code,
>  			    int value)
Andrzej Hajda April 16, 2018, 9:06 a.m. UTC | #4
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> The first time after we call rockchip_drm_do_flush() after
> rockchip_drm_psr_register(), we go from PSR_DISABLE to PSR_FLUSH. The
> difference between PSR_DISABLE and PSR_FLUSH is whether or not we have a
> delayed work pending - PSR is off in either state.  However
> psr_set_state() only catches the transition from PSR_FLUSH to
> PSR_DISABLE (which never happens), while going from PSR_DISABLE to
> PSR_FLUSH triggers a call to psr->set() to disable PSR while it's
> already disabled. This triggers the eDP PHY power-on sequence without
> being shut down first and this seems to occasionally leave the encoder
> unable to later enable PSR. Let's just simplify the state machine and
> simply consider PSR_DISABLE and PSR_FLUSH the same state. This lets us
> represent the hardware state by a simple boolean called "enabled" and,
> while at it, rename the misleading "active" boolean to "inhibit", which
> represents the purpose much better.

This phrase has no corresponding part in the patch.

>
> Also, userspace can (and does) trigger the rockchip_drm_do_flush() path
> from drmModeDirtyFB() at any time, whether or the encoder is active. If
> no mode is set, we call into analogix_dp_psr_set() which returns -EINVAL
> because encoder->crtc is NULL. Avoid this by starting out with
> psr->allowed set to false.

ditto

>
> Signed-off-by: Kristian H. Kristensen <hoegsberg@chromium.org>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>

Original author should be first, 1st line of the patch "From:...."
suggests it should be Tomasz.

Regards
Andrzej

> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 79 +++++++++--------------------
>  1 file changed, 23 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index c8655e625ba2..448c5fde241c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -22,19 +22,13 @@
>  
>  #define PSR_FLUSH_TIMEOUT_MS	100
>  
> -enum psr_state {
> -	PSR_FLUSH,
> -	PSR_ENABLE,
> -	PSR_DISABLE,
> -};
> -
>  struct psr_drv {
>  	struct list_head	list;
>  	struct drm_encoder	*encoder;
>  
>  	struct mutex		lock;
>  	bool			active;
> -	enum psr_state		state;
> +	bool			enabled;
>  
>  	struct delayed_work	flush_work;
>  	struct work_struct	disable_work;
> @@ -78,52 +72,22 @@ static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
>  	return psr;
>  }
>  
> -static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
> +static int psr_set_state_locked(struct psr_drv *psr, bool enable)
>  {
> -	/*
> -	 * Allowed finite state machine:
> -	 *
> -	 *   PSR_ENABLE  < = = = = = >  PSR_FLUSH
> -	 *       | ^                        |
> -	 *       | |                        |
> -	 *       v |                        |
> -	 *   PSR_DISABLE < - - - - - - - - -
> -	 */
> -	if (state == psr->state || !psr->active)
> -		return;
> -
> -	/* Already disabled in flush, change the state, but not the hardware */
> -	if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
> -		psr->state = state;
> -		return;
> -	}
> +	int ret;
>  
> -	/* Actually commit the state change to hardware */
> -	switch (state) {
> -	case PSR_ENABLE:
> -		if (psr->set(psr->encoder, true))
> -			return;
> -		break;
> -
> -	case PSR_DISABLE:
> -	case PSR_FLUSH:
> -		if (psr->set(psr->encoder, false))
> -			return;
> -		break;
> -
> -	default:
> -		pr_err("%s: Unknown state %d\n", __func__, state);
> -		return;
> -	}
> +	if (!psr->active)
> +		return -EINVAL;
>  
> -	psr->state = state;
> -}
> +	if (enable == psr->enabled)
> +		return 0;
>  
> -static void psr_set_state(struct psr_drv *psr, enum psr_state state)
> -{
> -	mutex_lock(&psr->lock);
> -	psr_set_state_locked(psr, state);
> -	mutex_unlock(&psr->lock);
> +	ret = psr->set(psr->encoder, enable);
> +	if (ret)
> +		return ret;
> +
> +	psr->enabled = enable;
> +	return 0;
>  }
>  
>  static void psr_flush_handler(struct work_struct *work)
> @@ -131,10 +95,8 @@ static void psr_flush_handler(struct work_struct *work)
>  	struct psr_drv *psr = container_of(to_delayed_work(work),
>  					   struct psr_drv, flush_work);
>  
> -	/* If the state has changed since we initiated the flush, do nothing */
>  	mutex_lock(&psr->lock);
> -	if (psr->state == PSR_FLUSH)
> -		psr_set_state_locked(psr, PSR_ENABLE);
> +	psr_set_state_locked(psr, true);
>  	mutex_unlock(&psr->lock);
>  }
>  
> @@ -176,6 +138,7 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
>  
>  	mutex_lock(&psr->lock);
>  	psr->active = false;
> +	psr->enabled = false;
>  	mutex_unlock(&psr->lock);
>  	cancel_delayed_work_sync(&psr->flush_work);
>  	cancel_work_sync(&psr->disable_work);
> @@ -187,8 +150,12 @@ EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
>  static void rockchip_drm_do_flush(struct psr_drv *psr)
>  {
>  	cancel_delayed_work_sync(&psr->flush_work);
> -	psr_set_state(psr, PSR_FLUSH);
> -	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
> +
> +	mutex_lock(&psr->lock);
> +	if (!psr_set_state_locked(psr, false))
> +		mod_delayed_work(system_wq, &psr->flush_work,
> +				 PSR_FLUSH_TIMEOUT_MS);
> +	mutex_unlock(&psr->lock);
>  }
>  
>  /**
> @@ -355,8 +322,8 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  	INIT_WORK(&psr->disable_work, psr_disable_handler);
>  	mutex_init(&psr->lock);
>  
> -	psr->active = true;
> -	psr->state = PSR_DISABLE;
> +	psr->active = false;
> +	psr->enabled = false;
>  	psr->encoder = encoder;
>  	psr->set = psr_set;
>
Andrzej Hajda April 16, 2018, 9:26 a.m. UTC | #5
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> Currently both rockchip_drm_psr_activate() and _deactivate() only set the
> boolean "active" flag without actually making sure that hardware state
> complies with it.
>
> Since we are going to extend the usage of this API to properly lock PSR
> for the duration of atomic commits, we change the semantics in following
> way:
>  - a counter is used to track the number of disallow requests,
>  - PSR is actually disabled in hardware on first disallow request,
>  - PSR enable work is scheduled on last disallow request.

I guess you meant "on last allow request".
I think It would be more readable if you replace disallow with inhibit.

>
> The above allows using the API as a way to deterministically synchronize
> PSR state changes with other DRM events, i.e. atomic commits and cursor
> updates. As a nice side effect, the naming is sorted out and we have
> "inhibit" for stopping the software logic and "enable" for hardware
> state.
>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c |  4 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c     | 57 ++++++++++++++++++-------
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.h     |  4 +-
>  3 files changed, 46 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index 6d45d62466b3..080f05352195 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -134,7 +134,7 @@ static int rockchip_dp_poweron_end(struct analogix_dp_plat_data *plat_data)
>  {
>  	struct rockchip_dp_device *dp = to_dp(plat_data);
>  
> -	return rockchip_drm_psr_activate(&dp->encoder);
> +	return rockchip_drm_psr_inhibit_put(&dp->encoder);
>  }
>  
>  static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
> @@ -142,7 +142,7 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
>  	struct rockchip_dp_device *dp = to_dp(plat_data);
>  	int ret;
>  
> -	ret = rockchip_drm_psr_deactivate(&dp->encoder);
> +	ret = rockchip_drm_psr_inhibit_get(&dp->encoder);
>  	if (ret != 0)
>  		return ret;
>  
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index 448c5fde241c..e7e16d92d5a1 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -27,7 +27,7 @@ struct psr_drv {
>  	struct drm_encoder	*encoder;
>  
>  	struct mutex		lock;
> -	bool			active;
> +	int			inhibit_count;
>  	bool			enabled;
>  
>  	struct delayed_work	flush_work;
> @@ -76,7 +76,7 @@ static int psr_set_state_locked(struct psr_drv *psr, bool enable)
>  {
>  	int ret;
>  
> -	if (!psr->active)
> +	if (psr->inhibit_count > 0)
>  		return -EINVAL;
>  
>  	if (enable == psr->enabled)
> @@ -101,13 +101,18 @@ static void psr_flush_handler(struct work_struct *work)
>  }
>  
>  /**
> - * rockchip_drm_psr_activate - activate PSR on the given pipe
> + * rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
>   * @encoder: encoder to obtain the PSR encoder
>   *
> + * Decrements PSR inhibit count on given encoder. Should be called only
> + * for a PSR inhibit count increment done before. If PSR inhibit counter
> + * reaches zero, PSR flush work is scheduled to make the hardware enter
> + * PSR mode in PSR_FLUSH_TIMEOUT_MS.
> + *
>   * Returns:
>   * Zero on success, negative errno on failure.
>   */
> -int rockchip_drm_psr_activate(struct drm_encoder *encoder)
> +int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder)
>  {
>  	struct psr_drv *psr = find_psr_by_encoder(encoder);
>  
> @@ -115,21 +120,29 @@ int rockchip_drm_psr_activate(struct drm_encoder *encoder)
>  		return PTR_ERR(psr);
>  
>  	mutex_lock(&psr->lock);
> -	psr->active = true;
> +	--psr->inhibit_count;

Maybe some WARN on negative value?
With doc fixes:
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej

> +	if (!psr->inhibit_count)
> +		mod_delayed_work(system_wq, &psr->flush_work,
> +				 PSR_FLUSH_TIMEOUT_MS);
>  	mutex_unlock(&psr->lock);
>  
>  	return 0;
>  }
> -EXPORT_SYMBOL(rockchip_drm_psr_activate);
> +EXPORT_SYMBOL(rockchip_drm_psr_inhibit_put);
>  
>  /**
> - * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
> + * rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
>   * @encoder: encoder to obtain the PSR encoder
>   *
> + * Increments PSR inhibit count on given encoder. This function guarantees
> + * that after it returns PSR is turned off on given encoder and no PSR-related
> + * hardware state change occurs at least until a matching call to
> + * rockchip_drm_psr_inhibit_put() is done.
> + *
>   * Returns:
>   * Zero on success, negative errno on failure.
>   */
> -int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
> +int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder)
>  {
>  	struct psr_drv *psr = find_psr_by_encoder(encoder);
>  
> @@ -137,15 +150,15 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
>  		return PTR_ERR(psr);
>  
>  	mutex_lock(&psr->lock);
> -	psr->active = false;
> -	psr->enabled = false;
> +	psr_set_state_locked(psr, false);
> +	++psr->inhibit_count;
>  	mutex_unlock(&psr->lock);
>  	cancel_delayed_work_sync(&psr->flush_work);
>  	cancel_work_sync(&psr->disable_work);
>  
>  	return 0;
>  }
> -EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
> +EXPORT_SYMBOL(rockchip_drm_psr_inhibit_get);
>  
>  static void rockchip_drm_do_flush(struct psr_drv *psr)
>  {
> @@ -301,6 +314,11 @@ static const struct input_device_id psr_ids[] = {
>   * @encoder: encoder that obtain the PSR function
>   * @psr_set: call back to set PSR state
>   *
> + * The function returns with PSR inhibit counter initialized with one
> + * and the caller (typically encoder driver) needs to call
> + * rockchip_drm_psr_inhibit_put() when it becomes ready to accept PSR
> + * enable request.
> + *
>   * Returns:
>   * Zero on success, negative errno on failure.
>   */
> @@ -322,7 +340,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  	INIT_WORK(&psr->disable_work, psr_disable_handler);
>  	mutex_init(&psr->lock);
>  
> -	psr->active = false;
> +	psr->inhibit_count = 1;
>  	psr->enabled = false;
>  	psr->encoder = encoder;
>  	psr->set = psr_set;
> @@ -362,6 +380,11 @@ EXPORT_SYMBOL(rockchip_drm_psr_register);
>   * @encoder: encoder that obtain the PSR function
>   * @psr_set: call back to set PSR state
>   *
> + * It is expected that the PSR inhibit counter is 1 when this function is
> + * called, which corresponds to a state when related encoder has been
> + * disconnected from any CRTCs and its driver called
> + * rockchip_drm_psr_inhibit_get() to stop the PSR logic.
> + *
>   * Returns:
>   * Zero on success, negative errno on failure.
>   */
> @@ -373,10 +396,14 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
>  		if (psr->encoder == encoder) {
> -			input_unregister_handler(&psr->input_handler);
> -			cancel_delayed_work_sync(&psr->flush_work);
> -			cancel_work_sync(&psr->disable_work);
> +			/*
> +			 * Any other value would mean that the encoder
> +			 * is still in use.
> +			 */
> +			WARN_ON(psr->inhibit_count != 1);
> +
>  			list_del(&psr->list);
> +			input_unregister_handler(&psr->input_handler);
>  			kfree(psr->input_handler.name);
>  			kfree(psr);
>  		}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> index 06537ee27565..40e026c14168 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> @@ -18,8 +18,8 @@
>  void rockchip_drm_psr_flush_all(struct drm_device *dev);
>  int rockchip_drm_psr_flush(struct drm_crtc *crtc);
>  
> -int rockchip_drm_psr_activate(struct drm_encoder *encoder);
> -int rockchip_drm_psr_deactivate(struct drm_encoder *encoder);
> +int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder);
> +int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder);
>  
>  int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  			int (*psr_set)(struct drm_encoder *, bool enable));
Andrzej Hajda April 16, 2018, 9:51 a.m. UTC | #6
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> Currently PSR flush is triggered from CRTC's .atomic_begin() callback,
> which is executed after modeset disables and enables and before plane
> updates are committed. Since PSR flush and re-enable can be triggered
> asynchronously by external sources (input event, delayed work), it can
> race with hardware programming done in the aforementioned stages.
>
> This patch blocks the PSR completely before hardware programming part
> begins and unblock after it ends. This relies on reference counted PSR
> disable introduced with previous patch.
>
> Cc: Kristian H. Kristensen <hoegsberg@chromium.org>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  | 61 ++++++++++++++++++++++++++++-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  7 ----
>  2 files changed, 60 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index e266539e04e5..d4f4118b482d 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -167,8 +167,67 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
>  	return ERR_PTR(ret);
>  }
>  
> +static void
> +rockchip_drm_psr_inhibit_get_state(struct drm_atomic_state *state)
> +{
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct drm_encoder *encoder;
> +	u32 encoder_mask = 0;
> +	int i;
> +
> +	for_each_old_crtc_in_state(state, crtc, crtc_state, i) {
> +		encoder_mask |= crtc_state->encoder_mask;
> +		encoder_mask |= crtc->state->encoder_mask;

Looks clever and cryptic. More readable would be with
for_each_oldnew_crtc_in_state.
Anyway:
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej

> +	}
> +
> +	drm_for_each_encoder_mask(encoder, state->dev, encoder_mask)
> +		rockchip_drm_psr_inhibit_get(encoder);
> +}
> +
> +static void
> +rockchip_drm_psr_inhibit_put_state(struct drm_atomic_state *state)
> +{
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct drm_encoder *encoder;
> +	u32 encoder_mask = 0;
> +	int i;
> +
> +	for_each_old_crtc_in_state(state, crtc, crtc_state, i) {
> +		encoder_mask |= crtc_state->encoder_mask;
> +		encoder_mask |= crtc->state->encoder_mask;
> +	}
> +
> +	drm_for_each_encoder_mask(encoder, state->dev, encoder_mask)
> +		rockchip_drm_psr_inhibit_put(encoder);
> +}
> +
> +static void
> +rockchip_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
> +{
> +	struct drm_device *dev = old_state->dev;
> +
> +	rockchip_drm_psr_inhibit_get_state(old_state);
> +
> +	drm_atomic_helper_commit_modeset_disables(dev, old_state);
> +
> +	drm_atomic_helper_commit_modeset_enables(dev, old_state);
> +
> +	drm_atomic_helper_commit_planes(dev, old_state,
> +					DRM_PLANE_COMMIT_ACTIVE_ONLY);
> +
> +	rockchip_drm_psr_inhibit_put_state(old_state);
> +
> +	drm_atomic_helper_commit_hw_done(old_state);
> +
> +	drm_atomic_helper_wait_for_vblanks(dev, old_state);
> +
> +	drm_atomic_helper_cleanup_planes(dev, old_state);
> +}
> +
>  static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
> -	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +	.atomic_commit_tail = rockchip_atomic_helper_commit_tail_rpm,
>  };
>  
>  static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 00f7f3441cf6..f14a10ca4792 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -1029,16 +1029,9 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
>  	}
>  }
>  
> -static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
> -				  struct drm_crtc_state *old_crtc_state)
> -{
> -	rockchip_drm_psr_flush(crtc);
> -}
> -
>  static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
>  	.mode_fixup = vop_crtc_mode_fixup,
>  	.atomic_flush = vop_crtc_atomic_flush,
> -	.atomic_begin = vop_crtc_atomic_begin,
>  	.atomic_enable = vop_crtc_atomic_enable,
>  	.atomic_disable = vop_crtc_atomic_disable,
>  };
Andrzej Hajda April 16, 2018, 9:53 a.m. UTC | #7
On 05.04.2018 11:50, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> It is not used anymore after last changes and it was not even correct to
> begin with as it assumed a 1:1 relation between a CRTC and encoder,
> while in fact a CRTC can be attached to multiple encoders.
>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej

> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 35 -----------------------------
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  1 -
>  2 files changed, 36 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index 1bf5cba9a64d..b1988ac758d5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -40,23 +40,6 @@ struct psr_drv {
>  	int (*set)(struct drm_encoder *encoder, bool enable);
>  };
>  
> -static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> -{
> -	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
> -	struct psr_drv *psr;
> -
> -	mutex_lock(&drm_drv->psr_list_lock);
> -	list_for_each_entry(psr, &drm_drv->psr_list, list) {
> -		if (psr->encoder->crtc == crtc)
> -			goto out;
> -	}
> -	psr = ERR_PTR(-ENODEV);
> -
> -out:
> -	mutex_unlock(&drm_drv->psr_list_lock);
> -	return psr;
> -}
> -
>  static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
>  {
>  	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
> @@ -173,24 +156,6 @@ static void rockchip_drm_do_flush(struct psr_drv *psr)
>  	mutex_unlock(&psr->lock);
>  }
>  
> -/**
> - * rockchip_drm_psr_flush - flush a single pipe
> - * @crtc: CRTC of the pipe to flush
> - *
> - * Returns:
> - * 0 on success, -errno on fail
> - */
> -int rockchip_drm_psr_flush(struct drm_crtc *crtc)
> -{
> -	struct psr_drv *psr = find_psr_by_crtc(crtc);
> -	if (IS_ERR(psr))
> -		return PTR_ERR(psr);
> -
> -	rockchip_drm_do_flush(psr);
> -	return 0;
> -}
> -EXPORT_SYMBOL(rockchip_drm_psr_flush);
> -
>  /**
>   * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
>   * @dev: drm device
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> index 40e026c14168..860c62494496 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> @@ -16,7 +16,6 @@
>  #define __ROCKCHIP_DRM_PSR___
>  
>  void rockchip_drm_psr_flush_all(struct drm_device *dev);
> -int rockchip_drm_psr_flush(struct drm_crtc *crtc);
>  
>  int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder);
>  int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder);
Andrzej Hajda April 16, 2018, 9:57 a.m. UTC | #8
On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: Tomasz Figa <tfiga@chromium.org>
>
> It looks like the driver subsystem detaches devices from power domains
> at shutdown without consent of the drivers.

It looks bit strange. Could you elaborate more on it. Could you show the
code performing the detach?


Regards
Andrzej


>  This means that we might have
> our power domain turned off behind our back and the only way to avoid
> problems is to stop doing any hardware programming at some point before
> the power is cut. A reboot notifier, despite being a misnomer and
> handling shutdowns as well, is a good place to do it.
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index e7e16d92d5a1..1bf5cba9a64d 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -13,6 +13,7 @@
>   */
>  
>  #include <linux/input.h>
> +#include <linux/reboot.h>
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> @@ -33,6 +34,7 @@ struct psr_drv {
>  	struct delayed_work	flush_work;
>  	struct work_struct	disable_work;
>  
> +	struct notifier_block	reboot_nb;
>  	struct input_handler    input_handler;
>  
>  	int (*set)(struct drm_encoder *encoder, bool enable);
> @@ -309,6 +311,24 @@ static const struct input_device_id psr_ids[] = {
>  	{ },
>  };
>  
> +static int rockchip_drm_psr_reboot_notifier(struct notifier_block *nb,
> +					    unsigned long action, void *data)
> +{
> +	struct psr_drv *psr = container_of(nb, struct psr_drv, reboot_nb);
> +
> +	/*
> +	 * It looks like the driver subsystem detaches devices from power
> +	 * domains at shutdown without consent of the drivers. This means
> +	 * that we might have our power domain turned off behind our back
> +	 * and the only way to avoid problems is to stop doing any hardware
> +	 * programming after this point, which is achieved by the unbalanced
> +	 * call below.
> +	 */
> +	rockchip_drm_psr_inhibit_get(psr->encoder);
> +
> +	return 0;
> +}
> +
>  /**
>   * rockchip_drm_psr_register - register encoder to psr driver
>   * @encoder: encoder that obtain the PSR function
> @@ -361,6 +381,9 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  	if (error)
>  		goto err1;
>  
> +	psr->reboot_nb.notifier_call = rockchip_drm_psr_reboot_notifier;
> +	register_reboot_notifier(&psr->reboot_nb);
> +
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_add_tail(&psr->list, &drm_drv->psr_list);
>  	mutex_unlock(&drm_drv->psr_list_lock);
> @@ -403,6 +426,7 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>  			WARN_ON(psr->inhibit_count != 1);
>  
>  			list_del(&psr->list);
> +			unregister_reboot_notifier(&psr->reboot_nb);
>  			input_unregister_handler(&psr->input_handler);
>  			kfree(psr->input_handler.name);
>  			kfree(psr);
Tomasz Figa April 16, 2018, 1:12 p.m. UTC | #9
Hi Andrzej,

On Mon, Apr 16, 2018 at 6:57 PM Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> > From: Tomasz Figa <tfiga@chromium.org>
> >
> > It looks like the driver subsystem detaches devices from power domains
> > at shutdown without consent of the drivers.

> It looks bit strange. Could you elaborate more on it. Could you show the
> code performing the detach?

It not only looks strange, but it is strange. The code was present in 4.4:

https://elixir.bootlin.com/linux/v4.4.128/source/drivers/base/platform.c#L553

but was apparently removed in 4.5:

https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/base/platform.c?h=next-20180416&id=2d30bb0b3889adf09b342722b2ce596c0763bc93

So we might not need this patch anymore.

Best regards,
Tomasz
Dmitry Torokhov April 16, 2018, 5:41 p.m. UTC | #10
On Mon, Apr 16, 2018 at 09:19:21AM +0200, Andrzej Hajda wrote:
> 
> +CC: linux-input list and maintainer
> 
> 
> On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> > From: "Kristian H. Kristensen" <hoegsberg@google.com>
> >
> > To improve PSR exit latency, we speculatively start exiting when we
> > receive input events. Occasionally, this may lead to false positives,
> > but most of the time we get a head start on coming out of PSR. Depending
> > on how userspace takes to produce a new frame in response to the event,
> > this can completely hide the exit latency. In case of Chrome OS, we
> > typically get the input notifier 50ms or more before the dirty_fb
> > triggered exit.
> 
> As I see from the code below, you just need notification from input
> subsystem on user activity.
> Maybe there is some simpler notification mechanism for such things?
> If not, maybe such helper should be created in input subsystem, I
> suppose it could be reused in other places as well.

Creating an input_handler is how you get user activity. It allows you to
decide what devices you are interested in and you get events so you
decide what to do with them.

I am pretty sure using something like atomic notifier is not that much
simpler, but much less flexible.

I wonder though we we really need to open devices when we connect to
them, or if we can leave it as is and only receive events if someone
else opened the device and is consuming events.

Thanks.

> 
> Regards
> Andrzej
> 
> >
> > Signed-off-by: Kristian H. Kristensen <hoegsberg@google.com>
> > Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> > Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> > Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > ---
> >
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 134 ++++++++++++++++++++++++++++
> >  1 file changed, 134 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > index 9376f4396b6b..a107845ba97c 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > @@ -12,6 +12,8 @@
> >   * GNU General Public License for more details.
> >   */
> >  
> > +#include <linux/input.h>
> > +
> >  #include <drm/drmP.h>
> >  #include <drm/drm_crtc_helper.h>
> >  
> > @@ -35,6 +37,9 @@ struct psr_drv {
> >  	enum psr_state		state;
> >  
> >  	struct delayed_work	flush_work;
> > +	struct work_struct	disable_work;
> > +
> > +	struct input_handler    input_handler;
> >  
> >  	int (*set)(struct drm_encoder *encoder, bool enable);
> >  };
> > @@ -133,6 +138,18 @@ static void psr_flush_handler(struct work_struct *work)
> >  	mutex_unlock(&psr->lock);
> >  }
> >  
> > +static void psr_disable_handler(struct work_struct *work)
> > +{
> > +	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
> > +
> > +	/* If the state has changed since we initiated the flush, do nothing */
> > +	mutex_lock(&psr->lock);
> > +	if (psr->state == PSR_ENABLE)
> > +		psr_set_state_locked(psr, PSR_FLUSH);
> > +	mutex_unlock(&psr->lock);
> > +	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
> > +}
> > +
> >  /**
> >   * rockchip_drm_psr_activate - activate PSR on the given pipe
> >   * @encoder: encoder to obtain the PSR encoder
> > @@ -173,6 +190,7 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
> >  	psr->active = false;
> >  	mutex_unlock(&psr->lock);
> >  	cancel_delayed_work_sync(&psr->flush_work);
> > +	cancel_work_sync(&psr->disable_work);
> >  
> >  	return 0;
> >  }
> > @@ -226,6 +244,95 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
> >  }
> >  EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
> >  
> > +static void psr_input_event(struct input_handle *handle,
> > +			    unsigned int type, unsigned int code,
> > +			    int value)
> > +{
> > +	struct psr_drv *psr = handle->handler->private;
> > +
> > +	schedule_work(&psr->disable_work);
> > +}
> > +
> > +static int psr_input_connect(struct input_handler *handler,
> > +			     struct input_dev *dev,
> > +			     const struct input_device_id *id)
> > +{
> > +	struct input_handle *handle;
> > +	int error;
> > +
> > +	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
> > +	if (!handle)
> > +		return -ENOMEM;
> > +
> > +	handle->dev = dev;
> > +	handle->handler = handler;
> > +	handle->name = "rockchip-psr";
> > +
> > +	error = input_register_handle(handle);
> > +	if (error)
> > +		goto err2;
> > +
> > +	error = input_open_device(handle);
> > +	if (error)
> > +		goto err1;
> > +
> > +	return 0;
> > +
> > +err1:
> > +	input_unregister_handle(handle);
> > +err2:
> > +	kfree(handle);
> > +	return error;
> > +}
> > +
> > +static void psr_input_disconnect(struct input_handle *handle)
> > +{
> > +	input_close_device(handle);
> > +	input_unregister_handle(handle);
> > +	kfree(handle);
> > +}
> > +
> > +/* Same device ids as cpu-boost */
> > +static const struct input_device_id psr_ids[] = {
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> > +			 INPUT_DEVICE_ID_MATCH_ABSBIT,
> > +		.evbit = { BIT_MASK(EV_ABS) },
> > +		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
> > +			    BIT_MASK(ABS_MT_POSITION_X) |
> > +			    BIT_MASK(ABS_MT_POSITION_Y) },
> > +	}, /* multi-touch touchscreen */
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> > +		.evbit = { BIT_MASK(EV_ABS) },
> > +		.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) }
> > +
> > +	}, /* stylus or joystick device */
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> > +		.evbit = { BIT_MASK(EV_KEY) },
> > +		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
> > +	}, /* pointer (e.g. trackpad, mouse) */
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> > +		.evbit = { BIT_MASK(EV_KEY) },
> > +		.keybit = { [BIT_WORD(KEY_ESC)] = BIT_MASK(KEY_ESC) },
> > +	}, /* keyboard */
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> > +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> > +		.evbit = { BIT_MASK(EV_KEY) },
> > +		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
> > +	}, /* joysticks not caught by ABS_X above */
> > +	{
> > +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> > +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> > +		.evbit = { BIT_MASK(EV_KEY) },
> > +		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
> > +	}, /* gamepad */
> > +	{ },
> > +};
> > +
> >  /**
> >   * rockchip_drm_psr_register - register encoder to psr driver
> >   * @encoder: encoder that obtain the PSR function
> > @@ -239,6 +346,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
> >  {
> >  	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
> >  	struct psr_drv *psr;
> > +	int error;
> >  
> >  	if (!encoder || !psr_set)
> >  		return -EINVAL;
> > @@ -248,6 +356,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
> >  		return -ENOMEM;
> >  
> >  	INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
> > +	INIT_WORK(&psr->disable_work, psr_disable_handler);
> >  	mutex_init(&psr->lock);
> >  
> >  	psr->active = true;
> > @@ -255,11 +364,33 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
> >  	psr->encoder = encoder;
> >  	psr->set = psr_set;
> >  
> > +	psr->input_handler.event = psr_input_event;
> > +	psr->input_handler.connect = psr_input_connect;
> > +	psr->input_handler.disconnect = psr_input_disconnect;
> > +	psr->input_handler.name =
> > +		kasprintf(GFP_KERNEL, "rockchip-psr-%s", encoder->name);
> > +	if (!psr->input_handler.name) {
> > +		error = -ENOMEM;
> > +		goto err2;
> > +	}
> > +	psr->input_handler.id_table = psr_ids;
> > +	psr->input_handler.private = psr;
> > +
> > +	error = input_register_handler(&psr->input_handler);
> > +	if (error)
> > +		goto err1;
> > +
> >  	mutex_lock(&drm_drv->psr_list_lock);
> >  	list_add_tail(&psr->list, &drm_drv->psr_list);
> >  	mutex_unlock(&drm_drv->psr_list_lock);
> >  
> >  	return 0;
> > +
> > + err1:
> > +	kfree(psr->input_handler.name);
> > + err2:
> > +	kfree(psr);
> > +	return error;
> >  }
> >  EXPORT_SYMBOL(rockchip_drm_psr_register);
> >  
> > @@ -279,8 +410,11 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> >  	mutex_lock(&drm_drv->psr_list_lock);
> >  	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
> >  		if (psr->encoder == encoder) {
> > +			input_unregister_handler(&psr->input_handler);
> >  			cancel_delayed_work_sync(&psr->flush_work);
> > +			cancel_work_sync(&psr->disable_work);
> >  			list_del(&psr->list);
> > +			kfree(psr->input_handler.name);
> >  			kfree(psr);
> >  		}
> >  	}
> 
>
Ezequiel Garcia April 16, 2018, 7:42 p.m. UTC | #11
On Thu, 2018-04-05 at 11:49 +0200, Enric Balletbo i Serra wrote:
> From: "Kristian H. Kristensen" <hoegsberg@google.com>
> 
> To improve PSR exit latency, we speculatively start exiting when we
> receive input events. Occasionally, this may lead to false positives,
> but most of the time we get a head start on coming out of PSR.
> Depending
> on how userspace takes to produce a new frame in response to the
> event,
> this can completely hide the exit latency. In case of Chrome OS, we
> typically get the input notifier 50ms or more before the dirty_fb
> triggered exit.
> 

Why is this rockchip-specific? It sounds more like something
we'd want to integrate generically for drivers to leverage.

Regards,
Eze
Enric Balletbo Serra April 18, 2018, 9:18 a.m. UTC | #12
Hi Andrzej, Tomasz

2018-04-16 15:12 GMT+02:00 Tomasz Figa <tfiga@chromium.org>:
> Hi Andrzej,
>
> On Mon, Apr 16, 2018 at 6:57 PM Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
>> > From: Tomasz Figa <tfiga@chromium.org>
>> >
>> > It looks like the driver subsystem detaches devices from power domains
>> > at shutdown without consent of the drivers.
>
>> It looks bit strange. Could you elaborate more on it. Could you show the
>> code performing the detach?
>
> It not only looks strange, but it is strange. The code was present in 4.4:
>
> https://elixir.bootlin.com/linux/v4.4.128/source/drivers/base/platform.c#L553
>
> but was apparently removed in 4.5:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/base/platform.c?h=next-20180416&id=2d30bb0b3889adf09b342722b2ce596c0763bc93
>
> So we might not need this patch anymore.
>

Right, seems that we don't need this patch anymore, I'll do more few
tests and likely remove this patch from this series. Thanks for
catching this.

Best regards,
  Enric

> Best regards,
> Tomasz
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Andrzej Hajda April 20, 2018, 1:47 p.m. UTC | #13
Hi Enric,


On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
> From: "Kristian H. Kristensen" <hoegsberg@google.com>
>
> To improve PSR exit latency, we speculatively start exiting when we
> receive input events. Occasionally, this may lead to false positives,
> but most of the time we get a head start on coming out of PSR. Depending
> on how userspace takes to produce a new frame in response to the event,
> this can completely hide the exit latency. In case of Chrome OS, we
> typically get the input notifier 50ms or more before the dirty_fb
> triggered exit.

This patch is quite controversial and require more attention/discussion
and probably changes.
The rest of the patches is OK, and all have r-b/t-b tags.
If you prefer I can merge all other patches, if you rebase patches 25-30
on top of patch 23, or I can only merge patches 01-23.
What do you prefer?

Regards
Andrzej

>
> Signed-off-by: Kristian H. Kristensen <hoegsberg@google.com>
> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 134 ++++++++++++++++++++++++++++
>  1 file changed, 134 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> index 9376f4396b6b..a107845ba97c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -12,6 +12,8 @@
>   * GNU General Public License for more details.
>   */
>  
> +#include <linux/input.h>
> +
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
>  
> @@ -35,6 +37,9 @@ struct psr_drv {
>  	enum psr_state		state;
>  
>  	struct delayed_work	flush_work;
> +	struct work_struct	disable_work;
> +
> +	struct input_handler    input_handler;
>  
>  	int (*set)(struct drm_encoder *encoder, bool enable);
>  };
> @@ -133,6 +138,18 @@ static void psr_flush_handler(struct work_struct *work)
>  	mutex_unlock(&psr->lock);
>  }
>  
> +static void psr_disable_handler(struct work_struct *work)
> +{
> +	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
> +
> +	/* If the state has changed since we initiated the flush, do nothing */
> +	mutex_lock(&psr->lock);
> +	if (psr->state == PSR_ENABLE)
> +		psr_set_state_locked(psr, PSR_FLUSH);
> +	mutex_unlock(&psr->lock);
> +	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
> +}
> +
>  /**
>   * rockchip_drm_psr_activate - activate PSR on the given pipe
>   * @encoder: encoder to obtain the PSR encoder
> @@ -173,6 +190,7 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
>  	psr->active = false;
>  	mutex_unlock(&psr->lock);
>  	cancel_delayed_work_sync(&psr->flush_work);
> +	cancel_work_sync(&psr->disable_work);
>  
>  	return 0;
>  }
> @@ -226,6 +244,95 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
>  
> +static void psr_input_event(struct input_handle *handle,
> +			    unsigned int type, unsigned int code,
> +			    int value)
> +{
> +	struct psr_drv *psr = handle->handler->private;
> +
> +	schedule_work(&psr->disable_work);
> +}
> +
> +static int psr_input_connect(struct input_handler *handler,
> +			     struct input_dev *dev,
> +			     const struct input_device_id *id)
> +{
> +	struct input_handle *handle;
> +	int error;
> +
> +	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
> +	if (!handle)
> +		return -ENOMEM;
> +
> +	handle->dev = dev;
> +	handle->handler = handler;
> +	handle->name = "rockchip-psr";
> +
> +	error = input_register_handle(handle);
> +	if (error)
> +		goto err2;
> +
> +	error = input_open_device(handle);
> +	if (error)
> +		goto err1;
> +
> +	return 0;
> +
> +err1:
> +	input_unregister_handle(handle);
> +err2:
> +	kfree(handle);
> +	return error;
> +}
> +
> +static void psr_input_disconnect(struct input_handle *handle)
> +{
> +	input_close_device(handle);
> +	input_unregister_handle(handle);
> +	kfree(handle);
> +}
> +
> +/* Same device ids as cpu-boost */
> +static const struct input_device_id psr_ids[] = {
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +			 INPUT_DEVICE_ID_MATCH_ABSBIT,
> +		.evbit = { BIT_MASK(EV_ABS) },
> +		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
> +			    BIT_MASK(ABS_MT_POSITION_X) |
> +			    BIT_MASK(ABS_MT_POSITION_Y) },
> +	}, /* multi-touch touchscreen */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_ABS) },
> +		.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) }
> +
> +	}, /* stylus or joystick device */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
> +	}, /* pointer (e.g. trackpad, mouse) */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(KEY_ESC)] = BIT_MASK(KEY_ESC) },
> +	}, /* keyboard */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
> +	}, /* joysticks not caught by ABS_X above */
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
> +		.evbit = { BIT_MASK(EV_KEY) },
> +		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
> +	}, /* gamepad */
> +	{ },
> +};
> +
>  /**
>   * rockchip_drm_psr_register - register encoder to psr driver
>   * @encoder: encoder that obtain the PSR function
> @@ -239,6 +346,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  {
>  	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>  	struct psr_drv *psr;
> +	int error;
>  
>  	if (!encoder || !psr_set)
>  		return -EINVAL;
> @@ -248,6 +356,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  		return -ENOMEM;
>  
>  	INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
> +	INIT_WORK(&psr->disable_work, psr_disable_handler);
>  	mutex_init(&psr->lock);
>  
>  	psr->active = true;
> @@ -255,11 +364,33 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>  	psr->encoder = encoder;
>  	psr->set = psr_set;
>  
> +	psr->input_handler.event = psr_input_event;
> +	psr->input_handler.connect = psr_input_connect;
> +	psr->input_handler.disconnect = psr_input_disconnect;
> +	psr->input_handler.name =
> +		kasprintf(GFP_KERNEL, "rockchip-psr-%s", encoder->name);
> +	if (!psr->input_handler.name) {
> +		error = -ENOMEM;
> +		goto err2;
> +	}
> +	psr->input_handler.id_table = psr_ids;
> +	psr->input_handler.private = psr;
> +
> +	error = input_register_handler(&psr->input_handler);
> +	if (error)
> +		goto err1;
> +
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_add_tail(&psr->list, &drm_drv->psr_list);
>  	mutex_unlock(&drm_drv->psr_list_lock);
>  
>  	return 0;
> +
> + err1:
> +	kfree(psr->input_handler.name);
> + err2:
> +	kfree(psr);
> +	return error;
>  }
>  EXPORT_SYMBOL(rockchip_drm_psr_register);
>  
> @@ -279,8 +410,11 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>  	mutex_lock(&drm_drv->psr_list_lock);
>  	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
>  		if (psr->encoder == encoder) {
> +			input_unregister_handler(&psr->input_handler);
>  			cancel_delayed_work_sync(&psr->flush_work);
> +			cancel_work_sync(&psr->disable_work);
>  			list_del(&psr->list);
> +			kfree(psr->input_handler.name);
>  			kfree(psr);
>  		}
>  	}
Enric Balletbo i Serra April 20, 2018, 1:51 p.m. UTC | #14
Hi Andrzej,

On 20/04/18 15:47, Andrzej Hajda wrote:
> Hi Enric,
> 
> 
> On 05.04.2018 11:49, Enric Balletbo i Serra wrote:
>> From: "Kristian H. Kristensen" <hoegsberg@google.com>
>>
>> To improve PSR exit latency, we speculatively start exiting when we
>> receive input events. Occasionally, this may lead to false positives,
>> but most of the time we get a head start on coming out of PSR. Depending
>> on how userspace takes to produce a new frame in response to the event,
>> this can completely hide the exit latency. In case of Chrome OS, we
>> typically get the input notifier 50ms or more before the dirty_fb
>> triggered exit.
> 
> This patch is quite controversial and require more attention/discussion
> and probably changes.

Agree.

> The rest of the patches is OK, and all have r-b/t-b tags.
> If you prefer I can merge all other patches, if you rebase patches 25-30
> on top of patch 23, or I can only merge patches 01-23.
> What do you prefer?
> 

If the patches 25-30 are also fine let me rebase those, and lets start a
discussion regarding patches 23-25 on another patchset. I'll send another
version without 23-25.

Regards,
 Enric

> Regards
> Andrzej
> 
>>
>> Signed-off-by: Kristian H. Kristensen <hoegsberg@google.com>
>> Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
>> ---
>>
>>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 134 ++++++++++++++++++++++++++++
>>  1 file changed, 134 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> index 9376f4396b6b..a107845ba97c 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -12,6 +12,8 @@
>>   * GNU General Public License for more details.
>>   */
>>  
>> +#include <linux/input.h>
>> +
>>  #include <drm/drmP.h>
>>  #include <drm/drm_crtc_helper.h>
>>  
>> @@ -35,6 +37,9 @@ struct psr_drv {
>>  	enum psr_state		state;
>>  
>>  	struct delayed_work	flush_work;
>> +	struct work_struct	disable_work;
>> +
>> +	struct input_handler    input_handler;
>>  
>>  	int (*set)(struct drm_encoder *encoder, bool enable);
>>  };
>> @@ -133,6 +138,18 @@ static void psr_flush_handler(struct work_struct *work)
>>  	mutex_unlock(&psr->lock);
>>  }
>>  
>> +static void psr_disable_handler(struct work_struct *work)
>> +{
>> +	struct psr_drv *psr = container_of(work, struct psr_drv, disable_work);
>> +
>> +	/* If the state has changed since we initiated the flush, do nothing */
>> +	mutex_lock(&psr->lock);
>> +	if (psr->state == PSR_ENABLE)
>> +		psr_set_state_locked(psr, PSR_FLUSH);
>> +	mutex_unlock(&psr->lock);
>> +	mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
>> +}
>> +
>>  /**
>>   * rockchip_drm_psr_activate - activate PSR on the given pipe
>>   * @encoder: encoder to obtain the PSR encoder
>> @@ -173,6 +190,7 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
>>  	psr->active = false;
>>  	mutex_unlock(&psr->lock);
>>  	cancel_delayed_work_sync(&psr->flush_work);
>> +	cancel_work_sync(&psr->disable_work);
>>  
>>  	return 0;
>>  }
>> @@ -226,6 +244,95 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
>>  }
>>  EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
>>  
>> +static void psr_input_event(struct input_handle *handle,
>> +			    unsigned int type, unsigned int code,
>> +			    int value)
>> +{
>> +	struct psr_drv *psr = handle->handler->private;
>> +
>> +	schedule_work(&psr->disable_work);
>> +}
>> +
>> +static int psr_input_connect(struct input_handler *handler,
>> +			     struct input_dev *dev,
>> +			     const struct input_device_id *id)
>> +{
>> +	struct input_handle *handle;
>> +	int error;
>> +
>> +	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
>> +	if (!handle)
>> +		return -ENOMEM;
>> +
>> +	handle->dev = dev;
>> +	handle->handler = handler;
>> +	handle->name = "rockchip-psr";
>> +
>> +	error = input_register_handle(handle);
>> +	if (error)
>> +		goto err2;
>> +
>> +	error = input_open_device(handle);
>> +	if (error)
>> +		goto err1;
>> +
>> +	return 0;
>> +
>> +err1:
>> +	input_unregister_handle(handle);
>> +err2:
>> +	kfree(handle);
>> +	return error;
>> +}
>> +
>> +static void psr_input_disconnect(struct input_handle *handle)
>> +{
>> +	input_close_device(handle);
>> +	input_unregister_handle(handle);
>> +	kfree(handle);
>> +}
>> +
>> +/* Same device ids as cpu-boost */
>> +static const struct input_device_id psr_ids[] = {
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
>> +			 INPUT_DEVICE_ID_MATCH_ABSBIT,
>> +		.evbit = { BIT_MASK(EV_ABS) },
>> +		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
>> +			    BIT_MASK(ABS_MT_POSITION_X) |
>> +			    BIT_MASK(ABS_MT_POSITION_Y) },
>> +	}, /* multi-touch touchscreen */
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
>> +		.evbit = { BIT_MASK(EV_ABS) },
>> +		.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) }
>> +
>> +	}, /* stylus or joystick device */
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
>> +		.evbit = { BIT_MASK(EV_KEY) },
>> +		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
>> +	}, /* pointer (e.g. trackpad, mouse) */
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
>> +		.evbit = { BIT_MASK(EV_KEY) },
>> +		.keybit = { [BIT_WORD(KEY_ESC)] = BIT_MASK(KEY_ESC) },
>> +	}, /* keyboard */
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
>> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
>> +		.evbit = { BIT_MASK(EV_KEY) },
>> +		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
>> +	}, /* joysticks not caught by ABS_X above */
>> +	{
>> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
>> +				INPUT_DEVICE_ID_MATCH_KEYBIT,
>> +		.evbit = { BIT_MASK(EV_KEY) },
>> +		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
>> +	}, /* gamepad */
>> +	{ },
>> +};
>> +
>>  /**
>>   * rockchip_drm_psr_register - register encoder to psr driver
>>   * @encoder: encoder that obtain the PSR function
>> @@ -239,6 +346,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>  {
>>  	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>>  	struct psr_drv *psr;
>> +	int error;
>>  
>>  	if (!encoder || !psr_set)
>>  		return -EINVAL;
>> @@ -248,6 +356,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>  		return -ENOMEM;
>>  
>>  	INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
>> +	INIT_WORK(&psr->disable_work, psr_disable_handler);
>>  	mutex_init(&psr->lock);
>>  
>>  	psr->active = true;
>> @@ -255,11 +364,33 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>  	psr->encoder = encoder;
>>  	psr->set = psr_set;
>>  
>> +	psr->input_handler.event = psr_input_event;
>> +	psr->input_handler.connect = psr_input_connect;
>> +	psr->input_handler.disconnect = psr_input_disconnect;
>> +	psr->input_handler.name =
>> +		kasprintf(GFP_KERNEL, "rockchip-psr-%s", encoder->name);
>> +	if (!psr->input_handler.name) {
>> +		error = -ENOMEM;
>> +		goto err2;
>> +	}
>> +	psr->input_handler.id_table = psr_ids;
>> +	psr->input_handler.private = psr;
>> +
>> +	error = input_register_handler(&psr->input_handler);
>> +	if (error)
>> +		goto err1;
>> +
>>  	mutex_lock(&drm_drv->psr_list_lock);
>>  	list_add_tail(&psr->list, &drm_drv->psr_list);
>>  	mutex_unlock(&drm_drv->psr_list_lock);
>>  
>>  	return 0;
>> +
>> + err1:
>> +	kfree(psr->input_handler.name);
>> + err2:
>> +	kfree(psr);
>> +	return error;
>>  }
>>  EXPORT_SYMBOL(rockchip_drm_psr_register);
>>  
>> @@ -279,8 +410,11 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>>  	mutex_lock(&drm_drv->psr_list_lock);
>>  	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
>>  		if (psr->encoder == encoder) {
>> +			input_unregister_handler(&psr->input_handler);
>>  			cancel_delayed_work_sync(&psr->flush_work);
>> +			cancel_work_sync(&psr->disable_work);
>>  			list_del(&psr->list);
>> +			kfree(psr->input_handler.name);
>>  			kfree(psr);
>>  		}
>>  	}
> 
>