diff mbox series

[9/9] drm/i915: Add privacy-screen support

Message ID 20210906073519.4615-10-hdegoede@redhat.com (mailing list archive)
State New, archived
Headers show
Series drm: Add privacy-screen class and connector properties | expand

Commit Message

Hans de Goede Sept. 6, 2021, 7:35 a.m. UTC
Add support for eDP panels with a built-in privacy screen using the
new drm_privacy_screen class.

One thing which stands out here is the addition of these 2 lines to
intel_atomic_commit_tail:

	for_each_new_connector_in_state(&state->base, connector, ...
		drm_connector_update_privacy_screen(connector, state);

It may seem more logical to instead take care of updating the
privacy-screen state by marking the crtc as needing a modeset and then
do this in both the encoder update_pipe (for fast-sets) and enable
(for full modesets) callbacks. But ATM these callbacks only get passed
the new connector_state and these callbacks are all called after
drm_atomic_helper_swap_state() at which point there is no way to get
the old state from the new state.

Without access to the old state, we do not know if the sw_state of
the privacy-screen has changes so we would need to call
drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
since all current known privacy-screen providers use ACPI calls which
are somewhat expensive to make.

Also, as all providers use ACPI calls, rather then poking GPU registers,
there is no need to order this together with other encoder operations.
Since no GPU poking is involved having this as a separate step of the
commit process actually is the logical thing to do.

Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
 drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
 drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
 3 files changed, 27 insertions(+)

Comments

Lyude Paul Sept. 15, 2021, 9:11 p.m. UTC | #1
On Mon, 2021-09-06 at 09:35 +0200, Hans de Goede wrote:
> Add support for eDP panels with a built-in privacy screen using the
> new drm_privacy_screen class.
> 
> One thing which stands out here is the addition of these 2 lines to
> intel_atomic_commit_tail:
> 
>         for_each_new_connector_in_state(&state->base, connector, ...
>                 drm_connector_update_privacy_screen(connector, state);
> 
> It may seem more logical to instead take care of updating the
> privacy-screen state by marking the crtc as needing a modeset and then
> do this in both the encoder update_pipe (for fast-sets) and enable
> (for full modesets) callbacks. But ATM these callbacks only get passed
> the new connector_state and these callbacks are all called after
> drm_atomic_helper_swap_state() at which point there is no way to get
> the old state from the new state.

I was going to suggest that you workaround this simply by adding a variable
that corresponds to the most recently committed privacy screen state somewhere
in a driver private structure. But, then I realized that's basically the same
as what you're doing now except that your current solution stores said state
in a shared struct. So, I think you probably do have the right idea here as
long as we don't get any non-ACPI providers in the future. This also seems
like something that wouldn't be difficult to fixup down the line if that ends
up changing.

> 
> Without access to the old state, we do not know if the sw_state of
> the privacy-screen has changes so we would need to call
> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> since all current known privacy-screen providers use ACPI calls which
> are somewhat expensive to make.
> 
> Also, as all providers use ACPI calls, rather then poking GPU registers,
> there is no need to order this together with other encoder operations.
> Since no GPU poking is involved having this as a separate step of the
> commit process actually is the logical thing to do.
> 
> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
>  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
>  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
>  3 files changed, 27 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c
> b/drivers/gpu/drm/i915/display/intel_display.c
> index 5560d2f4c352..7285873d329a 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct
> intel_atomic_state *state)
>         struct drm_device *dev = state->base.dev;
>         struct drm_i915_private *dev_priv = to_i915(dev);
>         struct intel_crtc_state *new_crtc_state, *old_crtc_state;
> +       struct drm_connector_state *new_connector_state;
> +       struct drm_connector *connector;
>         struct intel_crtc *crtc;
>         u64 put_domains[I915_MAX_PIPES] = {};
>         intel_wakeref_t wakeref = 0;
> @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct
> intel_atomic_state *state)
>                         intel_color_load_luts(new_crtc_state);
>         }
>  
> +       for_each_new_connector_in_state(&state->base, connector,
> new_connector_state, i)
> +               drm_connector_update_privacy_screen(connector, &state-
> >base);
> +
>         /*
>          * Now that the vblank has passed, we can go ahead and program the
>          * optimal watermarks on platforms that need two-step watermark
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
> b/drivers/gpu/drm/i915/display/intel_dp.c
> index 7f8e8865048f..3aa2072cccf6 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> @@ -37,6 +37,7 @@
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_dp_helper.h>
>  #include <drm/drm_edid.h>
> +#include <drm/drm_privacy_screen_consumer.h>
>  #include <drm/drm_probe_helper.h>
>  
>  #include "g4x_dp.h"
> @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp
> *intel_dp,
>         struct drm_connector *connector = &intel_connector->base;
>         struct drm_display_mode *fixed_mode = NULL;
>         struct drm_display_mode *downclock_mode = NULL;
> +       struct drm_privacy_screen *privacy_screen;
>         bool has_dpcd;
>         enum pipe pipe = INVALID_PIPE;
>         struct edid *edid;
> @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp
> *intel_dp,
>                                 fixed_mode->hdisplay, fixed_mode->vdisplay);
>         }
>  
> +       privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
> +       if (!IS_ERR(privacy_screen)) {
> +               drm_connector_attach_privacy_screen_provider(connector,
> +                                                           
> privacy_screen);
> +       } else if (PTR_ERR(privacy_screen) != -ENODEV) {
> +               drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
> +       }
> +
>         return true;
>  
>  out_vdd_off:
> diff --git a/drivers/gpu/drm/i915/i915_pci.c
> b/drivers/gpu/drm/i915/i915_pci.c
> index 146f7e39182a..d6913f567a1c 100644
> --- a/drivers/gpu/drm/i915/i915_pci.c
> +++ b/drivers/gpu/drm/i915/i915_pci.c
> @@ -25,6 +25,7 @@
>  #include <linux/vga_switcheroo.h>
>  
>  #include <drm/drm_drv.h>
> +#include <drm/drm_privacy_screen_consumer.h>
>  #include <drm/i915_pciids.h>
>  
>  #include "i915_drv.h"
> @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const
> struct pci_device_id *ent)
>  {
>         struct intel_device_info *intel_info =
>                 (struct intel_device_info *) ent->driver_data;
> +       struct drm_privacy_screen *privacy_screen;
>         int err;
>  
>         if (intel_info->require_force_probe &&
> @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const
> struct pci_device_id *ent)
>         if (vga_switcheroo_client_probe_defer(pdev))
>                 return -EPROBE_DEFER;
>  
> +       /*
> +        * We do not handle -EPROBE_DEFER further into the probe process, so
> +        * check if we have a laptop-panel privacy-screen for which the
> driver
> +        * has not loaded yet here.
> +        */
> +       privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
> +       if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -
> EPROBE_DEFER)
> +               return -EPROBE_DEFER;
> +
>         err = i915_driver_probe(pdev, ent);
> +       drm_privacy_screen_put(privacy_screen);
>         if (err)
>                 return err;
>
Hans de Goede Sept. 16, 2021, 9:12 a.m. UTC | #2
Hi,

On 9/15/21 11:11 PM, Lyude Paul wrote:
> On Mon, 2021-09-06 at 09:35 +0200, Hans de Goede wrote:
>> Add support for eDP panels with a built-in privacy screen using the
>> new drm_privacy_screen class.
>>
>> One thing which stands out here is the addition of these 2 lines to
>> intel_atomic_commit_tail:
>>
>>         for_each_new_connector_in_state(&state->base, connector, ...
>>                 drm_connector_update_privacy_screen(connector, state);
>>
>> It may seem more logical to instead take care of updating the
>> privacy-screen state by marking the crtc as needing a modeset and then
>> do this in both the encoder update_pipe (for fast-sets) and enable
>> (for full modesets) callbacks. But ATM these callbacks only get passed
>> the new connector_state and these callbacks are all called after
>> drm_atomic_helper_swap_state() at which point there is no way to get
>> the old state from the new state.
> 
> I was going to suggest that you workaround this simply by adding a variable
> that corresponds to the most recently committed privacy screen state somewhere
> in a driver private structure. But, then I realized that's basically the same
> as what you're doing now except that your current solution stores said state
> in a shared struct. So, I think you probably do have the right idea here as
> long as we don't get any non-ACPI providers in the future. This also seems
> like something that wouldn't be difficult to fixup down the line if that ends
> up changing.

Ack, this is all kernel internal stuff so we can always rework it if necessary.

Regards,

Hans




> 
>>
>> Without access to the old state, we do not know if the sw_state of
>> the privacy-screen has changes so we would need to call
>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
>> since all current known privacy-screen providers use ACPI calls which
>> are somewhat expensive to make.
>>
>> Also, as all providers use ACPI calls, rather then poking GPU registers,
>> there is no need to order this together with other encoder operations.
>> Since no GPU poking is involved having this as a separate step of the
>> commit process actually is the logical thing to do.
>>
>> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>>  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
>>  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
>>  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
>>  3 files changed, 27 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/i915/display/intel_display.c
>> b/drivers/gpu/drm/i915/display/intel_display.c
>> index 5560d2f4c352..7285873d329a 100644
>> --- a/drivers/gpu/drm/i915/display/intel_display.c
>> +++ b/drivers/gpu/drm/i915/display/intel_display.c
>> @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct
>> intel_atomic_state *state)
>>         struct drm_device *dev = state->base.dev;
>>         struct drm_i915_private *dev_priv = to_i915(dev);
>>         struct intel_crtc_state *new_crtc_state, *old_crtc_state;
>> +       struct drm_connector_state *new_connector_state;
>> +       struct drm_connector *connector;
>>         struct intel_crtc *crtc;
>>         u64 put_domains[I915_MAX_PIPES] = {};
>>         intel_wakeref_t wakeref = 0;
>> @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct
>> intel_atomic_state *state)
>>                         intel_color_load_luts(new_crtc_state);
>>         }
>>  
>> +       for_each_new_connector_in_state(&state->base, connector,
>> new_connector_state, i)
>> +               drm_connector_update_privacy_screen(connector, &state-
>>> base);
>> +
>>         /*
>>          * Now that the vblank has passed, we can go ahead and program the
>>          * optimal watermarks on platforms that need two-step watermark
>> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
>> b/drivers/gpu/drm/i915/display/intel_dp.c
>> index 7f8e8865048f..3aa2072cccf6 100644
>> --- a/drivers/gpu/drm/i915/display/intel_dp.c
>> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
>> @@ -37,6 +37,7 @@
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_dp_helper.h>
>>  #include <drm/drm_edid.h>
>> +#include <drm/drm_privacy_screen_consumer.h>
>>  #include <drm/drm_probe_helper.h>
>>  
>>  #include "g4x_dp.h"
>> @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp
>> *intel_dp,
>>         struct drm_connector *connector = &intel_connector->base;
>>         struct drm_display_mode *fixed_mode = NULL;
>>         struct drm_display_mode *downclock_mode = NULL;
>> +       struct drm_privacy_screen *privacy_screen;
>>         bool has_dpcd;
>>         enum pipe pipe = INVALID_PIPE;
>>         struct edid *edid;
>> @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp
>> *intel_dp,
>>                                 fixed_mode->hdisplay, fixed_mode->vdisplay);
>>         }
>>  
>> +       privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
>> +       if (!IS_ERR(privacy_screen)) {
>> +               drm_connector_attach_privacy_screen_provider(connector,
>> +                                                           
>> privacy_screen);
>> +       } else if (PTR_ERR(privacy_screen) != -ENODEV) {
>> +               drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
>> +       }
>> +
>>         return true;
>>  
>>  out_vdd_off:
>> diff --git a/drivers/gpu/drm/i915/i915_pci.c
>> b/drivers/gpu/drm/i915/i915_pci.c
>> index 146f7e39182a..d6913f567a1c 100644
>> --- a/drivers/gpu/drm/i915/i915_pci.c
>> +++ b/drivers/gpu/drm/i915/i915_pci.c
>> @@ -25,6 +25,7 @@
>>  #include <linux/vga_switcheroo.h>
>>  
>>  #include <drm/drm_drv.h>
>> +#include <drm/drm_privacy_screen_consumer.h>
>>  #include <drm/i915_pciids.h>
>>  
>>  #include "i915_drv.h"
>> @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const
>> struct pci_device_id *ent)
>>  {
>>         struct intel_device_info *intel_info =
>>                 (struct intel_device_info *) ent->driver_data;
>> +       struct drm_privacy_screen *privacy_screen;
>>         int err;
>>  
>>         if (intel_info->require_force_probe &&
>> @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const
>> struct pci_device_id *ent)
>>         if (vga_switcheroo_client_probe_defer(pdev))
>>                 return -EPROBE_DEFER;
>>  
>> +       /*
>> +        * We do not handle -EPROBE_DEFER further into the probe process, so
>> +        * check if we have a laptop-panel privacy-screen for which the
>> driver
>> +        * has not loaded yet here.
>> +        */
>> +       privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
>> +       if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -
>> EPROBE_DEFER)
>> +               return -EPROBE_DEFER;
>> +
>>         err = i915_driver_probe(pdev, ent);
>> +       drm_privacy_screen_put(privacy_screen);
>>         if (err)
>>                 return err;
>>  
>
Jani Nikula Sept. 16, 2021, 9:40 a.m. UTC | #3
Cc: Ville for input here, see question inline.

On Mon, 06 Sep 2021, Hans de Goede <hdegoede@redhat.com> wrote:
> Add support for eDP panels with a built-in privacy screen using the
> new drm_privacy_screen class.
>
> One thing which stands out here is the addition of these 2 lines to
> intel_atomic_commit_tail:
>
> 	for_each_new_connector_in_state(&state->base, connector, ...
> 		drm_connector_update_privacy_screen(connector, state);
>
> It may seem more logical to instead take care of updating the
> privacy-screen state by marking the crtc as needing a modeset and then
> do this in both the encoder update_pipe (for fast-sets) and enable
> (for full modesets) callbacks. But ATM these callbacks only get passed
> the new connector_state and these callbacks are all called after
> drm_atomic_helper_swap_state() at which point there is no way to get
> the old state from the new state.
>
> Without access to the old state, we do not know if the sw_state of
> the privacy-screen has changes so we would need to call
> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> since all current known privacy-screen providers use ACPI calls which
> are somewhat expensive to make.
>
> Also, as all providers use ACPI calls, rather then poking GPU registers,
> there is no need to order this together with other encoder operations.
> Since no GPU poking is involved having this as a separate step of the
> commit process actually is the logical thing to do.
>
> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
>  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
>  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
>  3 files changed, 27 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> index 5560d2f4c352..7285873d329a 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>  	struct drm_device *dev = state->base.dev;
>  	struct drm_i915_private *dev_priv = to_i915(dev);
>  	struct intel_crtc_state *new_crtc_state, *old_crtc_state;
> +	struct drm_connector_state *new_connector_state;
> +	struct drm_connector *connector;
>  	struct intel_crtc *crtc;
>  	u64 put_domains[I915_MAX_PIPES] = {};
>  	intel_wakeref_t wakeref = 0;
> @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>  			intel_color_load_luts(new_crtc_state);
>  	}
>  
> +	for_each_new_connector_in_state(&state->base, connector, new_connector_state, i)
> +		drm_connector_update_privacy_screen(connector, &state->base);
> +
>  	/*
>  	 * Now that the vblank has passed, we can go ahead and program the
>  	 * optimal watermarks on platforms that need two-step watermark
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
> index 7f8e8865048f..3aa2072cccf6 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> @@ -37,6 +37,7 @@
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_dp_helper.h>
>  #include <drm/drm_edid.h>
> +#include <drm/drm_privacy_screen_consumer.h>
>  #include <drm/drm_probe_helper.h>
>  
>  #include "g4x_dp.h"
> @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>  	struct drm_connector *connector = &intel_connector->base;
>  	struct drm_display_mode *fixed_mode = NULL;
>  	struct drm_display_mode *downclock_mode = NULL;
> +	struct drm_privacy_screen *privacy_screen;
>  	bool has_dpcd;
>  	enum pipe pipe = INVALID_PIPE;
>  	struct edid *edid;
> @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>  				fixed_mode->hdisplay, fixed_mode->vdisplay);
>  	}
>  
> +	privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
> +	if (!IS_ERR(privacy_screen)) {
> +		drm_connector_attach_privacy_screen_provider(connector,
> +							     privacy_screen);
> +	} else if (PTR_ERR(privacy_screen) != -ENODEV) {
> +		drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
> +	}
> +
>  	return true;
>  
>  out_vdd_off:
> diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
> index 146f7e39182a..d6913f567a1c 100644
> --- a/drivers/gpu/drm/i915/i915_pci.c
> +++ b/drivers/gpu/drm/i915/i915_pci.c
> @@ -25,6 +25,7 @@
>  #include <linux/vga_switcheroo.h>
>  
>  #include <drm/drm_drv.h>
> +#include <drm/drm_privacy_screen_consumer.h>
>  #include <drm/i915_pciids.h>
>  
>  #include "i915_drv.h"
> @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  {
>  	struct intel_device_info *intel_info =
>  		(struct intel_device_info *) ent->driver_data;
> +	struct drm_privacy_screen *privacy_screen;
>  	int err;
>  
>  	if (intel_info->require_force_probe &&
> @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	if (vga_switcheroo_client_probe_defer(pdev))
>  		return -EPROBE_DEFER;
>  
> +	/*
> +	 * We do not handle -EPROBE_DEFER further into the probe process, so
> +	 * check if we have a laptop-panel privacy-screen for which the driver
> +	 * has not loaded yet here.
> +	 */
> +	privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
> +	if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -EPROBE_DEFER)
> +		return -EPROBE_DEFER;
> +
>  	err = i915_driver_probe(pdev, ent);
> +	drm_privacy_screen_put(privacy_screen);
>  	if (err)
>  		return err;

Ideally, neither i915_pci_probe() nor i915_driver_probe() should assume
we have display. We might not. We should not wait if we are never going
to initialize display.

Alas, we'll only know after i915_driver_probe() ->
i915_driver_mmio_probe() -> intel_device_info_runtime_init(), which
modifies ->pipe_mask, which is the single point of truth. See
HAS_DISPLAY().

We do have tests for failing probe at various points (see the
i915_inject_probe_failure() calls) to stress the cleanup paths in
CI. Part of the point was to prepare us for -EPROBE_DEFER returns.

Looks like the earliest/cleanest point for checking this is in
intel_modeset_init_noirq(), i.e. first display init call. But I admit it
gives me an uneasy feeling to return -EPROBE_DEFER at that stage. The
only -EPROBE_DEFER return we currently have is the vga switcheroo stuff
you see in the patch context, and most platforms never return that.

Ville, I'd like to get your thoughts on that.

Anyway, even if we decide not to, err, defer returning -EPROBE_DEFER, I
think we should abstract this better. For example, add a
intel_modeset_probe_defer() function in intel_display.c that checks
this, and call that as the first thing in i915_driver_probe(). Just to
keep the display specific code out of the high level functions, even if
that is functionally the same as what you're doing here.

BR,
Jani.
Hans de Goede Sept. 16, 2021, 10:32 a.m. UTC | #4
Hi,

On 9/16/21 11:40 AM, Jani Nikula wrote:
> 
> Cc: Ville for input here, see question inline.
> 
> On Mon, 06 Sep 2021, Hans de Goede <hdegoede@redhat.com> wrote:
>> Add support for eDP panels with a built-in privacy screen using the
>> new drm_privacy_screen class.
>>
>> One thing which stands out here is the addition of these 2 lines to
>> intel_atomic_commit_tail:
>>
>> 	for_each_new_connector_in_state(&state->base, connector, ...
>> 		drm_connector_update_privacy_screen(connector, state);
>>
>> It may seem more logical to instead take care of updating the
>> privacy-screen state by marking the crtc as needing a modeset and then
>> do this in both the encoder update_pipe (for fast-sets) and enable
>> (for full modesets) callbacks. But ATM these callbacks only get passed
>> the new connector_state and these callbacks are all called after
>> drm_atomic_helper_swap_state() at which point there is no way to get
>> the old state from the new state.
>>
>> Without access to the old state, we do not know if the sw_state of
>> the privacy-screen has changes so we would need to call
>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
>> since all current known privacy-screen providers use ACPI calls which
>> are somewhat expensive to make.
>>
>> Also, as all providers use ACPI calls, rather then poking GPU registers,
>> there is no need to order this together with other encoder operations.
>> Since no GPU poking is involved having this as a separate step of the
>> commit process actually is the logical thing to do.
>>
>> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>>  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
>>  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
>>  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
>>  3 files changed, 27 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
>> index 5560d2f4c352..7285873d329a 100644
>> --- a/drivers/gpu/drm/i915/display/intel_display.c
>> +++ b/drivers/gpu/drm/i915/display/intel_display.c
>> @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>>  	struct drm_device *dev = state->base.dev;
>>  	struct drm_i915_private *dev_priv = to_i915(dev);
>>  	struct intel_crtc_state *new_crtc_state, *old_crtc_state;
>> +	struct drm_connector_state *new_connector_state;
>> +	struct drm_connector *connector;
>>  	struct intel_crtc *crtc;
>>  	u64 put_domains[I915_MAX_PIPES] = {};
>>  	intel_wakeref_t wakeref = 0;
>> @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>>  			intel_color_load_luts(new_crtc_state);
>>  	}
>>  
>> +	for_each_new_connector_in_state(&state->base, connector, new_connector_state, i)
>> +		drm_connector_update_privacy_screen(connector, &state->base);
>> +
>>  	/*
>>  	 * Now that the vblank has passed, we can go ahead and program the
>>  	 * optimal watermarks on platforms that need two-step watermark
>> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
>> index 7f8e8865048f..3aa2072cccf6 100644
>> --- a/drivers/gpu/drm/i915/display/intel_dp.c
>> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
>> @@ -37,6 +37,7 @@
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_dp_helper.h>
>>  #include <drm/drm_edid.h>
>> +#include <drm/drm_privacy_screen_consumer.h>
>>  #include <drm/drm_probe_helper.h>
>>  
>>  #include "g4x_dp.h"
>> @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>>  	struct drm_connector *connector = &intel_connector->base;
>>  	struct drm_display_mode *fixed_mode = NULL;
>>  	struct drm_display_mode *downclock_mode = NULL;
>> +	struct drm_privacy_screen *privacy_screen;
>>  	bool has_dpcd;
>>  	enum pipe pipe = INVALID_PIPE;
>>  	struct edid *edid;
>> @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>>  				fixed_mode->hdisplay, fixed_mode->vdisplay);
>>  	}
>>  
>> +	privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
>> +	if (!IS_ERR(privacy_screen)) {
>> +		drm_connector_attach_privacy_screen_provider(connector,
>> +							     privacy_screen);
>> +	} else if (PTR_ERR(privacy_screen) != -ENODEV) {
>> +		drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
>> +	}
>> +
>>  	return true;
>>  
>>  out_vdd_off:
>> diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
>> index 146f7e39182a..d6913f567a1c 100644
>> --- a/drivers/gpu/drm/i915/i915_pci.c
>> +++ b/drivers/gpu/drm/i915/i915_pci.c
>> @@ -25,6 +25,7 @@
>>  #include <linux/vga_switcheroo.h>
>>  
>>  #include <drm/drm_drv.h>
>> +#include <drm/drm_privacy_screen_consumer.h>
>>  #include <drm/i915_pciids.h>
>>  
>>  #include "i915_drv.h"
>> @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>  {
>>  	struct intel_device_info *intel_info =
>>  		(struct intel_device_info *) ent->driver_data;
>> +	struct drm_privacy_screen *privacy_screen;
>>  	int err;
>>  
>>  	if (intel_info->require_force_probe &&
>> @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>  	if (vga_switcheroo_client_probe_defer(pdev))
>>  		return -EPROBE_DEFER;
>>  
>> +	/*
>> +	 * We do not handle -EPROBE_DEFER further into the probe process, so
>> +	 * check if we have a laptop-panel privacy-screen for which the driver
>> +	 * has not loaded yet here.
>> +	 */
>> +	privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
>> +	if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -EPROBE_DEFER)
>> +		return -EPROBE_DEFER;
>> +
>>  	err = i915_driver_probe(pdev, ent);
>> +	drm_privacy_screen_put(privacy_screen);
>>  	if (err)
>>  		return err;
> 
> Ideally, neither i915_pci_probe() nor i915_driver_probe() should assume
> we have display. We might not. We should not wait if we are never going
> to initialize display.

Good point.

> Alas, we'll only know after i915_driver_probe() ->
> i915_driver_mmio_probe() -> intel_device_info_runtime_init(), which
> modifies ->pipe_mask, which is the single point of truth. See
> HAS_DISPLAY().
> 
> We do have tests for failing probe at various points (see the
> i915_inject_probe_failure() calls) to stress the cleanup paths in
> CI. Part of the point was to prepare us for -EPROBE_DEFER returns.
> 
> Looks like the earliest/cleanest point for checking this is in
> intel_modeset_init_noirq(), i.e. first display init call. But I admit it
> gives me an uneasy feeling to return -EPROBE_DEFER at that stage.

Ack, this is why I added the get + put here. Theoretically I could also
just have put the return -EPROBE_DEFER inside intel_edp_init_connector()
(changing its return type) but I did not feel comfortable with doing
that...

I think that just leaving this functionally as is, with the refactor
which you suggest below is best for now. drm_privacy_screen_get() will
only return -EPROBE_DEFER if there is an entry in the lookup-table
which the drm_privacy class code keeps this lookup table matches
on the dev_name() of the GPU's PCI-device. So assuming the lookup
table contains the correct dev_name() then there should be no match
for any GPU-s without a display.

Note ATM this is not true since the lookup added for the thinkpad_acpi
providing privacy-screen support case specifies NULL as dev_name,
which gets interpreted as a wildcard, but I can easily replace that
with "0000:00:02.0" before pushing this out. Which at least avoids
delaying probing of any discrete Intel GPUs which I guess may not
have displays.

That does still leave the case of a hybrid GPU laptop where all
displays are connected to the discrete-GPU and the iGPU is only
left enabled for quick-sync functionality. But I'm not sure if
that case is even detected by HAS_DISPLAY(), since the hw then
still has display-pipes.

Worst case scenario here is that we delay i915 binding to the device
until thinkpad_acpi loads, which I think is not too bad.

Note a downside of replace the NULL devname in the lookup with
"0000:00:02.0" is that that will not work for hybrid-gfx laptops
with the panel connected to the discrete-GPU atm this is not supported
anyways since amdgpu and nouveau lack a patch similar to this one.

But the plan was for this to work automatically as soon as nouveau /
amdgpu get support assuming that e.g. only either i915 or nouveau would
see the LCD panel and thus would trigger the code in e.g.
intel_edp_init_connector().

But if you feel more comfortable about this if I change the dev_name
in the lookup to "0000:00:02.0" I can do that and we can cross the
hybrid-gfx case when we hit that. The whole need to tie a random
vendor ACPI interface for privacy-screen control together with the
drm-subsys is a bit messy anyways, so some of this we (I?) will need
to figure out as we go.

> The
> only -EPROBE_DEFER return we currently have is the vga switcheroo stuff
> you see in the patch context, and most platforms never return that.
> 
> Ville, I'd like to get your thoughts on that.
> 
> Anyway, even if we decide not to, err, defer returning -EPROBE_DEFER, I
> think we should abstract this better. For example, add a
> intel_modeset_probe_defer() function in intel_display.c that checks
> this, and call that as the first thing in i915_driver_probe(). Just to
> keep the display specific code out of the high level functions, even if
> that is functionally the same as what you're doing here.

I'm fine with refactoring this a bit and adding
an intel_modeset_probe_defer() helper for this, I assume I should also
move the vga_switcheroo_client_probe_defer(pdev) check there?

As you suggested yourself in your reply to the coverletter I will
push out the rest of the series to drm-misc-next while we figure this
out. Assuming Lyude is happy with the answers which I gave to her
remarks about some of the other patches.

Regards,

Hans
Ville Syrjala Sept. 16, 2021, 1:45 p.m. UTC | #5
On Mon, Sep 06, 2021 at 09:35:19AM +0200, Hans de Goede wrote:
> Add support for eDP panels with a built-in privacy screen using the
> new drm_privacy_screen class.
> 
> One thing which stands out here is the addition of these 2 lines to
> intel_atomic_commit_tail:
> 
> 	for_each_new_connector_in_state(&state->base, connector, ...
> 		drm_connector_update_privacy_screen(connector, state);
> 
> It may seem more logical to instead take care of updating the
> privacy-screen state by marking the crtc as needing a modeset and then
> do this in both the encoder update_pipe (for fast-sets) and enable
> (for full modesets) callbacks. But ATM these callbacks only get passed
> the new connector_state and these callbacks are all called after
> drm_atomic_helper_swap_state() at which point there is no way to get
> the old state from the new state.

Pretty sure the full atomic state is plumbed all the way
down these days.

> 
> Without access to the old state, we do not know if the sw_state of
> the privacy-screen has changes so we would need to call
> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> since all current known privacy-screen providers use ACPI calls which
> are somewhat expensive to make.

I doubt anyone is going to care about a bit of overhead for a modeset.
The usual rule is that a modeset doesn't skip anything. That way we
can be 100% sure we remeber to update everythinbg. For fastsets I guess
one could argue skipping it if not needed, but not sure even that is
warranted.

The current code you have in there is cettainly 110% dodgy. Since the
sw_state is stored in the connector state I presume it's at least
trying to be an atomic property, which means you shouldn't go poking
at it after the swap_state ever. swap_state just swaps the pointers
which is all that you need. So at least that part should get nuked.
The immutable hw_state I guess should get updated when we program the
actual hw.
Ville Syrjala Sept. 16, 2021, 2:04 p.m. UTC | #6
On Thu, Sep 16, 2021 at 12:40:11PM +0300, Jani Nikula wrote:
> 
> Cc: Ville for input here, see question inline.
> 
> On Mon, 06 Sep 2021, Hans de Goede <hdegoede@redhat.com> wrote:
> > Add support for eDP panels with a built-in privacy screen using the
> > new drm_privacy_screen class.
> >
> > One thing which stands out here is the addition of these 2 lines to
> > intel_atomic_commit_tail:
> >
> > 	for_each_new_connector_in_state(&state->base, connector, ...
> > 		drm_connector_update_privacy_screen(connector, state);
> >
> > It may seem more logical to instead take care of updating the
> > privacy-screen state by marking the crtc as needing a modeset and then
> > do this in both the encoder update_pipe (for fast-sets) and enable
> > (for full modesets) callbacks. But ATM these callbacks only get passed
> > the new connector_state and these callbacks are all called after
> > drm_atomic_helper_swap_state() at which point there is no way to get
> > the old state from the new state.
> >
> > Without access to the old state, we do not know if the sw_state of
> > the privacy-screen has changes so we would need to call
> > drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> > since all current known privacy-screen providers use ACPI calls which
> > are somewhat expensive to make.
> >
> > Also, as all providers use ACPI calls, rather then poking GPU registers,
> > there is no need to order this together with other encoder operations.
> > Since no GPU poking is involved having this as a separate step of the
> > commit process actually is the logical thing to do.
> >
> > Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
> > Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> > ---
> >  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
> >  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
> >  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
> >  3 files changed, 27 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> > index 5560d2f4c352..7285873d329a 100644
> > --- a/drivers/gpu/drm/i915/display/intel_display.c
> > +++ b/drivers/gpu/drm/i915/display/intel_display.c
> > @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
> >  	struct drm_device *dev = state->base.dev;
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> >  	struct intel_crtc_state *new_crtc_state, *old_crtc_state;
> > +	struct drm_connector_state *new_connector_state;
> > +	struct drm_connector *connector;
> >  	struct intel_crtc *crtc;
> >  	u64 put_domains[I915_MAX_PIPES] = {};
> >  	intel_wakeref_t wakeref = 0;
> > @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
> >  			intel_color_load_luts(new_crtc_state);
> >  	}
> >  
> > +	for_each_new_connector_in_state(&state->base, connector, new_connector_state, i)
> > +		drm_connector_update_privacy_screen(connector, &state->base);
> > +
> >  	/*
> >  	 * Now that the vblank has passed, we can go ahead and program the
> >  	 * optimal watermarks on platforms that need two-step watermark
> > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
> > index 7f8e8865048f..3aa2072cccf6 100644
> > --- a/drivers/gpu/drm/i915/display/intel_dp.c
> > +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> > @@ -37,6 +37,7 @@
> >  #include <drm/drm_crtc.h>
> >  #include <drm/drm_dp_helper.h>
> >  #include <drm/drm_edid.h>
> > +#include <drm/drm_privacy_screen_consumer.h>
> >  #include <drm/drm_probe_helper.h>
> >  
> >  #include "g4x_dp.h"
> > @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
> >  	struct drm_connector *connector = &intel_connector->base;
> >  	struct drm_display_mode *fixed_mode = NULL;
> >  	struct drm_display_mode *downclock_mode = NULL;
> > +	struct drm_privacy_screen *privacy_screen;
> >  	bool has_dpcd;
> >  	enum pipe pipe = INVALID_PIPE;
> >  	struct edid *edid;
> > @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
> >  				fixed_mode->hdisplay, fixed_mode->vdisplay);
> >  	}
> >  
> > +	privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
> > +	if (!IS_ERR(privacy_screen)) {
> > +		drm_connector_attach_privacy_screen_provider(connector,
> > +							     privacy_screen);
> > +	} else if (PTR_ERR(privacy_screen) != -ENODEV) {
> > +		drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
> > +	}
> > +
> >  	return true;
> >  
> >  out_vdd_off:
> > diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
> > index 146f7e39182a..d6913f567a1c 100644
> > --- a/drivers/gpu/drm/i915/i915_pci.c
> > +++ b/drivers/gpu/drm/i915/i915_pci.c
> > @@ -25,6 +25,7 @@
> >  #include <linux/vga_switcheroo.h>
> >  
> >  #include <drm/drm_drv.h>
> > +#include <drm/drm_privacy_screen_consumer.h>
> >  #include <drm/i915_pciids.h>
> >  
> >  #include "i915_drv.h"
> > @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >  {
> >  	struct intel_device_info *intel_info =
> >  		(struct intel_device_info *) ent->driver_data;
> > +	struct drm_privacy_screen *privacy_screen;
> >  	int err;
> >  
> >  	if (intel_info->require_force_probe &&
> > @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >  	if (vga_switcheroo_client_probe_defer(pdev))
> >  		return -EPROBE_DEFER;
> >  
> > +	/*
> > +	 * We do not handle -EPROBE_DEFER further into the probe process, so
> > +	 * check if we have a laptop-panel privacy-screen for which the driver
> > +	 * has not loaded yet here.
> > +	 */
> > +	privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
> > +	if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -EPROBE_DEFER)
> > +		return -EPROBE_DEFER;
> > +
> >  	err = i915_driver_probe(pdev, ent);
> > +	drm_privacy_screen_put(privacy_screen);
> >  	if (err)
> >  		return err;
> 
> Ideally, neither i915_pci_probe() nor i915_driver_probe() should assume
> we have display. We might not. We should not wait if we are never going
> to initialize display.
> 
> Alas, we'll only know after i915_driver_probe() ->
> i915_driver_mmio_probe() -> intel_device_info_runtime_init(), which
> modifies ->pipe_mask, which is the single point of truth. See
> HAS_DISPLAY().
> 
> We do have tests for failing probe at various points (see the
> i915_inject_probe_failure() calls) to stress the cleanup paths in
> CI. Part of the point was to prepare us for -EPROBE_DEFER returns.
> 
> Looks like the earliest/cleanest point for checking this is in
> intel_modeset_init_noirq(), i.e. first display init call. But I admit it
> gives me an uneasy feeling to return -EPROBE_DEFER at that stage. The
> only -EPROBE_DEFER return we currently have is the vga switcheroo stuff
> you see in the patch context, and most platforms never return that.
> 
> Ville, I'd like to get your thoughts on that.

I'm just scaread about everything to do with deferred probing.

For example, I don't even know what would happen if you have some kind
of mismatch betwen i915 and thinkpad_acpi y/m/n? Or you have one but not
the other in the initrd? Is the machine at least guaranteed to boot
properly and light up the display at some point?

But yeah, failing the probe at some stage when we've already clobbered
a bunch of things sounds like an "interesting" idea. I don't think we've
given the error paths any real though beyond the "ci+error injection
seems to not explode too badly" approach.

> Anyway, even if we decide not to, err, defer returning -EPROBE_DEFER, I
> think we should abstract this better. For example, add a
> intel_modeset_probe_defer() function in intel_display.c that checks
> this, and call that as the first thing in i915_driver_probe(). Just to
> keep the display specific code out of the high level functions, even if
> that is functionally the same as what you're doing here.

Yeah, I guess something like that could be the safest option
for the moment.
Hans de Goede Sept. 17, 2021, 2:23 p.m. UTC | #7
Hi,

On 9/16/21 4:04 PM, Ville Syrjälä wrote:
> On Thu, Sep 16, 2021 at 12:40:11PM +0300, Jani Nikula wrote:
>>
>> Cc: Ville for input here, see question inline.
>>
>> On Mon, 06 Sep 2021, Hans de Goede <hdegoede@redhat.com> wrote:
>>> Add support for eDP panels with a built-in privacy screen using the
>>> new drm_privacy_screen class.
>>>
>>> One thing which stands out here is the addition of these 2 lines to
>>> intel_atomic_commit_tail:
>>>
>>> 	for_each_new_connector_in_state(&state->base, connector, ...
>>> 		drm_connector_update_privacy_screen(connector, state);
>>>
>>> It may seem more logical to instead take care of updating the
>>> privacy-screen state by marking the crtc as needing a modeset and then
>>> do this in both the encoder update_pipe (for fast-sets) and enable
>>> (for full modesets) callbacks. But ATM these callbacks only get passed
>>> the new connector_state and these callbacks are all called after
>>> drm_atomic_helper_swap_state() at which point there is no way to get
>>> the old state from the new state.
>>>
>>> Without access to the old state, we do not know if the sw_state of
>>> the privacy-screen has changes so we would need to call
>>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
>>> since all current known privacy-screen providers use ACPI calls which
>>> are somewhat expensive to make.
>>>
>>> Also, as all providers use ACPI calls, rather then poking GPU registers,
>>> there is no need to order this together with other encoder operations.
>>> Since no GPU poking is involved having this as a separate step of the
>>> commit process actually is the logical thing to do.
>>>
>>> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>> ---
>>>  drivers/gpu/drm/i915/display/intel_display.c |  5 +++++
>>>  drivers/gpu/drm/i915/display/intel_dp.c      | 10 ++++++++++
>>>  drivers/gpu/drm/i915/i915_pci.c              | 12 ++++++++++++
>>>  3 files changed, 27 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
>>> index 5560d2f4c352..7285873d329a 100644
>>> --- a/drivers/gpu/drm/i915/display/intel_display.c
>>> +++ b/drivers/gpu/drm/i915/display/intel_display.c
>>> @@ -10140,6 +10140,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>>>  	struct drm_device *dev = state->base.dev;
>>>  	struct drm_i915_private *dev_priv = to_i915(dev);
>>>  	struct intel_crtc_state *new_crtc_state, *old_crtc_state;
>>> +	struct drm_connector_state *new_connector_state;
>>> +	struct drm_connector *connector;
>>>  	struct intel_crtc *crtc;
>>>  	u64 put_domains[I915_MAX_PIPES] = {};
>>>  	intel_wakeref_t wakeref = 0;
>>> @@ -10237,6 +10239,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>>>  			intel_color_load_luts(new_crtc_state);
>>>  	}
>>>  
>>> +	for_each_new_connector_in_state(&state->base, connector, new_connector_state, i)
>>> +		drm_connector_update_privacy_screen(connector, &state->base);
>>> +
>>>  	/*
>>>  	 * Now that the vblank has passed, we can go ahead and program the
>>>  	 * optimal watermarks on platforms that need two-step watermark
>>> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
>>> index 7f8e8865048f..3aa2072cccf6 100644
>>> --- a/drivers/gpu/drm/i915/display/intel_dp.c
>>> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
>>> @@ -37,6 +37,7 @@
>>>  #include <drm/drm_crtc.h>
>>>  #include <drm/drm_dp_helper.h>
>>>  #include <drm/drm_edid.h>
>>> +#include <drm/drm_privacy_screen_consumer.h>
>>>  #include <drm/drm_probe_helper.h>
>>>  
>>>  #include "g4x_dp.h"
>>> @@ -5217,6 +5218,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>>>  	struct drm_connector *connector = &intel_connector->base;
>>>  	struct drm_display_mode *fixed_mode = NULL;
>>>  	struct drm_display_mode *downclock_mode = NULL;
>>> +	struct drm_privacy_screen *privacy_screen;
>>>  	bool has_dpcd;
>>>  	enum pipe pipe = INVALID_PIPE;
>>>  	struct edid *edid;
>>> @@ -5308,6 +5310,14 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
>>>  				fixed_mode->hdisplay, fixed_mode->vdisplay);
>>>  	}
>>>  
>>> +	privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
>>> +	if (!IS_ERR(privacy_screen)) {
>>> +		drm_connector_attach_privacy_screen_provider(connector,
>>> +							     privacy_screen);
>>> +	} else if (PTR_ERR(privacy_screen) != -ENODEV) {
>>> +		drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
>>> +	}
>>> +
>>>  	return true;
>>>  
>>>  out_vdd_off:
>>> diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
>>> index 146f7e39182a..d6913f567a1c 100644
>>> --- a/drivers/gpu/drm/i915/i915_pci.c
>>> +++ b/drivers/gpu/drm/i915/i915_pci.c
>>> @@ -25,6 +25,7 @@
>>>  #include <linux/vga_switcheroo.h>
>>>  
>>>  #include <drm/drm_drv.h>
>>> +#include <drm/drm_privacy_screen_consumer.h>
>>>  #include <drm/i915_pciids.h>
>>>  
>>>  #include "i915_drv.h"
>>> @@ -1167,6 +1168,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>>  {
>>>  	struct intel_device_info *intel_info =
>>>  		(struct intel_device_info *) ent->driver_data;
>>> +	struct drm_privacy_screen *privacy_screen;
>>>  	int err;
>>>  
>>>  	if (intel_info->require_force_probe &&
>>> @@ -1195,7 +1197,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>>  	if (vga_switcheroo_client_probe_defer(pdev))
>>>  		return -EPROBE_DEFER;
>>>  
>>> +	/*
>>> +	 * We do not handle -EPROBE_DEFER further into the probe process, so
>>> +	 * check if we have a laptop-panel privacy-screen for which the driver
>>> +	 * has not loaded yet here.
>>> +	 */
>>> +	privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
>>> +	if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -EPROBE_DEFER)
>>> +		return -EPROBE_DEFER;
>>> +
>>>  	err = i915_driver_probe(pdev, ent);
>>> +	drm_privacy_screen_put(privacy_screen);
>>>  	if (err)
>>>  		return err;
>>
>> Ideally, neither i915_pci_probe() nor i915_driver_probe() should assume
>> we have display. We might not. We should not wait if we are never going
>> to initialize display.
>>
>> Alas, we'll only know after i915_driver_probe() ->
>> i915_driver_mmio_probe() -> intel_device_info_runtime_init(), which
>> modifies ->pipe_mask, which is the single point of truth. See
>> HAS_DISPLAY().
>>
>> We do have tests for failing probe at various points (see the
>> i915_inject_probe_failure() calls) to stress the cleanup paths in
>> CI. Part of the point was to prepare us for -EPROBE_DEFER returns.
>>
>> Looks like the earliest/cleanest point for checking this is in
>> intel_modeset_init_noirq(), i.e. first display init call. But I admit it
>> gives me an uneasy feeling to return -EPROBE_DEFER at that stage. The
>> only -EPROBE_DEFER return we currently have is the vga switcheroo stuff
>> you see in the patch context, and most platforms never return that.
>>
>> Ville, I'd like to get your thoughts on that.
> 
> I'm just scaread about everything to do with deferred probing.
> 
> For example, I don't even know what would happen if you have some kind
> of mismatch betwen i915 and thinkpad_acpi y/m/n? Or you have one but not
> the other in the initrd? Is the machine at least guaranteed to boot
> properly and light up the display at some point?

If i915 us builtin and thinkpad_acpi is m and the machine is a ThinkPad
with a privacy-screen then the  i915 driver's probe won't get past
the added check until the thinkpad_acpi driver has loaded.

Same for i915 being in the initrd and thinkpad_acpi not, then
the i915 driver's probe won't get past
the added check until we've pivoted to the real root and the
thinkpad_acpi module is loaded from the real-root.

Note that the boot will otherwise continue normally and we will
still have console output (and even e.g. a plymouth splash after
a timeout) on the efifb.


> 
> But yeah, failing the probe at some stage when we've already clobbered
> a bunch of things sounds like an "interesting" idea. I don't think we've
> given the error paths any real though beyond the "ci+error injection
> seems to not explode too badly" approach.
> 
>> Anyway, even if we decide not to, err, defer returning -EPROBE_DEFER, I
>> think we should abstract this better. For example, add a
>> intel_modeset_probe_defer() function in intel_display.c that checks
>> this, and call that as the first thing in i915_driver_probe(). Just to
>> keep the display specific code out of the high level functions, even if
>> that is functionally the same as what you're doing here.
> 
> Yeah, I guess something like that could be the safest option
> for the moment.

Ack I will go with that then.

Regards,

Hans
Hans de Goede Sept. 17, 2021, 2:37 p.m. UTC | #8
Hi,

On 9/16/21 3:45 PM, Ville Syrjälä wrote:
> On Mon, Sep 06, 2021 at 09:35:19AM +0200, Hans de Goede wrote:
>> Add support for eDP panels with a built-in privacy screen using the
>> new drm_privacy_screen class.
>>
>> One thing which stands out here is the addition of these 2 lines to
>> intel_atomic_commit_tail:
>>
>> 	for_each_new_connector_in_state(&state->base, connector, ...
>> 		drm_connector_update_privacy_screen(connector, state);
>>
>> It may seem more logical to instead take care of updating the
>> privacy-screen state by marking the crtc as needing a modeset and then
>> do this in both the encoder update_pipe (for fast-sets) and enable
>> (for full modesets) callbacks. But ATM these callbacks only get passed
>> the new connector_state and these callbacks are all called after
>> drm_atomic_helper_swap_state() at which point there is no way to get
>> the old state from the new state.
> 
> Pretty sure the full atomic state is plumbed all the way
> down these days.

Including the old state? AFAICT the old-state is being thrown away
from drm_atomic_helper_swap_state(), so if we do this in a different
place then we don't have access to the old-state.


> 
>>
>> Without access to the old state, we do not know if the sw_state of
>> the privacy-screen has changes so we would need to call
>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
>> since all current known privacy-screen providers use ACPI calls which
>> are somewhat expensive to make.
> 
> I doubt anyone is going to care about a bit of overhead for a modeset.

But this is not a modeset, this is more like changing the backlight brightness,
atm the code does not set the needs_modeset when only the privacy-screen
sw-state has changed.

Also in my experience the firmware (AML) code which we end up calling
for this is not the highest quality code, often it has interesting
issues / unhandled corner cases. So in my experience with ACPI we
really should try to avoid these calls unless we absolutely must make them,
but I guess not making unnecessary calls is something which could be handled
inside the actual privacy-screen driver instead.

> The usual rule is that a modeset doesn't skip anything. That way we
> can be 100% sure we remeber to update everythinbg. For fastsets I guess
> one could argue skipping it if not needed, but not sure even that is
> warranted.

Right, but again this is not a full modeset.

> 
> The current code you have in there is cettainly 110% dodgy. Since the
> sw_state is stored in the connector state I presume it's at least
> trying to be an atomic property, which means you shouldn't go poking
> at it after the swap_state ever.

It is not being poked, it is only being read, also this is happening
before swap_state.

Note I'm open for suggestions to handle this differently,
including changing the drm_connector_update_privacy_screen()
helper which currently relies on being passed the state before swap_state
is called:

void drm_connector_update_privacy_screen(struct drm_connector *connector,
					 struct drm_atomic_state *state)
{
	struct drm_connector_state *new_connector_state, *old_connector_state;
	int ret;

	if (!connector->privacy_screen)
		return;

	new_connector_state = drm_atomic_get_new_connector_state(state, connector);
	old_connector_state = drm_atomic_get_old_connector_state(state, connector);

	if (new_connector_state->privacy_screen_sw_state ==
	    old_connector_state->privacy_screen_sw_state)
		return;

	ret = drm_privacy_screen_set_sw_state(connector->privacy_screen,
				new_connector_state->privacy_screen_sw_state);
	if (ret) {
		drm_err(connector->dev, "Error updating privacy-screen sw_state\n");
		return;
	}

So if you have any suggestions how to do this differently, please let me know
and I will take a shot at implementing those suggestions.

Please keep in mind that the drm_privacy_screen_set_sw_state() call also
needs to happens when just the connector_state->privacy_screen_sw_state changes,
which is not a reason to do a full modeset (iow needs_modeset maybe 0 during
the commit)

Regards,

Hans
Ville Syrjala Sept. 17, 2021, 4:25 p.m. UTC | #9
On Fri, Sep 17, 2021 at 04:37:14PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 9/16/21 3:45 PM, Ville Syrjälä wrote:
> > On Mon, Sep 06, 2021 at 09:35:19AM +0200, Hans de Goede wrote:
> >> Add support for eDP panels with a built-in privacy screen using the
> >> new drm_privacy_screen class.
> >>
> >> One thing which stands out here is the addition of these 2 lines to
> >> intel_atomic_commit_tail:
> >>
> >> 	for_each_new_connector_in_state(&state->base, connector, ...
> >> 		drm_connector_update_privacy_screen(connector, state);
> >>
> >> It may seem more logical to instead take care of updating the
> >> privacy-screen state by marking the crtc as needing a modeset and then
> >> do this in both the encoder update_pipe (for fast-sets) and enable
> >> (for full modesets) callbacks. But ATM these callbacks only get passed
> >> the new connector_state and these callbacks are all called after
> >> drm_atomic_helper_swap_state() at which point there is no way to get
> >> the old state from the new state.
> > 
> > Pretty sure the full atomic state is plumbed all the way
> > down these days.
> 
> Including the old state? AFAICT the old-state is being thrown away
> from drm_atomic_helper_swap_state(),

No. That's just when those annoying foo_state->state pointers get
clobbered. We've been moving away from using those and just
plumbing the entire atomic state everywhere.

Nothing actually gets freed until the whole drm_atomic_state gets
nuked after the commit is done.

> so if we do this in a different
> place then we don't have access to the old-state.
> 
> 
> > 
> >>
> >> Without access to the old state, we do not know if the sw_state of
> >> the privacy-screen has changes so we would need to call
> >> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> >> since all current known privacy-screen providers use ACPI calls which
> >> are somewhat expensive to make.
> > 
> > I doubt anyone is going to care about a bit of overhead for a modeset.
> 
> But this is not a modeset, this is more like changing the backlight brightness,
> atm the code does not set the needs_modeset when only the privacy-screen
> sw-state has changed.
> 
> Also in my experience the firmware (AML) code which we end up calling
> for this is not the highest quality code, often it has interesting
> issues / unhandled corner cases. So in my experience with ACPI we
> really should try to avoid these calls unless we absolutely must make them,
> but I guess not making unnecessary calls is something which could be handled
> inside the actual privacy-screen driver instead.
> 
> > The usual rule is that a modeset doesn't skip anything. That way we
> > can be 100% sure we remeber to update everythinbg. For fastsets I guess
> > one could argue skipping it if not needed, but not sure even that is
> > warranted.
> 
> Right, but again this is not a full modeset.

In general fastset is is just an optimized modeset. Userspace asked
for a modeset, but we noticed it doesn't need it. I don't think
there is a particular expectation that it's super fast.

But if this is really annoyingly slow in some actual usecase then
one way to avoid that need to compare against the old state is just
introduce another foo_changed flag.

> 
> > 
> > The current code you have in there is cettainly 110% dodgy. Since the
> > sw_state is stored in the connector state I presume it's at least
> > trying to be an atomic property, which means you shouldn't go poking
> > at it after the swap_state ever.
> 
> It is not being poked, it is only being read, also this is happening
> before swap_state.
> 
> Note I'm open for suggestions to handle this differently,
> including changing the drm_connector_update_privacy_screen()
> helper which currently relies on being passed the state before swap_state
> is called:
> 
> void drm_connector_update_privacy_screen(struct drm_connector *connector,
> 					 struct drm_atomic_state *state)
> {
> 	struct drm_connector_state *new_connector_state, *old_connector_state;
> 	int ret;
> 
> 	if (!connector->privacy_screen)
> 		return;
> 
> 	new_connector_state = drm_atomic_get_new_connector_state(state, connector);
> 	old_connector_state = drm_atomic_get_old_connector_state(state, connector);
> 
> 	if (new_connector_state->privacy_screen_sw_state ==
> 	    old_connector_state->privacy_screen_sw_state)
> 		return;
> 
> 	ret = drm_privacy_screen_set_sw_state(connector->privacy_screen,
> 				new_connector_state->privacy_screen_sw_state);
> 	if (ret) {
> 		drm_err(connector->dev, "Error updating privacy-screen sw_state\n");
> 		return;
> 	}
> 
> So if you have any suggestions how to do this differently, please let me know
> and I will take a shot at implementing those suggestions.

You cut the code too soon. Just after this you call the other
update_privacy_screen() thing which does poke at
connector->state->stuff AFAICS.

> 
> Please keep in mind that the drm_privacy_screen_set_sw_state() call also
> needs to happens when just the connector_state->privacy_screen_sw_state changes,
> which is not a reason to do a full modeset (iow needs_modeset maybe 0 during
> the commit)
> 
> Regards,
> 
> Hans
> 
>
Hans de Goede Sept. 17, 2021, 4:42 p.m. UTC | #10
Hi,

On 9/17/21 6:25 PM, Ville Syrjälä wrote:
> On Fri, Sep 17, 2021 at 04:37:14PM +0200, Hans de Goede wrote:
>> Hi,
>>
>> On 9/16/21 3:45 PM, Ville Syrjälä wrote:
>>> On Mon, Sep 06, 2021 at 09:35:19AM +0200, Hans de Goede wrote:
>>>> Add support for eDP panels with a built-in privacy screen using the
>>>> new drm_privacy_screen class.
>>>>
>>>> One thing which stands out here is the addition of these 2 lines to
>>>> intel_atomic_commit_tail:
>>>>
>>>> 	for_each_new_connector_in_state(&state->base, connector, ...
>>>> 		drm_connector_update_privacy_screen(connector, state);
>>>>
>>>> It may seem more logical to instead take care of updating the
>>>> privacy-screen state by marking the crtc as needing a modeset and then
>>>> do this in both the encoder update_pipe (for fast-sets) and enable
>>>> (for full modesets) callbacks. But ATM these callbacks only get passed
>>>> the new connector_state and these callbacks are all called after
>>>> drm_atomic_helper_swap_state() at which point there is no way to get
>>>> the old state from the new state.
>>>
>>> Pretty sure the full atomic state is plumbed all the way
>>> down these days.
>>
>> Including the old state? AFAICT the old-state is being thrown away
>> from drm_atomic_helper_swap_state(),
> 
> No. That's just when those annoying foo_state->state pointers get
> clobbered. We've been moving away from using those and just
> plumbing the entire atomic state everywhere.
> 
> Nothing actually gets freed until the whole drm_atomic_state gets
> nuked after the commit is done.
> 
>> so if we do this in a different
>> place then we don't have access to the old-state.
>>
>>
>>>
>>>>
>>>> Without access to the old state, we do not know if the sw_state of
>>>> the privacy-screen has changes so we would need to call
>>>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
>>>> since all current known privacy-screen providers use ACPI calls which
>>>> are somewhat expensive to make.
>>>
>>> I doubt anyone is going to care about a bit of overhead for a modeset.
>>
>> But this is not a modeset, this is more like changing the backlight brightness,
>> atm the code does not set the needs_modeset when only the privacy-screen
>> sw-state has changed.
>>
>> Also in my experience the firmware (AML) code which we end up calling
>> for this is not the highest quality code, often it has interesting
>> issues / unhandled corner cases. So in my experience with ACPI we
>> really should try to avoid these calls unless we absolutely must make them,
>> but I guess not making unnecessary calls is something which could be handled
>> inside the actual privacy-screen driver instead.
>>
>>> The usual rule is that a modeset doesn't skip anything. That way we
>>> can be 100% sure we remeber to update everythinbg. For fastsets I guess
>>> one could argue skipping it if not needed, but not sure even that is
>>> warranted.
>>
>> Right, but again this is not a full modeset.
> 
> In general fastset is is just an optimized modeset. Userspace asked
> for a modeset, but we noticed it doesn't need it. I don't think
> there is a particular expectation that it's super fast.
> 
> But if this is really annoyingly slow in some actual usecase

Yeah these acpi-calls might take like a 100 ms easily, so
we really want to avoid it if it is not necessary.

> then
> one way to avoid that need to compare against the old state is just
> introduce another foo_changed flag.

Ok, so I have the feeling that you have an idea of how you think this
should be done / how this code should look instead of what I have
currently.

Can you perhaps provide a rough sketch / description of how you
think this should be done (instead of the current implementation) ?

Should I do the update from the the encoder update_pipe (for fast-sets)
and enable (for full modesets) callbacks instead as I mention in
the commit message ?

And since I still only want to do the call if there is an actual
change, where could I best do the old / new sw_state change cmp to
set the new foo_changed flag?




> 
>>
>>>
>>> The current code you have in there is cettainly 110% dodgy. Since the
>>> sw_state is stored in the connector state I presume it's at least
>>> trying to be an atomic property, which means you shouldn't go poking
>>> at it after the swap_state ever.
>>
>> It is not being poked, it is only being read, also this is happening
>> before swap_state.
>>
>> Note I'm open for suggestions to handle this differently,
>> including changing the drm_connector_update_privacy_screen()
>> helper which currently relies on being passed the state before swap_state
>> is called:
>>
>> void drm_connector_update_privacy_screen(struct drm_connector *connector,
>> 					 struct drm_atomic_state *state)
>> {
>> 	struct drm_connector_state *new_connector_state, *old_connector_state;
>> 	int ret;
>>
>> 	if (!connector->privacy_screen)
>> 		return;
>>
>> 	new_connector_state = drm_atomic_get_new_connector_state(state, connector);
>> 	old_connector_state = drm_atomic_get_old_connector_state(state, connector);
>>
>> 	if (new_connector_state->privacy_screen_sw_state ==
>> 	    old_connector_state->privacy_screen_sw_state)
>> 		return;
>>
>> 	ret = drm_privacy_screen_set_sw_state(connector->privacy_screen,
>> 				new_connector_state->privacy_screen_sw_state);
>> 	if (ret) {
>> 		drm_err(connector->dev, "Error updating privacy-screen sw_state\n");
>> 		return;
>> 	}
>>
>> So if you have any suggestions how to do this differently, please let me know
>> and I will take a shot at implementing those suggestions.
> 
> You cut the code too soon. Just after this you call the other
> update_privacy_screen() thing which does poke at
> connector->state->stuff AFAICS.

True, the idea here is to only update the hw_state, the returned sw_state
should always be the one which we just set. But I agree it would be better to
change the code here so that drm_connector_update_privacy_screen() only
updates privacy_screen_hw_state I will change the code to do this in the
next version of this patch-set.

Regards,

Hans
Ville Syrjala Sept. 17, 2021, 5:04 p.m. UTC | #11
On Fri, Sep 17, 2021 at 06:42:04PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 9/17/21 6:25 PM, Ville Syrjälä wrote:
> > On Fri, Sep 17, 2021 at 04:37:14PM +0200, Hans de Goede wrote:
> >> Hi,
> >>
> >> On 9/16/21 3:45 PM, Ville Syrjälä wrote:
> >>> On Mon, Sep 06, 2021 at 09:35:19AM +0200, Hans de Goede wrote:
> >>>> Add support for eDP panels with a built-in privacy screen using the
> >>>> new drm_privacy_screen class.
> >>>>
> >>>> One thing which stands out here is the addition of these 2 lines to
> >>>> intel_atomic_commit_tail:
> >>>>
> >>>> 	for_each_new_connector_in_state(&state->base, connector, ...
> >>>> 		drm_connector_update_privacy_screen(connector, state);
> >>>>
> >>>> It may seem more logical to instead take care of updating the
> >>>> privacy-screen state by marking the crtc as needing a modeset and then
> >>>> do this in both the encoder update_pipe (for fast-sets) and enable
> >>>> (for full modesets) callbacks. But ATM these callbacks only get passed
> >>>> the new connector_state and these callbacks are all called after
> >>>> drm_atomic_helper_swap_state() at which point there is no way to get
> >>>> the old state from the new state.
> >>>
> >>> Pretty sure the full atomic state is plumbed all the way
> >>> down these days.
> >>
> >> Including the old state? AFAICT the old-state is being thrown away
> >> from drm_atomic_helper_swap_state(),
> > 
> > No. That's just when those annoying foo_state->state pointers get
> > clobbered. We've been moving away from using those and just
> > plumbing the entire atomic state everywhere.
> > 
> > Nothing actually gets freed until the whole drm_atomic_state gets
> > nuked after the commit is done.
> > 
> >> so if we do this in a different
> >> place then we don't have access to the old-state.
> >>
> >>
> >>>
> >>>>
> >>>> Without access to the old state, we do not know if the sw_state of
> >>>> the privacy-screen has changes so we would need to call
> >>>> drm_privacy_screen_set_sw_state() unconditionally. This is undesirable
> >>>> since all current known privacy-screen providers use ACPI calls which
> >>>> are somewhat expensive to make.
> >>>
> >>> I doubt anyone is going to care about a bit of overhead for a modeset.
> >>
> >> But this is not a modeset, this is more like changing the backlight brightness,
> >> atm the code does not set the needs_modeset when only the privacy-screen
> >> sw-state has changed.
> >>
> >> Also in my experience the firmware (AML) code which we end up calling
> >> for this is not the highest quality code, often it has interesting
> >> issues / unhandled corner cases. So in my experience with ACPI we
> >> really should try to avoid these calls unless we absolutely must make them,
> >> but I guess not making unnecessary calls is something which could be handled
> >> inside the actual privacy-screen driver instead.
> >>
> >>> The usual rule is that a modeset doesn't skip anything. That way we
> >>> can be 100% sure we remeber to update everythinbg. For fastsets I guess
> >>> one could argue skipping it if not needed, but not sure even that is
> >>> warranted.
> >>
> >> Right, but again this is not a full modeset.
> > 
> > In general fastset is is just an optimized modeset. Userspace asked
> > for a modeset, but we noticed it doesn't need it. I don't think
> > there is a particular expectation that it's super fast.
> > 
> > But if this is really annoyingly slow in some actual usecase
> 
> Yeah these acpi-calls might take like a 100 ms easily, so
> we really want to avoid it if it is not necessary.
> 
> > then
> > one way to avoid that need to compare against the old state is just
> > introduce another foo_changed flag.
> 
> Ok, so I have the feeling that you have an idea of how you think this
> should be done / how this code should look instead of what I have
> currently.
> 
> Can you perhaps provide a rough sketch / description of how you
> think this should be done (instead of the current implementation) ?
> 
> Should I do the update from the the encoder update_pipe (for fast-sets)
> and enable (for full modesets) callbacks instead as I mention in
> the commit message ?
> 
> And since I still only want to do the call if there is an actual
> change, where could I best do the old / new sw_state change cmp to
> set the new foo_changed flag?
>

I guess it could be just something like this:

intel_digital_connector_duplicate_state()
{
	foo_changed = false;
}

intel_digital_connector_atomic_check()
{
	if (old_foo != new_foo) {
		mode_changed = true;
		foo_changed = true;
	}
}

update_pipe()
{
	if (foo_changed)
		update_foo();
}
Lyude Paul Sept. 20, 2021, 9:06 p.m. UTC | #12
On Thu, 2021-09-16 at 12:32 +0200, Hans de Goede wrote:
> 
> I'm fine with refactoring this a bit and adding
> an intel_modeset_probe_defer() helper for this, I assume I should also
> move the vga_switcheroo_client_probe_defer(pdev) check there?
> 
> As you suggested yourself in your reply to the coverletter I will
> push out the rest of the series to drm-misc-next while we figure this
> out. Assuming Lyude is happy with the answers which I gave to her
> remarks about some of the other patches.

I am, btw!

> 
> Regards,
> 
> Hans
>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 5560d2f4c352..7285873d329a 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -10140,6 +10140,8 @@  static void intel_atomic_commit_tail(struct intel_atomic_state *state)
 	struct drm_device *dev = state->base.dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct intel_crtc_state *new_crtc_state, *old_crtc_state;
+	struct drm_connector_state *new_connector_state;
+	struct drm_connector *connector;
 	struct intel_crtc *crtc;
 	u64 put_domains[I915_MAX_PIPES] = {};
 	intel_wakeref_t wakeref = 0;
@@ -10237,6 +10239,9 @@  static void intel_atomic_commit_tail(struct intel_atomic_state *state)
 			intel_color_load_luts(new_crtc_state);
 	}
 
+	for_each_new_connector_in_state(&state->base, connector, new_connector_state, i)
+		drm_connector_update_privacy_screen(connector, &state->base);
+
 	/*
 	 * Now that the vblank has passed, we can go ahead and program the
 	 * optimal watermarks on platforms that need two-step watermark
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 7f8e8865048f..3aa2072cccf6 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -37,6 +37,7 @@ 
 #include <drm/drm_crtc.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_privacy_screen_consumer.h>
 #include <drm/drm_probe_helper.h>
 
 #include "g4x_dp.h"
@@ -5217,6 +5218,7 @@  static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 	struct drm_connector *connector = &intel_connector->base;
 	struct drm_display_mode *fixed_mode = NULL;
 	struct drm_display_mode *downclock_mode = NULL;
+	struct drm_privacy_screen *privacy_screen;
 	bool has_dpcd;
 	enum pipe pipe = INVALID_PIPE;
 	struct edid *edid;
@@ -5308,6 +5310,14 @@  static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 				fixed_mode->hdisplay, fixed_mode->vdisplay);
 	}
 
+	privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
+	if (!IS_ERR(privacy_screen)) {
+		drm_connector_attach_privacy_screen_provider(connector,
+							     privacy_screen);
+	} else if (PTR_ERR(privacy_screen) != -ENODEV) {
+		drm_warn(&dev_priv->drm, "Error getting privacy-screen\n");
+	}
+
 	return true;
 
 out_vdd_off:
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index 146f7e39182a..d6913f567a1c 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -25,6 +25,7 @@ 
 #include <linux/vga_switcheroo.h>
 
 #include <drm/drm_drv.h>
+#include <drm/drm_privacy_screen_consumer.h>
 #include <drm/i915_pciids.h>
 
 #include "i915_drv.h"
@@ -1167,6 +1168,7 @@  static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	struct intel_device_info *intel_info =
 		(struct intel_device_info *) ent->driver_data;
+	struct drm_privacy_screen *privacy_screen;
 	int err;
 
 	if (intel_info->require_force_probe &&
@@ -1195,7 +1197,17 @@  static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (vga_switcheroo_client_probe_defer(pdev))
 		return -EPROBE_DEFER;
 
+	/*
+	 * We do not handle -EPROBE_DEFER further into the probe process, so
+	 * check if we have a laptop-panel privacy-screen for which the driver
+	 * has not loaded yet here.
+	 */
+	privacy_screen = drm_privacy_screen_get(&pdev->dev, NULL);
+	if (IS_ERR(privacy_screen) && PTR_ERR(privacy_screen) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
 	err = i915_driver_probe(pdev, ent);
+	drm_privacy_screen_put(privacy_screen);
 	if (err)
 		return err;