Re: [Intel-gfx] [PATCH v9 5/5] drm/i915: Enable support for integrated privacy screen
Hi, On 3/12/20 7:56 PM, Rajat Jain wrote: Add support for an ACPI based integrated privacy screen that is available on some systems. Signed-off-by: Rajat Jain So as discussed a while ago I'm working on adding support for the privacy-screen on Lenovo Thinkpads, introducing a small new subsystem / helper-class as intermediary for when the privacy-screen is controlled by e.g. some random drivers/platform/x86 driver rather then directly by the GPU driver. I'm almost ready to send out v1. I was working on hooking things up in the i915 code and I was wondering what you were doing when the property is actually changed and we need to commit the new privacy-screen state to the hardware. This made me look at this patch, some comments inline: --- v9: same as v8 v8: - separate the APCI privacy screen into a separate patch. - Don't destroy the property if there is no privacy screen (because drm core doesn't like destroying property in late_register()). - The setting change needs to be committed in ->update_pipe() for ddi.c as well as dp.c and both of them call intel_dp_add_properties() v7: Look for ACPI node in ->late_register() hook. Do the scan only once per drm_device (instead of 1 per drm_connector) v6: Addressed minor comments from Jani at https://lkml.org/lkml/2020/1/24/1143 - local variable renamed. - used drm_dbg_kms() - used acpi_device_handle() - Used opaque type acpi_handle instead of void* v5: same as v4 v4: Same as v3 v3: fold the code into existing acpi_device_id_update() function v2: formed by splitting the original patch into ACPI lookup, and privacy screen property. Also move it into i915 now that I found existing code in i915 that can be re-used. drivers/gpu/drm/i915/display/intel_atomic.c | 2 ++ drivers/gpu/drm/i915/display/intel_ddi.c| 1 + drivers/gpu/drm/i915/display/intel_dp.c | 34 - drivers/gpu/drm/i915/display/intel_dp.h | 5 +++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index d043057d2fa03..9898d8980e7ce 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -150,6 +150,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn, new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio || new_conn_state->base.content_type != old_conn_state->base.content_type || new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode || + new_conn_state->base.privacy_screen_status != + old_conn_state->base.privacy_screen_status || !blob_equal(new_conn_state->base.hdr_output_metadata, old_conn_state->base.hdr_output_metadata)) crtc_state->mode_changed = true; Right I was planning on doing this to. diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 73d0f4648c06a..69a5423216dc5 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3708,6 +3708,7 @@ static void intel_ddi_update_pipe(struct intel_encoder *encoder, if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) intel_ddi_update_pipe_dp(encoder, crtc_state, conn_state); + intel_dp_update_privacy_screen(encoder, crtc_state, conn_state); intel_hdcp_update_pipe(encoder, crtc_state, conn_state); } And this too. diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3ddc424b028c1..5f33ebb466135 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -62,6 +62,7 @@ #include "intel_lspcon.h" #include "intel_lvds.h" #include "intel_panel.h" +#include "intel_privacy_screen.h" #include "intel_psr.h" #include "intel_sideband.h" #include "intel_tc.h" @@ -5886,6 +5887,10 @@ intel_dp_connector_register(struct drm_connector *connector) dev_priv->acpi_scan_done = true; } + /* Check for integrated Privacy screen support */ + if (intel_privacy_screen_present(to_intel_connector(connector))) + drm_connector_attach_privacy_screen_property(connector); + DRM_DEBUG_KMS("registering %s bus for %s\n", intel_dp->aux.name, connector->kdev->kobj.name); @@ -6883,6 +6888,33 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect connector->state->scaling_mode = DRM_MODE_SCALE_ASPECT; } + + /* +* Created here, but depending on result of probing for privacy-screen +* in intel_dp_connector_register(), gets attached in that function. +* Need to create here because the drm core doesn't like creating +* properties
Re: [RFC][PATCH 0/9] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 7/3/20 2:58 PM, Daniel Vetter wrote: On Fri, Jul 3, 2020 at 12:55 PM Hans de Goede wrote: Hi, On 7/1/20 4:10 PM, Thomas Zimmermann wrote: Hi Daniel, thanks for reviewing most of the patchset. Am 30.06.20 um 11:06 schrieb Daniel Vetter: On Mon, Jun 29, 2020 at 11:39 AM Hans de Goede wrote: Hi, On 6/25/20 2:00 PM, Thomas Zimmermann wrote: This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simplekms, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simplekms is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simplekms can also serve as interim solution on graphics hardware without native DRM driver. Cool, thank you for doing this, this is a very welcome change, but ... (see below). So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simplekms. Patches 3 to 7 add the simplekms driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 6 and 7 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. Patches 8 and 9 add a hand-over mechanism. Simplekms acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simplekms before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. I tested simplekms with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. Ugh, Xorg not working OOTB is a bit of a showstopper, we cannot just go around and break userspace. OTOH this does seem like an userspace issue and not something which we can (or should try to) fix in the kernel. I guess the solution will have to be to have this default to N for now in Kconfig and clearly mention in the Kconfig help text that this needs a fixed Xorg modesetting driver before it can be enabled. Any chance you have time to work on fixing the Xorg modesetting driver so that this will just work with the standard Xorg autoconfiguration stuff? Hm, why do we even have both platform and pci drivers visible at the same time? I thought the point of this is that simplekms is built-in, then initrd loads the real drm driver, and by the time Xorg is running, simplekms should be long gone. Maybe a few more details of what's going wrong and why to help unconfuse me? I tested simplekms with PCI graphics cards. Xorg does it's own scanning of the busses. It supports a platform bus, where it finds the simple-framebuffer device that is driven by simplekms. Xorg also scans the PCI bus, where is finds the native PCI device; usually driven by the native driver. It's the same hardware, but on different busses. For each device, Xorg tries to configure a screen, the Xorg modeset driver tried to open the DRM device file and acquire DRM master status on it. This works for the first screen, but DRM master status can only be acquired once, so it fails for the second screen. Xorg then aborts and asks for manual configuration of the display device. This can be solved by setting the platform device's bus id in the xorg.conf device section. It just doesn't happen automatically. I found it hard to find a solution to this. Weston just opens a DRM device file and uses whatever it gets. Ideally, Xorg would do the same. That whole bus-scanning exercise gives it a wrong idea on which graphics devices are available. One idea for a fix is to compare the device I/O-memory ranges and filter out duplicates on the Xorg modeset driver. I don't know how reliable this works in practice or if their are false positives. I think that this should work nicely, although I wonder how Xorg will get the memory-range for the simplefb platform device, it looks like it will need to parse /dev/iomem for this, or we need to add a new sysfs attr to the simplefb device for this. Adding the new sysfs attr has the added bonus that we
Re: [RFC][PATCH 0/9] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 7/1/20 4:10 PM, Thomas Zimmermann wrote: Hi Daniel, thanks for reviewing most of the patchset. Am 30.06.20 um 11:06 schrieb Daniel Vetter: On Mon, Jun 29, 2020 at 11:39 AM Hans de Goede wrote: Hi, On 6/25/20 2:00 PM, Thomas Zimmermann wrote: This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simplekms, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simplekms is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simplekms can also serve as interim solution on graphics hardware without native DRM driver. Cool, thank you for doing this, this is a very welcome change, but ... (see below). So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simplekms. Patches 3 to 7 add the simplekms driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 6 and 7 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. Patches 8 and 9 add a hand-over mechanism. Simplekms acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simplekms before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. I tested simplekms with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. Ugh, Xorg not working OOTB is a bit of a showstopper, we cannot just go around and break userspace. OTOH this does seem like an userspace issue and not something which we can (or should try to) fix in the kernel. I guess the solution will have to be to have this default to N for now in Kconfig and clearly mention in the Kconfig help text that this needs a fixed Xorg modesetting driver before it can be enabled. Any chance you have time to work on fixing the Xorg modesetting driver so that this will just work with the standard Xorg autoconfiguration stuff? Hm, why do we even have both platform and pci drivers visible at the same time? I thought the point of this is that simplekms is built-in, then initrd loads the real drm driver, and by the time Xorg is running, simplekms should be long gone. Maybe a few more details of what's going wrong and why to help unconfuse me? I tested simplekms with PCI graphics cards. Xorg does it's own scanning of the busses. It supports a platform bus, where it finds the simple-framebuffer device that is driven by simplekms. Xorg also scans the PCI bus, where is finds the native PCI device; usually driven by the native driver. It's the same hardware, but on different busses. For each device, Xorg tries to configure a screen, the Xorg modeset driver tried to open the DRM device file and acquire DRM master status on it. This works for the first screen, but DRM master status can only be acquired once, so it fails for the second screen. Xorg then aborts and asks for manual configuration of the display device. This can be solved by setting the platform device's bus id in the xorg.conf device section. It just doesn't happen automatically. I found it hard to find a solution to this. Weston just opens a DRM device file and uses whatever it gets. Ideally, Xorg would do the same. That whole bus-scanning exercise gives it a wrong idea on which graphics devices are available. One idea for a fix is to compare the device I/O-memory ranges and filter out duplicates on the Xorg modeset driver. I don't know how reliable this works in practice or if their are false positives. I think that this should work nicely, although I wonder how Xorg will get the memory-range for the simplefb platform device, it looks like it will need to parse /dev/iomem for this, or we need to add a new sysfs attr to the simplefb device for this. Adding the new sysfs attr has the added bonus that we only enable the duplicate based resource check for simplefb devices. Hmm, I think we can actually fix
Re: [RFC][PATCH 0/9] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 7/1/20 3:48 PM, Thomas Zimmermann wrote: Hi Hans Am 29.06.20 um 11:38 schrieb Hans de Goede: Hi, On 6/25/20 2:00 PM, Thomas Zimmermann wrote: This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simplekms, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simplekms is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simplekms can also serve as interim solution on graphics hardware without native DRM driver. Cool, thank you for doing this, this is a very welcome change, but ... (see below). So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simplekms. Patches 3 to 7 add the simplekms driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 6 and 7 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. Patches 8 and 9 add a hand-over mechanism. Simplekms acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simplekms before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. I tested simplekms with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. Ugh, Xorg not working OOTB is a bit of a showstopper, we cannot just go around and break userspace. OTOH this does seem like an userspace issue and not something which we can (or should try to) fix in the kernel. Xorg is an important use case, but simplekms does not "break userspace." If you're not using simplekms, nothing changes; if simplekms is replaced by the native driver, nothing changes. Simplekms works with Xorg of the device is auto-configured. Xorg is not able to auto-configure simplekms devices ATM. As I already mentioned in my replay to Daniel: If there is no native driver, or the native driver fails to load (e.g. nvidia binary driver dkms build fails with a nwer kernel) then having simplekms enables changes the user, experience from Xorg on fbdev, slow but usable to search for a solution to no GUI. I agree that we cannot solve this on the kernel side, but it is a real problem which we need to keep in mind. I guess the solution will have to be to have this default to N for now in Kconfig and clearly mention in the Kconfig help text that this needs a fixed Xorg modesetting driver before it can be enabled. Sure, but simplekms is just a driver. Shouldn't it default to N anyway? I guess so. Any chance you have time to work on fixing the Xorg modesetting driver so that this will just work with the standard Xorg autoconfiguration stuff? I'll do if somehow possible. See my reply to Daniel for a description of the problem. Great. One cosmetical issue is that simplekms's device file is card0 and the native driver's device file is card1. After simplekms has been kicked out, only card1 is left. This does not seem to be a practical problem however. TODO/IDEAS: * provide deferred takeover I assume you mean akin to CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER ? I don't think you need to do anything for that, as long as you just leave the fb contents intact until requested to change it. Great. If it's that easy; even better. Right now with flickerfree boot we have fbcon on top of efifb and efifb does not do anything special wrt CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER ATM it does draw/restore the ACPI BGRT logo since since some firmwares don't draw that themselves, but that is not necessary in most cases and other then that all the deferred takeover magic is in the fbcon code, it does not bind to the fbdev (and thus does not draw to it) until the first time the kernel tries to output text to the console, together with the "quiet" kernel commandline argument that ensures that the fb is kept unmodified until e.g. a panic happens. With simplekms we
Re: [RFC][PATCH 0/9] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 6/30/20 11:06 AM, Daniel Vetter wrote: On Mon, Jun 29, 2020 at 11:39 AM Hans de Goede wrote: Hi, On 6/25/20 2:00 PM, Thomas Zimmermann wrote: This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simplekms, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simplekms is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simplekms can also serve as interim solution on graphics hardware without native DRM driver. Cool, thank you for doing this, this is a very welcome change, but ... (see below). So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simplekms. Patches 3 to 7 add the simplekms driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 6 and 7 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. Patches 8 and 9 add a hand-over mechanism. Simplekms acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simplekms before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. I tested simplekms with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. Ugh, Xorg not working OOTB is a bit of a showstopper, we cannot just go around and break userspace. OTOH this does seem like an userspace issue and not something which we can (or should try to) fix in the kernel. I guess the solution will have to be to have this default to N for now in Kconfig and clearly mention in the Kconfig help text that this needs a fixed Xorg modesetting driver before it can be enabled. Any chance you have time to work on fixing the Xorg modesetting driver so that this will just work with the standard Xorg autoconfiguration stuff? Hm, why do we even have both platform and pci drivers visible at the same time? I thought the point of this is that simplekms is built-in, then initrd loads the real drm driver, and by the time Xorg is running, simplekms should be long gone. Maybe a few more details of what's going wrong and why to help unconfuse me? I guess that Thomas deliberately disabled the normal kms driver to test this. Still we should make sure that this (Xorg on simpledrm) works, a user might see this because of running an older kernel with a newer GPU, or when using the nvidia binary and thus having nouveau disabled and then installing a newer kernel which breaks the dkms nvidia module building because of API changes. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC][PATCH 0/9] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 6/25/20 2:00 PM, Thomas Zimmermann wrote: This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simplekms, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simplekms is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simplekms can also serve as interim solution on graphics hardware without native DRM driver. Cool, thank you for doing this, this is a very welcome change, but ... (see below). So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simplekms. Patches 3 to 7 add the simplekms driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 6 and 7 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. Patches 8 and 9 add a hand-over mechanism. Simplekms acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simplekms before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. I tested simplekms with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. Ugh, Xorg not working OOTB is a bit of a showstopper, we cannot just go around and break userspace. OTOH this does seem like an userspace issue and not something which we can (or should try to) fix in the kernel. I guess the solution will have to be to have this default to N for now in Kconfig and clearly mention in the Kconfig help text that this needs a fixed Xorg modesetting driver before it can be enabled. Any chance you have time to work on fixing the Xorg modesetting driver so that this will just work with the standard Xorg autoconfiguration stuff? One cosmetical issue is that simplekms's device file is card0 and the native driver's device file is card1. After simplekms has been kicked out, only card1 is left. This does not seem to be a practical problem however. TODO/IDEAS: * provide deferred takeover I assume you mean akin to CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER ? I don't think you need to do anything for that, as long as you just leave the fb contents intact until requested to change it. Right now with flickerfree boot we have fbcon on top of efifb and efifb does not do anything special wrt CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER ATM it does draw/restore the ACPI BGRT logo since since some firmwares don't draw that themselves, but that is not necessary in most cases and other then that all the deferred takeover magic is in the fbcon code, it does not bind to the fbdev (and thus does not draw to it) until the first time the kernel tries to output text to the console, together with the "quiet" kernel commandline argument that ensures that the fb is kept unmodified until e.g. a panic happens. With simplekms we would replace "fbcon on top of efifb" with "fbcon on top of emulated-fbdev on top of simplekms" so as long as the emulated-fbdev and simplekms code defer from say clearing the screen to black, but keep it as is. Then the fb contents should be preserved until fbcon decides to takeover the fbdev and draw to it. * provide bootsplash DRM client Hmm, I guess this might be interesting for simple cases, but although I would love to kill plymouth (I've become one of the upstream maintainers for it) I'm afraid it is not that easy, it does a bunch of stuff which will be tricky to do in the kernel: 1) Ask the user for diskcrypt passwords: https://ic.pics.livejournal.com/hansdegoede/13347631/1496/1496_900.png 2) Show a nice splash + progressbar when installing updates in offline updates mode: https://ic.pics.livejournal.com/hansdegoede/13347631/899/899_900.png Still this would be nice for the non diskcrypt case I guess, then we could not use plymouth during normal boot and only use it for offline updates and it would also be nice for various embedded cases.
Re: [PATCH 1/2] drm: panel-orientation-quirks: Add quirk for Asus T101HA panel
Hi, On 5/31/20 7:25 PM, Emil Velikov wrote: On Sun, 31 May 2020 at 10:30, Hans de Goede wrote: Like the Asus T100HA the Asus T101HA also uses a panel which has been mounted 90 degrees rotated, add a quirk for this. Reading the commit message, made me wonder: If it's similar why it doesn't use the same quirk? Something like the following helps: ... 90 degrees rotated, albeit in the opposite direction. Add a quirk for this. With that the series is: Reviewed-by: Emil Velikov Thank you, I've ammended the commit msg with your suggestion and pushed the 2 patches to drm-misc-fixes. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 05/15] pwm: lpss: Use pwm_lpss_apply() when restoring state on resume
Before this commit a suspend + resume of the LPSS PWM controller would result in the controller being reset to its defaults of output-freq = clock/256, duty-cycle=100%, until someone changes to the output-freq and/or duty-cycle are made. This problem has been masked so far because the main consumer (the i915 driver) was always making duty-cycle changes on resume. With the conversion of the i915 driver to the atomic PWM API the driver now only disables/enables the PWM on suspend/resume leaving the output-freq and duty as is, triggering this problem. The LPSS PWM controller has a mechanism where the ctrl register value and the actual base-unit and on-time-div values used are latched. When software sets the SW_UPDATE bit then at the end of the current PWM cycle, the new values from the ctrl-register will be latched into the actual registers, and the SW_UPDATE bit will be cleared. The problem is that before this commit our suspend/resume handling consisted of simply saving the PWM ctrl register on suspend and restoring it on resume, without setting the PWM_SW_UPDATE bit. When the controller has lost its state over a suspend/resume and thus has been reset to the defaults, just restoring the register is not enough. We must also set the SW_UPDATE bit to tell the controller to latch the restored values into the actual registers. Fixing this problem is not as simple as just or-ing in the value which is being restored with SW_UPDATE. If the PWM was enabled before we must write the new settings + PWM_SW_UPDATE before setting PWM_ENABLE. We must also wait for PWM_SW_UPDATE to become 0 again and depending on the model we must do this either before or after the setting of PWM_ENABLE. All the necessary logic for doing this is already present inside pwm_lpss_apply(), so instead of duplicating this inside the resume handler, this commit makes the resume handler use pwm_lpss_apply() to restore the settings when necessary. This fixes the output-freq and duty-cycle being reset to their defaults on resume. Signed-off-by: Hans de Goede --- Changes in v3: - This replaces the "pwm: lpss: Set SW_UPDATE bit when enabling the PWM" patch from previous versions of this patch-set, which really was a hack working around the resume issue which this patch fixes properly. --- drivers/pwm/pwm-lpss.c | 62 -- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 80d0f9c64f9d..4f3d60ce9929 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -123,25 +123,31 @@ static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond) pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); } -static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) +static int __pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state, bool from_resume) { struct pwm_lpss_chip *lpwm = to_lpwm(chip); int ret; if (state->enabled) { if (!pwm_is_enabled(pwm)) { - pm_runtime_get_sync(chip->dev); + if (!from_resume) + pm_runtime_get_sync(chip->dev); + ret = pwm_lpss_is_updating(pwm); if (ret) { - pm_runtime_put(chip->dev); + if (!from_resume) + pm_runtime_put(chip->dev); + return ret; } pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false); ret = pwm_lpss_wait_for_update(pwm); if (ret) { - pm_runtime_put(chip->dev); + if (!from_resume) + pm_runtime_put(chip->dev); + return ret; } pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true); @@ -154,12 +160,20 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, } } else if (pwm_is_enabled(pwm)) { pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE); - pm_runtime_put(chip->dev); + + if (!from_resume) + pm_runtime_put(chip->dev); } return 0; } +static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + return __pwm_lpss_apply(chip, pwm, state, false); +} + static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *
[PATCH v3 06/15] pwm: crc: Fix period / duty_cycle times being off by a factor of 256
While looking into adding atomic-pwm support to the pwm-crc driver I noticed something odd, there is a PWM_BASE_CLK define of 6 MHz and there is a clock-divider which divides this with a value between 1-128, and there are 256 duty-cycle steps. The pwm-crc code before this commit assumed that a clock-divider setting of 1 means that the PWM output is running at 6 MHZ, if that is true, where do these 256 duty-cycle steps come from? This would require an internal frequency of 256 * 6 MHz = 1.5 GHz, that seems unlikely for a PMIC which is using a silicon process optimized for power-switching transistors. It is way more likely that there is an 8 bit counter for the duty cycle which acts as an extra fixed divider wrt the PWM output frequency. The main user of the pwm-crc driver is the i915 GPU driver which uses it for backlight control. Lets compare the PWM register values set by the video-BIOS (the GOP), assuming the extra fixed divider is present versus the PWM frequency specified in the Video-BIOS-Tables: Device: PWM Hz set by BIOS PWM Hz specified in VBT Asus T100TA 200 200 Asus T100HA 200 200 Lenovo Miix 2 8 23437 2 Toshiba WT8-A 23437 2 So as we can see if we assume the extra division by 256 then the register values set by the GOP are an exact match for the VBT values, where as otherwise the values would be of by a factor of 256. This commit fixes the period / duty_cycle calculations to take the extra division by 256 into account. Signed-off-by: Hans de Goede --- Changes in v3: - Use NSEC_PER_USEC instead of adding a new (non-sensical) NSEC_PER_MHZ define --- drivers/pwm/pwm-crc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 272eeb071147..c056eb9b858c 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -21,8 +21,8 @@ #define PWM_MAX_LEVEL 0xFF -#define PWM_BASE_CLK 600 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 21333/* 46.875KHz */ +#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ +#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ /** * struct crystalcove_pwm - Crystal Cove PWM controller @@ -72,7 +72,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); - clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC; + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_USEC); regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 01/15] ACPI / LPSS: Resume Cherry Trail PWM controller in no-irq phase
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets poked from the _PS0 method of the graphics-card device: Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ If (((Local0 & 0x03) == 0x03)) { PSAT &= 0xFFFC Local1 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ RSTA = Zero RSTF = Zero RSTA = One RSTF = One PWMB |= 0xC000 PWMC = PWMB /* \_SB_.PCI0.GFX0.PWMB */ } Where PSAT is the power-status register of the PWM controller, so if it is in D3 when the GFX0 device's PS0 method runs then it will turn it on and restore the PWM ctrl register value it saved from its PS3 handler. Note not only does it restore it, it ors it with 0xC000 turning it on at a time where we may not want it to get turned on at all. The pwm_get call which the i915 driver does to get a reference to the PWM controller, already adds a device-link making the GFX0 device a consumer of the PWM device. So it should already have been resumed when the above AML runs and the AML should thus not do its undesirable poking of the PWM controller register. But the PCI core powers on PCI devices in the no-irq resume phase and thus calls the troublesome PS0 method in the no-irq resume phase. Where as LPSS devices by default are resumed in the early resume phase. This commit sets the resume_from_noirq flag in the bsw_pwm_dev_desc struct, so that Cherry Trail PWM controllers will be resumed in the no-irq phase. Together with the device-link added by the pwm-get this ensures that the PWM controller will be on when the troublesome PS0 method runs, which stops it from poking the PWM controller. Signed-off-by: Hans de Goede --- drivers/acpi/acpi_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index c5a5a179f49d..446e666b3466 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -257,6 +257,7 @@ static const struct lpss_device_desc bsw_pwm_dev_desc = { .flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, .prv_offset = 0x800, .setup = bsw_pwm_setup, + .resume_from_noirq = true, }; static const struct lpss_device_desc byt_uart_dev_desc = { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 03/15] pwm: lpss: Fix off by one error in base_unit math in pwm_lpss_prepare()
According to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. So assuming e.g. a 16 bit counter this means that if base_unit is set to 1, after 65535 input clock-cycles the counter has been increased from 0 to 65535 and it will overflow on the next cycle, so it will overflow after every 65536 clock cycles and thus the calculations done in pwm_lpss_prepare() should use 65536 and not 65535. This commit fixes this. Note this also aligns the calculations in pwm_lpss_prepare() with those in pwm_lpss_get_state(). Note this effectively reverts commit 684309e5043e ("pwm: lpss: Avoid potential overflow of base_unit"). The next patch in this series really fixes the potential overflow of the base_unit value. Fixes: 684309e5043e ("pwm: lpss: Avoid potential overflow of base_unit") Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede --- Changes in v3: - Add Fixes tag - Add Reviewed-by: Andy Shevchenko tag --- drivers/pwm/pwm-lpss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 9d965ffe66d1..43b1fc634af1 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -93,7 +93,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, * The equation is: * base_unit = round(base_unit_range * freq / c) */ - base_unit_range = BIT(lpwm->info->base_unit_bits) - 1; + base_unit_range = BIT(lpwm->info->base_unit_bits); freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); @@ -104,8 +104,8 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; - ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); - base_unit &= base_unit_range; + ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); + base_unit &= (base_unit_range - 1); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 10/15] pwm: crc: Implement apply() method to support the new atomic PWM API
Replace the enable, disable and config pwm_ops with an apply op, to support the new atomic PWM API. Signed-off-by: Hans de Goede --- Changes in v3: - Keep crc_pwm_calc_clk_div() helper to avoid needless churn --- drivers/pwm/pwm-crc.c | 89 ++- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index b72008c9b072..8a7f4707279c 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -51,59 +51,76 @@ static int crc_pwm_calc_clk_div(int period_ns) return clk_div; } -static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) +static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +const struct pwm_state *state) { - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); - - return 0; -} - -static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); -} - -static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); struct device *dev = crc_pwm->chip.dev; - int level; + int err; - if (period_ns > PWM_MAX_PERIOD_NS) { + if (state->period > PWM_MAX_PERIOD_NS) { dev_err(dev, "un-supported period_ns\n"); return -EINVAL; } - if (pwm_get_period(pwm) != period_ns) { - int clk_div = crc_pwm_calc_clk_div(period_ns); + if (state->polarity != PWM_POLARITY_NORMAL) + return -EOPNOTSUPP; + + if (pwm_is_enabled(pwm) && !state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + if (err) { + dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err); + return err; + } + } + + if (pwm_get_duty_cycle(pwm) != state->duty_cycle || + pwm_get_period(pwm) != state->period) { + int level = state->duty_cycle * PWM_MAX_LEVEL / state->period; + err = regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (err) { + dev_err(dev, "Error writing PWM0_DUTY_CYCLE %d\n", err); + return err; + } + } + + if (pwm_is_enabled(pwm) && state->enabled && + pwm_get_period(pwm) != state->period) { /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } + } - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, - clk_div | PWM_OUTPUT_ENABLE); + if (pwm_get_period(pwm) != state->period || + pwm_is_enabled(pwm) != state->enabled) { + int clk_div = crc_pwm_calc_clk_div(state->period); + int pwm_output_enable = state->enabled ? PWM_OUTPUT_ENABLE : 0; + + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, + clk_div | pwm_output_enable); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } } - /* change the pwm duty cycle */ - level = duty_ns * PWM_MAX_LEVEL / period_ns; - regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (!pwm_is_enabled(pwm) && state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); + if (err) { + dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err); + return err; + } + } return 0; } static const struct pwm_ops crc_pwm_ops = { - .config = crc_pwm_config, - .enable = crc_pwm_enable, - .disable = crc_pwm_disable, + .apply = crc_pwm_apply, }; static int crystalcove_pwm_probe(struct platform_device *pdev) -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 08/15] pwm: crc: Fix period changes not having any effect
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register I strongly suspect that the BACKLIGHT_EN register at address 0x51 really controls a separate output-only GPIO which is connected to the LCD panels backlight-enable input. Like how the PANEL_EN register at address 0x52 controls an output-only GPIO which is earmarked for the LCD panel's enable pin. If this is correct then this GPIO should really be added to the gpio-crystalcove.c driver and the PWM driver should stop poking it. But I've been unable to come up with a definitive answer here, so I'm keeping this as is for now. As the comment in the old code already indicates we must disable the PWM before we can change the clock divider. But the crc_pwm_disable() and crc_pwm_enable() calls the old code make for this only change the BACKLIGHT_EN register; and the value of that register does not matter for changing the period / the divider. What does matter is that the PWM_OUTPUT_ENABLE bit must be cleared before a new value can be written. This commit modifies crc_pwm_config() to clear PWM_OUTPUT_ENABLE instead when changing the period, so that period changes actually work. Note this fix will cause a significant behavior change on some devices using the CRC PWM output to drive their backlight. Before the PWM would always run with the output frequency configured by the BIOS at boot, now the period time specified by the i915 driver will actually be honored. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 7 ++- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 44ec7d5b63e1..81232da0c767 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -82,14 +82,11 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, if (pwm_get_period(pwm) != period_ns) { int clk_div = crc_pwm_calc_clk_div(period_ns); - /* changing the clk divisor, need to disable fisrt */ - crc_pwm_disable(c, pwm); + /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); - - /* enable back */ - crc_pwm_enable(c, pwm); } /* change the pwm duty cycle */ -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 14/15] drm/i915: panel: Honor the VBT PWM min setting for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the minimum allowed PWM level to 0. But several of these devices specify a non 0 minimum setting in their VBT. Change pwm_setup_backlight() to use get_backlight_min_vbt() to get the minimum level. Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 14e611c92194..cb28b9908ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1925,8 +1925,8 @@ static int pwm_setup_backlight(struct intel_connector *connector, */ pwm_apply_args(panel->backlight.pwm); - panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ + panel->backlight.min = get_backlight_min_vbt(connector); level = intel_panel_compute_brightness(connector, 100); ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); @@ -1941,8 +1941,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, level = DIV_ROUND_UP(pwm_get_duty_cycle(panel->backlight.pwm) * 100, panel->backlight.pwm_period_ns); - panel->backlight.level = - intel_panel_compute_brightness(connector, level); + level = intel_panel_compute_brightness(connector, level); + panel->backlight.level = clamp(level, panel->backlight.min, + panel->backlight.max); panel->backlight.enabled = panel->backlight.level != 0; drm_info(_priv->drm, "Using %s PWM for LCD backlight control\n", -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 07/15] pwm: crc: Fix off-by-one error in the clock-divider calculations
The CRC PWM controller has a clock-divider which divides the clock with a value between 1-128. But as can seen from the PWM_DIV_CLK_xxx defines, this range maps to a register value of 0-127. So after calculating the clock-divider we must subtract 1 to get the register value, unless the requested frequency was so high that the calculation has already resulted in a (rounded) divider value of 0. Note that before this fix, setting a period of PWM_MAX_PERIOD_NS which corresponds to the max. divider value of 128 could have resulted in a bug where the code would use 128 as divider-register value which would have resulted in an actual divider value of 0 (and the enable bit being set). A rounding error stopped this bug from actually happen. This same rounding error means that after the subtraction of 1 it is impossible to set the divider to 128. Also bump PWM_MAX_PERIOD_NS by 1 ns to allow setting a divider of 128 (register-value 127). Signed-off-by: Hans de Goede --- Changes in v3: - Introduce crc_pwm_calc_clk_div() here instead of later in the patch-set to reduce the amount of churn in the patch-set a bit --- drivers/pwm/pwm-crc.c | 17 ++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index c056eb9b858c..44ec7d5b63e1 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -22,7 +22,7 @@ #define PWM_MAX_LEVEL 0xFF #define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ +#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */ /** * struct crystalcove_pwm - Crystal Cove PWM controller @@ -39,6 +39,18 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) return container_of(pc, struct crystalcove_pwm, chip); } +static int crc_pwm_calc_clk_div(int period_ns) +{ + int clk_div; + + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_USEC); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; + + return clk_div; +} + static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); @@ -68,11 +80,10 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, } if (pwm_get_period(pwm) != period_ns) { - int clk_div; + int clk_div = crc_pwm_calc_clk_div(period_ns); /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_USEC); regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 09/15] pwm: crc: Enable/disable PWM output on enable/disable
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register So far we've kept the PWM_OUTPUT_ENABLE bit set when disabling the PWM, this commit makes crc_pwm_disable() clear it on disable and makes crc_pwm_enable() set it again on re-enable. Signed-off-by: Hans de Goede --- Changes in v3: - Remove paragraph about tri-stating the output from the commit message, we don't have a datasheet so this was just an unfounded guess --- drivers/pwm/pwm-crc.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 81232da0c767..b72008c9b072 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -54,7 +54,9 @@ static int crc_pwm_calc_clk_div(int period_ns) static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); return 0; @@ -63,8 +65,10 @@ static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); } static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 15/15] drm/i915: panel: Use atomic PWM API for devs with an external PWM controller
Now that the PWM drivers which we use have been converted to the atomic PWM API, we can move the i915 panel code over to using the atomic PWM API. The removes a long standing FIXME and this removes a flicker where the backlight brightness would jump to 100% when i915 loads even if using the fastset path. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 3 +- drivers/gpu/drm/i915/display/intel_panel.c| 73 +-- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index de32f9efb120..4bd9981e70a1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -223,7 +224,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; - int pwm_period_ns; + struct pwm_state pwm_state; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index cb28b9908ca4..a0f76343f381 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -592,10 +592,11 @@ static u32 bxt_get_backlight(struct intel_connector *connector) static u32 pwm_get_backlight(struct intel_connector *connector) { struct intel_panel *panel = >panel; - int duty_ns; + int duty_ns, period_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); + period_ns = pwm_get_period(panel->backlight.pwm); + return DIV_ROUND_UP(duty_ns * 100, period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -669,10 +670,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, - panel->backlight.pwm_period_ns); + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void @@ -841,10 +842,8 @@ static void pwm_disable_backlight(const struct drm_connector_state *old_conn_sta struct intel_connector *connector = to_intel_connector(old_conn_state->connector); struct intel_panel *panel = >panel; - /* Disable the backlight */ - intel_panel_actually_set_backlight(old_conn_state, 0); - usleep_range(2000, 3000); - pwm_disable(panel->backlight.pwm); + panel->backlight.pwm_state.enabled = false; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state) @@ -1176,9 +1175,14 @@ static void pwm_enable_backlight(const struct intel_crtc_state *crtc_state, { struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = >panel; + int level = panel->backlight.level; - pwm_enable(panel->backlight.pwm); - intel_panel_actually_set_backlight(conn_state, panel->backlight.level); + level = intel_panel_compute_brightness(connector, level); + + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + panel->backlight.pwm_state.enabled = true; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state, @@ -1897,8 +1901,7 @@ static int pwm_setup_backlight(struct intel_connector *connector, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_panel *panel = >panel; const char *desc; - u32 level, ns; - int retval; + u32 level; /* Get the right PWM chip for DSI backlight according to VBT */ if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) { @@ -1916,36 +1919,30 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } - panel->backlight.pwm_period_ns = NSEC_PER_SEC / -get_vbt_pwm_freq(dev_priv); - - /* -
[PATCH v3 04/15] pwm: lpss: Add range limit check for the base_unit register value
When the user requests a high enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value of 0. But according to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. Adding 0 to the counter is a no-op. The data-sheet even explicitly states that writing 0 to the base_unit bits will result in the PWM outputting a continuous 0 signal. When the user requestes a low enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value which is bigger then base_unit_range - 1. Currently the codes for this deals with this by applying a mask: base_unit &= (base_unit_range - 1); But this means that we let the value overflow the range, we throw away the higher bits and store whatever value is left in the lower bits into the register leading to a random output frequency, rather then clamping the output frequency to the highest frequency which the hardware can do. This commit fixes both issues by clamping the base_unit value to be between 1 and (base_unit_range - 1). Fixes: 684309e5043e ("pwm: lpss: Avoid potential overflow of base_unit") Signed-off-by: Hans de Goede --- Changes in v3: - Change upper limit of clamp to (base_unit_range - 1) - Add Fixes tag --- drivers/pwm/pwm-lpss.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 43b1fc634af1..80d0f9c64f9d 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -97,6 +97,9 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); + /* base_unit must not be 0 and we also want to avoid overflowing it */ + base_unit = clamp_t(unsigned long long, base_unit, 1, + base_unit_range - 1); on_time_div = 255ULL * duty_ns; do_div(on_time_div, period_ns); @@ -105,7 +108,6 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); - base_unit &= (base_unit_range - 1); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 13/15] drm/i915: panel: Honor the VBT PWM frequency for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the period-time passed to pwm_config() to 21333 ns. I suspect this was done because many VBTs set the PWM frequency to 200 which corresponds to a period-time of 500 ns, which greatly exceeds the PWM_MAX_PERIOD_NS define in the Crystal Cove PMIC PWM driver, which used to be 21333. This PWM_MAX_PERIOD_NS define was actually based on a bug in the PWM driver where its period and duty-cycle times where off by a factor of 256. Due to this bug the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 would result in the PWM driver using its divider of 128, which would result in a PWM output frequency of 600 Hz / 256 / 128 = 183 Hz. So actually pretty close to the default VBT value of 200 Hz. Now that this bug in the pwm-crc driver is fixed, we can actually use the VBT defined frequency. This is important because: a) With the pwm-crc driver fixed it will now translate the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 ns / 46 Khz to a PWM output frequency of 23 KHz (the max it can do). b) The pwm-lpss driver used on many models has always honored the 21333 ns / 46 Khz request Some panels do not like such high output frequencies. E.g. on a Terra Pad 1061 tablet, using the LPSS PWM controller, the backlight would go from off to max, when changing the sysfs backlight brightness value from 90-100%, anything under aprox. 90% would turn the backlight fully off. Honoring the VBT specified PWM frequency will also hopefully fix the various bug reports which we have received about users perceiving the backlight to flicker after a suspend/resume cycle. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 1 + drivers/gpu/drm/i915/display/intel_panel.c| 19 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 2bf3d4cb4ea9..de32f9efb120 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -223,6 +223,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; + int pwm_period_ns; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 8efdd9f08a08..14e611c92194 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -40,8 +40,6 @@ #include "intel_dsi_dcs_backlight.h" #include "intel_panel.h" -#define CRC_PMIC_PWM_PERIOD_NS 21333 - void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -597,7 +595,7 @@ static u32 pwm_get_backlight(struct intel_connector *connector) int duty_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); + return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -671,9 +669,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); + pwm_config(panel->backlight.pwm, duty_ns, + panel->backlight.pwm_period_ns); } static void @@ -1917,6 +1916,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } + panel->backlight.pwm_period_ns = NSEC_PER_SEC / +get_vbt_pwm_freq(dev_priv); + /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. @@ -1926,9 +1928,10 @@ static int pwm_setup_backlight(struct intel_connector *connector, panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ level = intel_panel_compute_brightness(connector, 100); - ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - retval = pwm_config(panel->backlight.pwm, ns, CRC_PMIC_PWM_PERIOD_NS); + retval = pwm_config(panel->backlight.pwm, ns, +
[PATCH v3 02/15] ACPI / LPSS: Save Cherry Trail PWM ctx registers only once (at activation)
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets turned off from the _PS3 method of the graphics-card dev: Method (_PS3, 0, Serialized) // _PS3: Power State 3 { ... PWMB = PWMC /* \_SB_.PCI0.GFX0.PWMC */ PSAT |= 0x03 Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ ... } Where PSAT is the power-status register of the PWM controller. Since the i915 driver will do a pwm_get on the pwm device as it uses it to control the LCD panel backlight, there is a device-link marking the i915 device as a consumer of the pwm device. So that the PWM controller will always be suspended after the i915 driver suspends (which is the right thing to do). This causes the above GFX0 PS3 AML code to run before acpi_lpss.c calls acpi_lpss_save_ctx(). So on these devices the PWM controller will already be off when acpi_lpss_save_ctx() runs. This causes it to read/save all 1-s (0x) as ctx register values. When these bogus values get restored on resume the PWM controller actually keeps working, since most bits are reserved, but this does set bit 3 of the LPSS General purpose register, which for the PWM controller has the following function: "This bit is re-used to support 32kHz slow mode. Default is 19.2MHz as PWM source clock". This causes the clock of the PWM controller to switch from 19.2MHz to 32KHz, which is a slow-down of a factor 600. Surprisingly enough so far there have been few bug reports about this. This is likely because the i915 driver was hardcoding the PWM frequency to 46 KHz, which divided by 600 would result in a PWM frequency of approx. 78 Hz, which mostly still works fine. There are some bug reports about the LCD backlight flickering after suspend/resume which are likely caused by this issue. But with the upcoming patch-series to finally switch the i915 drivers code for external PWM controllers to use the atomic API and to honor the PWM frequency specified in the video BIOS (VBT), this becomes a much bigger problem. On most cases the VBT specifies either 200 Hz or 20 KHz as PWM frequency, which with the mentioned issue ends up being either 1/3 Hz, where the backlight actually visible blinks on and off every 3s, or in 33 Hz and horrible flickering of the backlight. There are a number of possible solutions to this problem: 1. Make acpi_lpss_save_ctx() run before GFX0._PS3 Pro: Clean solution from pov of not medling with save/restore ctx code Con: As mentioned the current ordering is the right thing to do Con: Requires assymmetry in at what suspend/resume phase we do the save vs restore, requiring more suspend/resume ordering hacks in already convoluted acpi_lpss.c suspend/resume code. 2. Do some sort of save once mode for the LPSS ctx Pro: Reasonably clean Con: Needs a new LPSS flag + code changes to handle the flag 3. Detect we have failed to save the ctx registers and do not restore them Pro: Not PWM specific, might help with issues on other LPSS devices too Con: If we can get away with not restoring the ctx why bother with it at all? 4. Do not save the ctx for CHT PWM controllers Pro: Clean, as simple as dropping a flag? Con: Not so simple as dropping a flag, needs a new flag to ensure that we still do lpss_deassert_reset() on device activation. 5. Make the pwm-lpss code fixup the LPSS-context registers Pro: Keeps acpi_lpss.c code clean Con: Moves knowledge of LPSS-context into the pwm-lpss.c code 1 and 5 both do not seem to be a desirable way forward. 3 and 4 seem ok, but they both assume that restoring the LPSS-context registers is not necessary. I have done a couple of test and those do show that restoring the LPSS-context indeed does not seem to be necessary on devices using s2idle suspend (and successfully reaching S0i3). But I have no hardware to test deep / S3 suspend. So I'm not sure that not restoring the context is safe. That leaves solution 2, which is about as simple / clean as 3 and 4, so this commit fixes the described problem by implementing a new LPSS_SAVE_CTX_ONCE flag and setting that for the CHT PWM controllers. Signed-off-by: Hans de Goede --- Changes in v2: - Move #define LPSS_SAVE_CTX_ONCE define to group it with LPSS_SAVE_CTX --- drivers/acpi/acpi_lpss.c | 21 + 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 446e666b3466..7e6db0f1d9ee 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -67,7 +67,15 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_CLK_DIVIDER BIT(2) #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) -#define LPSS_NO_D3_DELAY BIT(5) +/* + * For some devices the DSDT AML code for another device turns off the device + * before our suspend h
[PATCH v3 11/15] pwm: crc: Implement get_state() method
Implement the pwm_ops.get_state() method to complete the support for the new atomic PWM API. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede --- Changes in v3: - Add Andy's Reviewed-by tag - Remove extra whitespace to align some code after assignments (requested by Uwe Kleine-König) --- drivers/pwm/pwm-crc.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 8a7f4707279c..b311354d40a3 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -119,8 +119,37 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + struct device *dev = crc_pwm->chip.dev; + unsigned int clk_div, clk_div_reg, duty_cycle_reg; + int error; + + error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, _div_reg); + if (error) { + dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error); + return; + } + + error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, _cycle_reg); + if (error) { + dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error); + return; + } + + clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1; + + state->period = clk_div * NSEC_PER_USEC * 256 / PWM_BASE_CLK_MHZ; + state->duty_cycle = duty_cycle_reg * state->period / PWM_MAX_LEVEL; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled = !!(clk_div_reg & PWM_OUTPUT_ENABLE); +} + static const struct pwm_ops crc_pwm_ops = { .apply = crc_pwm_apply, + .get_state = crc_pwm_get_state, }; static int crystalcove_pwm_probe(struct platform_device *pdev) -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 12/15] drm/i915: panel: Add get_vbt_pwm_freq() helper
Factor the code which checks and drm_dbg_kms-s the VBT PWM frequency out of get_backlight_max_vbt(). This is a preparation patch for honering the VBT PWM frequency for devices which use an external PWM controller (devices using pwm_setup_backlight()). Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 27 ++ 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 3c5056dbf607..8efdd9f08a08 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1543,18 +1543,9 @@ static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul); } -static u32 get_backlight_max_vbt(struct intel_connector *connector) +static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_panel *panel = >panel; u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz; - u32 pwm; - - if (!panel->backlight.hz_to_pwm) { - drm_dbg_kms(_priv->drm, - "backlight frequency conversion not supported\n"); - return 0; - } if (pwm_freq_hz) { drm_dbg_kms(_priv->drm, @@ -1567,6 +1558,22 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector) pwm_freq_hz); } + return pwm_freq_hz; +} + +static u32 get_backlight_max_vbt(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_panel *panel = >panel; + u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv); + u32 pwm; + + if (!panel->backlight.hz_to_pwm) { + drm_dbg_kms(_priv->drm, + "backlight frequency conversion not supported\n"); + return 0; + } + pwm = panel->backlight.hz_to_pwm(connector, pwm_freq_hz); if (!pwm) { drm_dbg_kms(_priv->drm, -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 00/15] acpi/pwm/i915: Convert pwm-crc and i915 driver's PWM code to use the atomic PWM API
Hi All, Here is v3 of my patch series converting the i915 driver's code for controlling the panel's backlight with an external PWM controller to use the atomic PWM API. See below for the changelog. Initially the plan was for this series to consist of 2 parts: 1. convert the pwm-crc driver to support the atomic PWM API and 2. convert the i915 driver's PWM code to use the atomic PWM API. But during testing I've found a number of bugs in the pwm-lpss and I found that the acpi_lpss code needs some special handling because of some ugliness found in most Cherry Trail DSDTs. So now this series has grown somewhat large and consists of 4 parts: 1. acpi_lpss fixes workarounds for Cherry Trail DSTD nastiness 2. various fixes to the pwm-lpss driver 3. convert the pwm-crc driver to support the atomic PWM API and 4. convert the i915 driver's PWM code to use the atomic PWM API So we need to discuss how to merge this (once it passes review). Although the inter-dependencies are only runtime I still think we should make sure that 1-3 are in the drm-intel-next-queued (dinq) tree before merging the i915 changes. Both to make sure that the intel-gfx CI system does not become unhappy and for bisecting reasons. The involved acpi_lpss and pwm drivers do not see a whole lot of churn, so it likely is the easiest to just merge everything through dinq. Rafael and Thierry, can I get your Acked-by for directly merging this into dinq? This series has been tested (and re-tested after adding various bug-fixes) extensively. It has been tested on the following devices: -Asus T100TA BYT + CRC-PMIC PWM -Toshiba WT8-A BYT + CRC-PMIC PWM -Thundersoft TS178 BYT + CRC-PMIC PWM, inverse PWM -Asus T100HA CHT + CRC-PMIC PWM -Terra Pad 1061 BYT + LPSS PWM -Trekstor Twin 10.1 BYT + LPSS PWM -Asus T101HA CHT + CRC-PMIC PWM -GPD Pocket CHT + CRC-PMIC PWM Changelog: Changes in v2: - Fix coverletter subject - Drop accidentally included debugging patch - "[PATCH v3 02/15] ACPI / LPSS: Save Cherry Trail PWM ctx registers only once ( - Move #define LPSS_SAVE_CTX_ONCE define to group it with LPSS_SAVE_CTX Changes in v3: - "[PATCH v3 04/15] pwm: lpss: Add range limit check for the base_unit register value" - Use base_unit_range - 1 as maximum value for the clamp() - "[PATCH v3 05/15] pwm: lpss: Use pwm_lpss_apply() when restoring state on resume" - This replaces the "pwm: lpss: Set SW_UPDATE bit when enabling the PWM" patch from previous versions of this patch-set, which really was a hack working around the resume issue which this patch fixes properly. - PATCH v3 6 - 11 pwm-crc changes: - Various small changes resulting from the reviews by Andy and Uwe, including some refactoring of the patches to reduce the amount of churn in the patch-set Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: pwm/i915: Convert pwm-crc and i915 driver's PWM code to use the atomic PWM API
Hi, On 6/11/20 11:21 PM, Uwe Kleine-König wrote: Hello, On Mon, Jun 08, 2020 at 04:35:00PM +0200, Daniel Vetter wrote: On Sat, Jun 06, 2020 at 10:25:45PM +0200, Hans de Goede wrote: Hi All, This patch series converts the i915 driver's cpde for controlling the panel's backlight with an external PWM controller to use the atomic PWM API. Initially the plan was for this series to consist of 2 parts: 1. convert the pwm-crc driver to support the atomic PWM API and 2. convert the i915 driver's PWM code to use the atomic PWM API. But during testing I've found a number of bugs in the pwm-lpss and I found that the acpi_lpss code needs some special handling because of some ugliness found in most Cherry Trail DSDTs. So now this series has grown somewhat large and consists of 4 parts: 1. acpi_lpss fixes workarounds for Cherry Trail DSTD nastiness 2. various fixes to the pwm-lpss driver 3. convert the pwm-crc driver to support the atomic PWM API and 4. convert the i915 driver's PWM code to use the atomic PWM API So we need to discuss how to merge this (once it passes review). Although the inter-dependencies are only runtime I still think we should make sure that 1-3 are in the drm-intel-next-queued (dinq) tree before merging the i915 changes. Both to make sure that the intel-gfx CI system does not become unhappy and for bisecting reasons. Simplest is if acpi acks the acpi patches for merging through drm-intel.git. Second simplest is topic branch (drm-intel maintainers can do that) with the entire pile, which then acpi and drm-intel can both pull in. Up to the two maintainer teams to figure this one out. I'm unclear about the dependencies There is a runtime dependency of the i915 changes on the PWM changes and since the intel-gfx folks use a lot of CI, we this need to get the PWM changes into the drm-intel tree before the i915 changes can land. , but the changes to drivers/pwm need an ack (or processing) by the PWM team. Of course, I asked for an Acked-by from the PWM team (once this passes review) for merging this through the drm-intel tree, as the i915 driver is the main (only AFAIK) consumer of the PWMs controlled by these 2 drivers. Daniel -ed that bit when he replied. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 11/15] pwm: crc: Implement get_state() method
Hi, On 6/11/20 11:37 PM, Uwe Kleine-König wrote: Hello, On Sun, Jun 07, 2020 at 08:18:36PM +0200, Hans de Goede wrote: Implement the pwm_ops.get_state() method to complete the support for the new atomic PWM API. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 58c7e9ef7278..6c75a3470bc8 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -114,8 +114,37 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + struct device *dev = crc_pwm->chip.dev; + unsigned int clk_div, clk_div_reg, duty_cycle_reg; + int error; + + error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, _div_reg); + if (error) { + dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error); + return; + } + + error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, _cycle_reg); + if (error) { + dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error); + return; + } I assume that duty_cycle_reg cannot be bigger than 0xff? Would it make sense to mask the value accordingly to get more robust code? + clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1; + + state->period = clk_div * NSEC_PER_MHZ * 256 / PWM_BASE_CLK_MHZ; + state->duty_cycle = duty_cycle_reg * state->period / PWM_MAX_LEVEL; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled= !!(clk_div_reg & PWM_OUTPUT_ENABLE); These aligned = look strange (IMHO). If you don't feel strong here I'd like to see a single space before a =. Ok, will change for the next version. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 09/15] pwm: crc: Enable/disable PWM output on enable/disable
Hi, On 6/12/20 12:20 AM, Uwe Kleine-König wrote: On Sun, Jun 07, 2020 at 08:18:34PM +0200, Hans de Goede wrote: The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register So far we've kept the PWM_OUTPUT_ENABLE bit set when disabling the PWM, this commit makes crc_pwm_disable() clear it on disable and makes crc_pwm_enable() set it again on re-enable. This should disable the internal (divided) PWM clock and tri-state the PWM output pin when disabled, saving some power. It would be great if you could also document that disabling the PWM makes the output tri-state. There are a few drivers that have a "Limitations" section at their top. Describing that there (in the same format) would be the right place. Also note that according to Thierry's conception getting a (driven) inactive output is the right thing for a disabled PWM. Hmm, the tri-state thing is an assumption from my side and we don't have any docs for this PWM controller, so I'm not sure at all if that is true. So I think it will be better to just drop the tri-state bit from the commit msg for the next version. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 06/15] pwm: crc: Fix period / duty_cycle times being off by a factor of 256
Hi, On 6/9/20 1:29 PM, Andy Shevchenko wrote: On Sun, Jun 07, 2020 at 08:18:31PM +0200, Hans de Goede wrote: While looking into adding atomic-pwm support to the pwm-crc driver I noticed something odd, there is a PWM_BASE_CLK define of 6 MHz and there is a clock-divider which divides this with a value between 1-128, and there are 256 duty-cycle steps. The pwm-crc code before this commit assumed that a clock-divider setting of 1 means that the PWM output is running at 6 MHZ, if that is true, where do these 256 duty-cycle steps come from? This would require an internal frequency of 256 * 6 MHz = 1.5 GHz, that seems unlikely for a PMIC which is using a silicon process optimized for power-switching transistors. It is way more likely that there is an 8 bit counter for the duty cycle which acts as an extra fixed divider wrt the PWM output frequency. The main user of the pwm-crc driver is the i915 GPU driver which uses it for backlight control. Lets compare the PWM register values set by the video-BIOS (the GOP), assuming the extra fixed divider is present versus the PWM frequency specified in the Video-BIOS-Tables: Device: PWM Hz set by BIOS PWM Hz specified in VBT Asus T100TA 200 200 Asus T100HA 200 200 Lenovo Miix 2 8 23437 2 Toshiba WT8-A 23437 2 So as we can see if we assume the extra division by 256 then the register values set by the GOP are an exact match for the VBT values, where as otherwise the values would be of by a factor of 256. This commit fixes the period / duty_cycle calculations to take the extra division by 256 into account. ... +#define NSEC_PER_MHZ 1000 This is against physics. What this cryptic name means actually? Existing NSEC_PER_USEC ? Yes, using existing NSEC_PER_USEC is better I will use that for the next version. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 10/15] pwm: crc: Implement apply() method to support the new atomic PWM API
Hi, On 6/9/20 1:32 PM, Andy Shevchenko wrote: On Sun, Jun 07, 2020 at 08:18:35PM +0200, Hans de Goede wrote: Replace the enable, disable and config pwm_ops with an apply op, to support the new atomic PWM API. ... -static int crc_pwm_calc_clk_div(int period_ns) +static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +const struct pwm_state *state) { - int clk_div; - - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); - /* clk_div 1 - 128, maps to register values 0-127 */ - if (clk_div > 0) - clk_div--; - - return clk_div; -} ... + clk_div = PWM_BASE_CLK_MHZ * state->period / + (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; And again... :-( Well yes I cannot help it that the original code, as submitted by Intel, was of very questionable quality, so instead of just converting it to the atomic PWM API I had to do a ton of bugfixes first... I tried to do this all in small bits rather then in a single big rewrite the buggy commit to make life easier for reviewers. I can introduce the crc_pwm_calc_clk_div helper earlier as you suggested in an earlier mail. I guess I could also keep the helper here, and then fold it into the function in a later commit (*). Would that work for you ? Regards, Hans *) Because having a helper for 3 lines of code when it is used only once is not helpful IMHO, it only makes it harder to figure out what the code is exactly doing when readin the code. ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 03/15] pwm: lpss: Add range limit check for the base_unit register value
Hi, On 6/8/20 2:51 PM, Andy Shevchenko wrote: On Mon, Jun 08, 2020 at 01:07:12PM +0200, Hans de Goede wrote: On 6/8/20 5:50 AM, Andy Shevchenko wrote: On Sun, Jun 07, 2020 at 08:18:28PM +0200, Hans de Goede wrote: When the user requests a high enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value of 0. But according to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. Adding 0 to the counter is a no-op. The data-sheet even explicitly states that writing 0 to the base_unit bits will result in the PWM outputting a continuous 0 signal. So, and why it's a problem? Lets sya the user requests a PWM output frequency of 100Hz on Cherry Trail which has a 1920 Hz clock this will result in 100 * 65536 / 1920 = 0.3 -> 0 as base-unit value. So instead of getting 100 Hz the user will now get a pin which is always outputting low. OTOH if we clamp to 1 as lowest value, the user will get 19200 / 65536 = 292 Hz as output frequency which is as close to the requested value as we can get while actually still working as a PWM controller. So, we should basically divide and round up, no? Yes, that will work for the low limit of base_unit but it will make all the other requested period values less accurate. At least for 0 we will get 0. We're dealing with frequency here, but the API is dealing with period, so to get 0 HZ the API user would have to request a period of > 1s e.g. request 2s / 0.5 Hz but then the user is still not really requesting 0Hz (that would correspond with a period of infinity which integers cannot represent. base_unit values > (base_unit_range / 256), or iow base_unit values using the 8 most significant bits, cause loss of resolution of the duty-cycle. E.g. assuming a base_unit_range of 65536 steps, then a base_unit value of 768 (256 * 3), limits the duty-cycle resolution to 65536 / 768 = 85 steps. Clamp the max base_unit value to base_unit_range / 32 to ensure a duty-cycle resolution of at least 32 steps. This limits the maximum output frequency to 600 KHz / 780 KHz depending on the base clock. This part I don't understand. Why we limiting base unit? I seems like a deliberate regression. The way the PWM controller works is that the base-unit gets added to say a 16 bit (on CHT) counter each input clock and then the highest 8 bits of that counter get compared to the value programmed into the ON_TIME_DIV bits. Lets say we do not clamp and allow any value and lets say the user selects an output frequency of half the input clock, so base-unit value is 32768, then the counter will only have 2 values: 0 and 32768 after that it will wrap around again. So any on time-div value < 128 will result in the output being always high and any value > 128 will result in the output being high/low 50% of the time and a value of 255 will make the output always low. So in essence we now only have 3 duty cycle levels, which seems like a bad idea to me / not what a pwm controller is supposed to do. It's exactly what is written in the documentation. I can't buy base unit clamp. Though, I can buy, perhaps, on time divisor granularity, i.e. 1/ 0% - 25%-1 (0%) 2/ 25% - 50% - 75% (50%) 3/ 75%+1 - 100% (100%) And so on till we got a maximum resolution (8 bits). Note that the PWM API does not expose the granularity to the API user, which is why I went with just putting a minimum on it of 32 steps. Anyways I don't have a strong opinion on this, so I'm fine with not clamping the base-unit to preserve granularity. We should still clamp it to avoid overflow if the user us requesting a really high frequency though! The old code had: base_unit &= base_unit_range; Which means that if the user requests a too high value, then we first overflow base_unit and then truncate it to fit leading to a random frequency. So if we forget my minimal granularity argument, then at a minimum we need to replace the above line with: base_unit = clamp_t(unsigned long long, base_unit, 1, base_unit_range - 1); And since we need the clamp anyways we can then keep the current round-closest behavior. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 04/15] pwm: lpss: Fix off by one error in base_unit math in pwm_lpss_prepare()
Hi, On 6/8/20 5:55 AM, Andy Shevchenko wrote: On Sun, Jun 07, 2020 at 08:18:29PM +0200, Hans de Goede wrote: According to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. So assuming e.g. a 16 bit counter this means that if base_unit is set to 1, after 65535 input clock-cycles the counter has been increased from 0 to 65535 and it will overflow on the next cycle, so it will overflow after every 65536 clock cycles and thus the calculations done in pwm_lpss_prepare() should use 65536 and not 65535. This commit fixes this. Note this also aligns the calculations in pwm_lpss_prepare() with those in pwm_lpss_get_state(). This one sounds like a bug which I have noticed on Broxton (but thought as a hardware issue). In any case it has to be tested on various platforms to see how it affects on them. If you like at the datasheet / read my commit description then it becomes obvious that because of the way the PWM controller works that it takes the full 2^(base-unit-bits) for the counter to overflow, not 2^(base-unit-bits) - 1. This will make a difference of a factor 65535/65536 in the output frequency which will be tricky to measure. IOW I'm not sure we can really test if this helps, but it is obviously the right thing to do and it aligns the pwm_apply code with the pwm_get_state code which already does not have the - 1. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2 03/15] pwm: lpss: Add range limit check for the base_unit register value
Hi, On 6/8/20 5:50 AM, Andy Shevchenko wrote: On Sun, Jun 07, 2020 at 08:18:28PM +0200, Hans de Goede wrote: When the user requests a high enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value of 0. But according to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. Adding 0 to the counter is a no-op. The data-sheet even explicitly states that writing 0 to the base_unit bits will result in the PWM outputting a continuous 0 signal. So, and why it's a problem? Lets sya the user requests a PWM output frequency of 100Hz on Cherry Trail which has a 1920 Hz clock this will result in 100 * 65536 / 1920 = 0.3 -> 0 as base-unit value. So instead of getting 100 Hz the user will now get a pin which is always outputting low. OTOH if we clamp to 1 as lowest value, the user will get 19200 / 65536 = 292 Hz as output frequency which is as close to the requested value as we can get while actually still working as a PWM controller. base_unit values > (base_unit_range / 256), or iow base_unit values using the 8 most significant bits, cause loss of resolution of the duty-cycle. E.g. assuming a base_unit_range of 65536 steps, then a base_unit value of 768 (256 * 3), limits the duty-cycle resolution to 65536 / 768 = 85 steps. Clamp the max base_unit value to base_unit_range / 32 to ensure a duty-cycle resolution of at least 32 steps. This limits the maximum output frequency to 600 KHz / 780 KHz depending on the base clock. This part I don't understand. Why we limiting base unit? I seems like a deliberate regression. The way the PWM controller works is that the base-unit gets added to say a 16 bit (on CHT) counter each input clock and then the highest 8 bits of that counter get compared to the value programmed into the ON_TIME_DIV bits. Lets say we do not clamp and allow any value and lets say the user selects an output frequency of half the input clock, so base-unit value is 32768, then the counter will only have 2 values: 0 and 32768 after that it will wrap around again. So any on time-div value < 128 will result in the output being always high and any value > 128 will result in the output being high/low 50% of the time and a value of 255 will make the output always low. So in essence we now only have 3 duty cycle levels, which seems like a bad idea to me / not what a pwm controller is supposed to do. So I decided to put a cut of at having at least 32 steps. The mean reason I wrote this patch though is to avoid a base-unit value of 0 which really results in a completely non working PWM output. I personally believe clamping on the high side is a good idea too. But if you are against that I can drop that part. Note that the clamping on the high side will not affect the primary user of the LPSS-pwm driver which is the i915 backlight code, that never asks for such high frequencies. But it could help to avoid an user shooting themselves in the foot when using the PWM on a dev board through the sysfs interface. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 09/15] pwm: crc: Enable/disable PWM output on enable/disable
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register So far we've kept the PWM_OUTPUT_ENABLE bit set when disabling the PWM, this commit makes crc_pwm_disable() clear it on disable and makes crc_pwm_enable() set it again on re-enable. This should disable the internal (divided) PWM clock and tri-state the PWM output pin when disabled, saving some power. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 24 +--- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index ef49a6e3c4d6..53734bcf67e1 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -41,10 +41,24 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) return container_of(pc, struct crystalcove_pwm, chip); } +static int crc_pwm_calc_clk_div(int period_ns) +{ + int clk_div; + + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; + + return clk_div; +} + static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); return 0; @@ -53,8 +67,10 @@ static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); } static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, @@ -70,16 +86,10 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, } if (pwm_get_period(pwm) != period_ns) { - int clk_div; + int clk_div = crc_pwm_calc_clk_div(period_ns); /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); - - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); - /* clk_div 1 - 128, maps to register values 0-127 */ - if (clk_div > 0) - clk_div--; - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); } -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 12/15] drm/i915: panel: Add get_vbt_pwm_freq() helper
Factor the code which checks and drm_dbg_kms-s the VBT PWM frequency out of get_backlight_max_vbt(). This is a preparation patch for honering the VBT PWM frequency for devices which use an external PWM controller (devices using pwm_setup_backlight()). Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 27 ++ 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 3c5056dbf607..8efdd9f08a08 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1543,18 +1543,9 @@ static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul); } -static u32 get_backlight_max_vbt(struct intel_connector *connector) +static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_panel *panel = >panel; u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz; - u32 pwm; - - if (!panel->backlight.hz_to_pwm) { - drm_dbg_kms(_priv->drm, - "backlight frequency conversion not supported\n"); - return 0; - } if (pwm_freq_hz) { drm_dbg_kms(_priv->drm, @@ -1567,6 +1558,22 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector) pwm_freq_hz); } + return pwm_freq_hz; +} + +static u32 get_backlight_max_vbt(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_panel *panel = >panel; + u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv); + u32 pwm; + + if (!panel->backlight.hz_to_pwm) { + drm_dbg_kms(_priv->drm, + "backlight frequency conversion not supported\n"); + return 0; + } + pwm = panel->backlight.hz_to_pwm(connector, pwm_freq_hz); if (!pwm) { drm_dbg_kms(_priv->drm, -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 11/15] pwm: crc: Implement get_state() method
Implement the pwm_ops.get_state() method to complete the support for the new atomic PWM API. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 58c7e9ef7278..6c75a3470bc8 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -114,8 +114,37 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + struct device *dev = crc_pwm->chip.dev; + unsigned int clk_div, clk_div_reg, duty_cycle_reg; + int error; + + error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, _div_reg); + if (error) { + dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error); + return; + } + + error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, _cycle_reg); + if (error) { + dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error); + return; + } + + clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1; + + state->period = clk_div * NSEC_PER_MHZ * 256 / PWM_BASE_CLK_MHZ; + state->duty_cycle = duty_cycle_reg * state->period / PWM_MAX_LEVEL; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled= !!(clk_div_reg & PWM_OUTPUT_ENABLE); +} + static const struct pwm_ops crc_pwm_ops = { .apply = crc_pwm_apply, + .get_state = crc_pwm_get_state, }; static int crystalcove_pwm_probe(struct platform_device *pdev) -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 10/15] pwm: crc: Implement apply() method to support the new atomic PWM API
Replace the enable, disable and config pwm_ops with an apply op, to support the new atomic PWM API. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 107 +++--- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 53734bcf67e1..58c7e9ef7278 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -41,70 +41,81 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) return container_of(pc, struct crystalcove_pwm, chip); } -static int crc_pwm_calc_clk_div(int period_ns) +static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +const struct pwm_state *state) { - int clk_div; - - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); - /* clk_div 1 - 128, maps to register values 0-127 */ - if (clk_div > 0) - clk_div--; - - return clk_div; -} - -static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); - - return 0; -} - -static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); -} - -static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + int err, clk_div, level, pwm_output_enable; struct device *dev = crc_pwm->chip.dev; - int level; - if (period_ns > PWM_MAX_PERIOD_NS) { + if (state->period > PWM_MAX_PERIOD_NS) { dev_err(dev, "un-supported period_ns\n"); return -EINVAL; } - if (pwm_get_period(pwm) != period_ns) { - int clk_div = crc_pwm_calc_clk_div(period_ns); + if (state->polarity != PWM_POLARITY_NORMAL) + return -ENOTSUPP; + + if (pwm_is_enabled(pwm) && !state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + if (err) { + dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err); + return err; + } + } + + if (pwm_get_duty_cycle(pwm) != state->duty_cycle || + pwm_get_period(pwm) != state->period) { + level = state->duty_cycle * PWM_MAX_LEVEL / state->period; + err = regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (err) { + dev_err(dev, "Error writing PWM0_DUTY_CYCLE %d\n", err); + return err; + } + } + + if (pwm_is_enabled(pwm) && state->enabled && + pwm_get_period(pwm) != state->period) { /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, - clk_div | PWM_OUTPUT_ENABLE); + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } } - /* change the pwm duty cycle */ - level = duty_ns * PWM_MAX_LEVEL / period_ns; - regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (pwm_get_period(pwm) != state->period || + pwm_is_enabled(pwm) != state->enabled) { + clk_div = PWM_BASE_CLK_MHZ * state->period / + (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; + + pwm_output_enable = state->enabled ? PWM_OUTPUT_ENABLE : 0; + + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, + clk_div | pwm_output_enable); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } + } + + if (!pwm_is_enabled(pwm) && state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); + if (err) { +
[PATCH v2 05/15] pwm: lpss: Set SW_UPDATE bit when enabling the PWM
On the LPSS PWM controller found on Bay Trail (BYT) and Cherry Trail (CHT) platforms, the following sequence results in an output duty-cycle of 100% independent of what the duty-cycle requested in the ctrl-reg is: 1. Clear ENABLE bit in ctrl register 2. Let the machine reach a S0i3 low power state 3. Set the ENABLE bit in the ctrl register The LPSS PWM controller has a mechanism where the ctrl register value and the actual base-unit and on-time-div values used are latched. When software sets the SW_UPDATE bit then at the end of the current PWM cycle, the new values from the ctrl-register will be latched into the actual registers, and the SW_UPDATE bit will be cleared. Note on BYT and CHT the ENABLE bit must be set before waiting for the SW_UPDATE bit to clear, otherwise the SW_UPDATE bit will never clear (this is indicated in the pwm-lpss.c code by lpwm->info->bypass being false). My theory about why this is happening is that when we hit S0i3 the part which holds the latched values gets turned off and when its turned back on again at least the on-time-div value has been lost and gets reset to 0 which corresponds to an output duty-cycle of 100%. Testing has shown that setting the SW_UPDATE bit to request latching the ctrl-register values into the actual registers (again) fixes this, confirming this theory. In the past there have been issues where setting the SW_UPDATE bit when nothing has changed would lead to the next ctrl register changing being ignored, see commit 2153bbc12f77 ("pwm: lpss: Only set update bit if we are actually changing the settings"), so we should only set the SW_UPDATE bit when actually changing the ENABLE bit from 0 to 1. When looking into how to fix this I noticed that on platforms where lpwm->info->bypass == false we unnecessarily do 2 read-modify-write cycles of the ctrl register, one to set the base-unit and on-time-div, immediately followed by another to set the ENABLE bit. This commit fixes the 100% duty cycle issue by folding the setting of the ENABLE bit into pwm_lpss_prepare(), which already checks if any bits in the ctrl-register have actually changed and if that is the case then sets the SW_UPDATE bit. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index a764e062103b..2cb0e2a9c08c 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -80,7 +80,7 @@ static inline int pwm_lpss_is_updating(struct pwm_device *pwm) } static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, -int duty_ns, int period_ns) +int duty_ns, int period_ns, bool enable) { unsigned long long on_time_div; unsigned long c = lpwm->info->clk_rate, base_unit_range; @@ -115,6 +115,8 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; + if (enable) + ctrl |= PWM_ENABLE; if (orig_ctrl != ctrl) { pwm_lpss_write(pwm, ctrl); @@ -142,8 +144,9 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, pm_runtime_put(chip->dev); return ret; } - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); - pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false); + pwm_lpss_prepare(lpwm, pwm, +state->duty_cycle, state->period, +lpwm->info->bypass == false); ret = pwm_lpss_wait_for_update(pwm); if (ret) { pm_runtime_put(chip->dev); @@ -154,7 +157,8 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, ret = pwm_lpss_is_updating(pwm); if (ret) return ret; - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); + pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, +state->period, false); return pwm_lpss_wait_for_update(pwm); } } else if (pwm_is_enabled(pwm)) { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 15/15] drm/i915: panel: Use atomic PWM API for devs with an external PWM controller
Now that the PWM drivers which we use have been converted to the atomic PWM API, we can move the i915 panel code over to using the atomic PWM API. The removes a long standing FIXME and this removes a flicker where the backlight brightness would jump to 100% when i915 loads even if using the fastset path. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 3 +- drivers/gpu/drm/i915/display/intel_panel.c| 73 +-- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 24ea4a7b6dde..48afb2925271 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -223,7 +224,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; - int pwm_period_ns; + struct pwm_state pwm_state; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index cb28b9908ca4..a0f76343f381 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -592,10 +592,11 @@ static u32 bxt_get_backlight(struct intel_connector *connector) static u32 pwm_get_backlight(struct intel_connector *connector) { struct intel_panel *panel = >panel; - int duty_ns; + int duty_ns, period_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); + period_ns = pwm_get_period(panel->backlight.pwm); + return DIV_ROUND_UP(duty_ns * 100, period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -669,10 +670,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, - panel->backlight.pwm_period_ns); + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void @@ -841,10 +842,8 @@ static void pwm_disable_backlight(const struct drm_connector_state *old_conn_sta struct intel_connector *connector = to_intel_connector(old_conn_state->connector); struct intel_panel *panel = >panel; - /* Disable the backlight */ - intel_panel_actually_set_backlight(old_conn_state, 0); - usleep_range(2000, 3000); - pwm_disable(panel->backlight.pwm); + panel->backlight.pwm_state.enabled = false; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state) @@ -1176,9 +1175,14 @@ static void pwm_enable_backlight(const struct intel_crtc_state *crtc_state, { struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = >panel; + int level = panel->backlight.level; - pwm_enable(panel->backlight.pwm); - intel_panel_actually_set_backlight(conn_state, panel->backlight.level); + level = intel_panel_compute_brightness(connector, level); + + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + panel->backlight.pwm_state.enabled = true; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state, @@ -1897,8 +1901,7 @@ static int pwm_setup_backlight(struct intel_connector *connector, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_panel *panel = >panel; const char *desc; - u32 level, ns; - int retval; + u32 level; /* Get the right PWM chip for DSI backlight according to VBT */ if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) { @@ -1916,36 +1919,30 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } - panel->backlight.pwm_period_ns = NSEC_PER_SEC / -get_vbt_pwm_freq(dev_priv); - - /* -
[PATCH v2 13/15] drm/i915: panel: Honor the VBT PWM frequency for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the period-time passed to pwm_config() to 21333 ns. I suspect this was done because many VBTs set the PWM frequency to 200 which corresponds to a period-time of 500 ns, which greatly exceeds the PWM_MAX_PERIOD_NS define in the Crystal Cove PMIC PWM driver, which used to be 21333. This PWM_MAX_PERIOD_NS define was actually based on a bug in the PWM driver where its period and duty-cycle times where off by a factor of 256. Due to this bug the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 would result in the PWM driver using its divider of 128, which would result in a PWM output frequency of 600 Hz / 256 / 128 = 183 Hz. So actually pretty close to the default VBT value of 200 Hz. Now that this bug in the pwm-crc driver is fixed, we can actually use the VBT defined frequency. This is important because: a) With the pwm-crc driver fixed it will now translate the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 ns / 46 Khz to a PWM output frequency of 23 KHz (the max it can do). b) The pwm-lpss driver used on many models has always honored the 21333 ns / 46 Khz request Some panels do not like such high output frequencies. E.g. on a Terra Pad 1061 tablet, using the LPSS PWM controller, the backlight would go from off to max, when changing the sysfs backlight brightness value from 90-100%, anything under aprox. 90% would turn the backlight fully off. Honoring the VBT specified PWM frequency will also hopefully fix the various bug reports which we have received about users perceiving the backlight to flicker after a suspend/resume cycle. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 1 + drivers/gpu/drm/i915/display/intel_panel.c| 19 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b24266c624fa..24ea4a7b6dde 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -223,6 +223,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; + int pwm_period_ns; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 8efdd9f08a08..14e611c92194 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -40,8 +40,6 @@ #include "intel_dsi_dcs_backlight.h" #include "intel_panel.h" -#define CRC_PMIC_PWM_PERIOD_NS 21333 - void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -597,7 +595,7 @@ static u32 pwm_get_backlight(struct intel_connector *connector) int duty_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); + return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -671,9 +669,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); + pwm_config(panel->backlight.pwm, duty_ns, + panel->backlight.pwm_period_ns); } static void @@ -1917,6 +1916,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } + panel->backlight.pwm_period_ns = NSEC_PER_SEC / +get_vbt_pwm_freq(dev_priv); + /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. @@ -1926,9 +1928,10 @@ static int pwm_setup_backlight(struct intel_connector *connector, panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ level = intel_panel_compute_brightness(connector, 100); - ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - retval = pwm_config(panel->backlight.pwm, ns, CRC_PMIC_PWM_PERIOD_NS); + retval = pwm_config(panel->backlight.pwm, ns, +
[PATCH v2 06/15] pwm: crc: Fix period / duty_cycle times being off by a factor of 256
While looking into adding atomic-pwm support to the pwm-crc driver I noticed something odd, there is a PWM_BASE_CLK define of 6 MHz and there is a clock-divider which divides this with a value between 1-128, and there are 256 duty-cycle steps. The pwm-crc code before this commit assumed that a clock-divider setting of 1 means that the PWM output is running at 6 MHZ, if that is true, where do these 256 duty-cycle steps come from? This would require an internal frequency of 256 * 6 MHz = 1.5 GHz, that seems unlikely for a PMIC which is using a silicon process optimized for power-switching transistors. It is way more likely that there is an 8 bit counter for the duty cycle which acts as an extra fixed divider wrt the PWM output frequency. The main user of the pwm-crc driver is the i915 GPU driver which uses it for backlight control. Lets compare the PWM register values set by the video-BIOS (the GOP), assuming the extra fixed divider is present versus the PWM frequency specified in the Video-BIOS-Tables: Device: PWM Hz set by BIOS PWM Hz specified in VBT Asus T100TA 200 200 Asus T100HA 200 200 Lenovo Miix 2 8 23437 2 Toshiba WT8-A 23437 2 So as we can see if we assume the extra division by 256 then the register values set by the GOP are an exact match for the VBT values, where as otherwise the values would be of by a factor of 256. This commit fixes the period / duty_cycle calculations to take the extra division by 256 into account. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 272eeb071147..43fc912c1fe9 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -21,8 +21,10 @@ #define PWM_MAX_LEVEL 0xFF -#define PWM_BASE_CLK 600 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 21333/* 46.875KHz */ +#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ +#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ + +#define NSEC_PER_MHZ 1000 /** * struct crystalcove_pwm - Crystal Cove PWM controller @@ -72,7 +74,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); - clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC; + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 14/15] drm/i915: panel: Honor the VBT PWM min setting for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the minimum allowed PWM level to 0. But several of these devices specify a non 0 minimum setting in their VBT. Change pwm_setup_backlight() to use get_backlight_min_vbt() to get the minimum level. Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 14e611c92194..cb28b9908ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1925,8 +1925,8 @@ static int pwm_setup_backlight(struct intel_connector *connector, */ pwm_apply_args(panel->backlight.pwm); - panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ + panel->backlight.min = get_backlight_min_vbt(connector); level = intel_panel_compute_brightness(connector, 100); ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); @@ -1941,8 +1941,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, level = DIV_ROUND_UP(pwm_get_duty_cycle(panel->backlight.pwm) * 100, panel->backlight.pwm_period_ns); - panel->backlight.level = - intel_panel_compute_brightness(connector, level); + level = intel_panel_compute_brightness(connector, level); + panel->backlight.level = clamp(level, panel->backlight.min, + panel->backlight.max); panel->backlight.enabled = panel->backlight.level != 0; drm_info(_priv->drm, "Using %s PWM for LCD backlight control\n", -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 08/15] pwm: crc: Fix period changes not having any effect
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register I strongly suspect that the BACKLIGHT_EN register at address 0x51 really controls a separate output-only GPIO which is connected to the LCD panels backlight-enable input. Like how the PANEL_EN register at address 0x52 controls an output-only GPIO which is earmarked for the LCD panel's enable pin. If this is correct then this GPIO should really be added to the gpio-crystalcove.c driver and the PWM driver should stop poking it. But I've been unable to come up with a definitive answer here, so I'm keeping this as is for now. As the comment in the old code already indicates we must disable the PWM before we can change the clock divider. But the crc_pwm_disable() and crc_pwm_enable() calls the old code make for this only change the BACKLIGHT_EN register; and the value of that register does not matter for changing the period / the divider. What does matter is that the PWM_OUTPUT_ENABLE bit must be cleared before a new value can be written. This commit modifies crc_pwm_config() to clear PWM_OUTPUT_ENABLE instead when changing the period, so that period changes actually work. Note this fix will cause a significant behavior change on some devices using the CRC PWM output to drive their backlight. Before the PWM would always run with the output frequency configured by the BIOS at boot, now the period time specified by the i915 driver will actually be honored. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 8 +++- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 5ba2a65c524c..ef49a6e3c4d6 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -72,8 +72,9 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, if (pwm_get_period(pwm) != period_ns) { int clk_div; - /* changing the clk divisor, need to disable fisrt */ - crc_pwm_disable(c, pwm); + /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); /* clk_div 1 - 128, maps to register values 0-127 */ if (clk_div > 0) @@ -81,9 +82,6 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); - - /* enable back */ - crc_pwm_enable(c, pwm); } /* change the pwm duty cycle */ -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 07/15] pwm: crc: Fix off-by-one error in the clock-divider calculations
The CRC PWM controller has a clock-divider which divides the clock with a value between 1-128. But as can seen from the PWM_DIV_CLK_xxx defines, this range maps to a register value of 0-127. So after calculating the clock-divider we must subtract 1 to get the register value, unless the requested frequency was so high that the calculation has already resulted in a (rounded) divider value of 0. Note that before this fix, setting a period of PWM_MAX_PERIOD_NS which corresponds to the max. divider value of 128 could have resulted in a bug where the code would use 128 as divider-register value which would have resulted in an actual divider value of 0 (and the enable bit being set). A rounding error stopped this bug from actually happen. This same rounding error means that after the subtraction of 1 it is impossible to set the divider to 128. Also bump PWM_MAX_PERIOD_NS by 1 ns to allow setting a divider of 128 (register-value 127). Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 43fc912c1fe9..5ba2a65c524c 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -22,7 +22,7 @@ #define PWM_MAX_LEVEL 0xFF #define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ +#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */ #define NSEC_PER_MHZ 1000 @@ -75,6 +75,9 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 04/15] pwm: lpss: Fix off by one error in base_unit math in pwm_lpss_prepare()
According to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. So assuming e.g. a 16 bit counter this means that if base_unit is set to 1, after 65535 input clock-cycles the counter has been increased from 0 to 65535 and it will overflow on the next cycle, so it will overflow after every 65536 clock cycles and thus the calculations done in pwm_lpss_prepare() should use 65536 and not 65535. This commit fixes this. Note this also aligns the calculations in pwm_lpss_prepare() with those in pwm_lpss_get_state(). Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index cae74ce61654..a764e062103b 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -93,7 +93,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, * The equation is: * base_unit = round(base_unit_range * freq / c) */ - base_unit_range = BIT(lpwm->info->base_unit_bits) - 1; + base_unit_range = BIT(lpwm->info->base_unit_bits); freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); @@ -112,7 +112,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; - ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); + ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 01/15] ACPI / LPSS: Resume Cherry Trail PWM controller in no-irq phase
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets poked from the _PS0 method of the graphics-card device: Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ If (((Local0 & 0x03) == 0x03)) { PSAT &= 0xFFFC Local1 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ RSTA = Zero RSTF = Zero RSTA = One RSTF = One PWMB |= 0xC000 PWMC = PWMB /* \_SB_.PCI0.GFX0.PWMB */ } Where PSAT is the power-status register of the PWM controller, so if it is in D3 when the GFX0 device's PS0 method runs then it will turn it on and restore the PWM ctrl register value it saved from its PS3 handler. Note not only does it restore it, it ors it with 0xC000 turning it on at a time where we may not want it to get turned on at all. The pwm_get call which the i915 driver does to get a reference to the PWM controller, already adds a device-link making the GFX0 device a consumer of the PWM device. So it should already have been resumed when the above AML runs and the AML should thus not do its undesirable poking of the PWM controller register. But the PCI core powers on PCI devices in the no-irq resume phase and thus calls the troublesome PS0 method in the no-irq resume phase. Where as LPSS devices by default are resumed in the early resume phase. This commit sets the resume_from_noirq flag in the bsw_pwm_dev_desc struct, so that Cherry Trail PWM controllers will be resumed in the no-irq phase. Together with the device-link added by the pwm-get this ensures that the PWM controller will be on when the troublesome PS0 method runs, which stops it from poking the PWM controller. Signed-off-by: Hans de Goede --- drivers/acpi/acpi_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 5e2bfbcf526f..67892fc0b822 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -257,6 +257,7 @@ static const struct lpss_device_desc bsw_pwm_dev_desc = { .flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, .prv_offset = 0x800, .setup = bsw_pwm_setup, + .resume_from_noirq = true, }; static const struct lpss_device_desc byt_uart_dev_desc = { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 02/15] ACPI / LPSS: Save Cherry Trail PWM ctx registers only once (at activation)
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets turned off from the _PS3 method of the graphics-card dev: Method (_PS3, 0, Serialized) // _PS3: Power State 3 { ... PWMB = PWMC /* \_SB_.PCI0.GFX0.PWMC */ PSAT |= 0x03 Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ ... } Where PSAT is the power-status register of the PWM controller. Since the i915 driver will do a pwm_get on the pwm device as it uses it to control the LCD panel backlight, there is a device-link marking the i915 device as a consumer of the pwm device. So that the PWM controller will always be suspended after the i915 driver suspends (which is the right thing to do). This causes the above GFX0 PS3 AML code to run before acpi_lpss.c calls acpi_lpss_save_ctx(). So on these devices the PWM controller will already be off when acpi_lpss_save_ctx() runs. This causes it to read/save all 1-s (0x) as ctx register values. When these bogus values get restored on resume the PWM controller actually keeps working, since most bits are reserved, but this does set bit 3 of the LPSS General purpose register, which for the PWM controller has the following function: "This bit is re-used to support 32kHz slow mode. Default is 19.2MHz as PWM source clock". This causes the clock of the PWM controller to switch from 19.2MHz to 32KHz, which is a slow-down of a factor 600. Surprisingly enough so far there have been few bug reports about this. This is likely because the i915 driver was hardcoding the PWM frequency to 46 KHz, which divided by 600 would result in a PWM frequency of approx. 78 Hz, which mostly still works fine. There are some bug reports about the LCD backlight flickering after suspend/resume which are likely caused by this issue. But with the upcoming patch-series to finally switch the i915 drivers code for external PWM controllers to use the atomic API and to honor the PWM frequency specified in the video BIOS (VBT), this becomes a much bigger problem. On most cases the VBT specifies either 200 Hz or 20 KHz as PWM frequency, which with the mentioned issue ends up being either 1/3 Hz, where the backlight actually visible blinks on and off every 3s, or in 33 Hz and horrible flickering of the backlight. There are a number of possible solutions to this problem: 1. Make acpi_lpss_save_ctx() run before GFX0._PS3 Pro: Clean solution from pov of not medling with save/restore ctx code Con: As mentioned the current ordering is the right thing to do Con: Requires assymmetry in at what suspend/resume phase we do the save vs restore, requiring more suspend/resume ordering hacks in already convoluted acpi_lpss.c suspend/resume code. 2. Do some sort of save once mode for the LPSS ctx Pro: Reasonably clean Con: Needs a new LPSS flag + code changes to handle the flag 3. Detect we have failed to save the ctx registers and do not restore them Pro: Not PWM specific, might help with issues on other LPSS devices too Con: If we can get away with not restoring the ctx why bother with it at all? 4. Do not save the ctx for CHT PWM controllers Pro: Clean, as simple as dropping a flag? Con: Not so simple as dropping a flag, needs a new flag to ensure that we still do lpss_deassert_reset() on device activation. 5. Make the pwm-lpss code fixup the LPSS-context registers Pro: Keeps acpi_lpss.c code clean Con: Moves knowledge of LPSS-context into the pwm-lpss.c code 1 and 5 both do not seem to be a desirable way forward. 3 and 4 seem ok, but they both assume that restoring the LPSS-context registers is not necessary. I have done a couple of test and those do show that restoring the LPSS-context indeed does not seem to be necessary on devices using s2idle suspend (and successfully reaching S0i3). But I have no hardware to test deep / S3 suspend. So I'm not sure that not restoring the context is safe. That leaves solution 2, which is about as simple / clean as 3 and 4, so this commit fixes the described problem by implementing a new LPSS_SAVE_CTX_ONCE flag and setting that for the CHT PWM controllers. Signed-off-by: Hans de Goede --- Changes in v2: - Move #define LPSS_SAVE_CTX_ONCE define to group it with LPSS_SAVE_CTX --- drivers/acpi/acpi_lpss.c | 21 + 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 67892fc0b822..a8d7d83ac761 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -67,7 +67,15 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_CLK_DIVIDER BIT(2) #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) -#define LPSS_NO_D3_DELAY BIT(5) +/* + * For some devices the DSDT AML code for another device turns off the device + * before our suspend h
[PATCH v2 00/15] pwm/i915: Convert pwm-crc and i915 driver's PWM code to use the atomic PWM API
Hi All, Here is v2 dropping a debugging-patch which I accidentally kept for v1 and addressing a minor review remark from Andy for the 2nd patch. This patch series converts the i915 driver's code for controlling the panel's backlight with an external PWM controller to use the atomic PWM API. Initially the plan was for this series to consist of 2 parts: 1. convert the pwm-crc driver to support the atomic PWM API and 2. convert the i915 driver's PWM code to use the atomic PWM API. But during testing I've found a number of bugs in the pwm-lpss and I found that the acpi_lpss code needs some special handling because of some ugliness found in most Cherry Trail DSDTs. So now this series has grown somewhat large and consists of 4 parts: 1. acpi_lpss fixes workarounds for Cherry Trail DSTD nastiness 2. various fixes to the pwm-lpss driver 3. convert the pwm-crc driver to support the atomic PWM API and 4. convert the i915 driver's PWM code to use the atomic PWM API So we need to discuss how to merge this (once it passes review). Although the inter-dependencies are only runtime I still think we should make sure that 1-3 are in the drm-intel-next-queued (dinq) tree before merging the i915 changes. Both to make sure that the intel-gfx CI system does not become unhappy and for bisecting reasons. The involved acpi_lpss and pwm drivers do not see a whole lot of churn, so we could just merge everything through dinq, or we could use immutable branch and merge those into dinq. So Rafael and Thierry, can I either get your Acked-by for directly merging this into dinq, or can you provide an immutable branch with these patches? This series has been tested (and re-tested after adding various bug-fixes) extensively. It has been tested on the following devices: -Asus T100TA BYT + CRC-PMIC PWM -Toshiba WT8-A BYT + CRC-PMIC PWM -Thundersoft TS178 BYT + CRC-PMIC PWM, inverse PWM -Asus T100HA CHT + CRC-PMIC PWM -Terra Pad 1061 BYT + LPSS PWM -Trekstor Twin 10.1 BYT + LPSS PWM -Asus T101HA CHT + CRC-PMIC PWM -GPD Pocket CHT + CRC-PMIC PWM Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 03/15] pwm: lpss: Add range limit check for the base_unit register value
When the user requests a high enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value of 0. But according to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. Adding 0 to the counter is a no-op. The data-sheet even explicitly states that writing 0 to the base_unit bits will result in the PWM outputting a continuous 0 signal. base_unit values > (base_unit_range / 256), or iow base_unit values using the 8 most significant bits, cause loss of resolution of the duty-cycle. E.g. assuming a base_unit_range of 65536 steps, then a base_unit value of 768 (256 * 3), limits the duty-cycle resolution to 65536 / 768 = 85 steps. Clamp the max base_unit value to base_unit_range / 32 to ensure a duty-cycle resolution of at least 32 steps. This limits the maximum output frequency to 600 KHz / 780 KHz depending on the base clock. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 9d965ffe66d1..cae74ce61654 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -97,6 +97,14 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); + /* +* base_unit must not be 0 and for values > (base_unit_range / 256) +* (values using the 8 most significant bits) the duty-cycle resolution +* degrades. Clamp the maximum value to base_unit_range / 32 which +* leaves a duty-cycle resolution of 32 steps. +*/ + base_unit = clamp_t(unsigned long long, base_unit, 1, + base_unit_range / 32); on_time_div = 255ULL * duty_ns; do_div(on_time_div, period_ns); @@ -105,7 +113,6 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); - base_unit &= base_unit_range; ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: pwm/i915: Convert pwm-crc and i915 driver's PWM code to use the atomic PWM API
Hi All, I forgot the [PATCH 0/16] part of the subject here and I accidentally left a patch adding some debugging printk-s in the series. I will send out a v2 addressing this. Regards, Hans On 6/6/20 10:25 PM, Hans de Goede wrote: Hi All, This patch series converts the i915 driver's cpde for controlling the panel's backlight with an external PWM controller to use the atomic PWM API. Initially the plan was for this series to consist of 2 parts: 1. convert the pwm-crc driver to support the atomic PWM API and 2. convert the i915 driver's PWM code to use the atomic PWM API. But during testing I've found a number of bugs in the pwm-lpss and I found that the acpi_lpss code needs some special handling because of some ugliness found in most Cherry Trail DSDTs. So now this series has grown somewhat large and consists of 4 parts: 1. acpi_lpss fixes workarounds for Cherry Trail DSTD nastiness 2. various fixes to the pwm-lpss driver 3. convert the pwm-crc driver to support the atomic PWM API and 4. convert the i915 driver's PWM code to use the atomic PWM API So we need to discuss how to merge this (once it passes review). Although the inter-dependencies are only runtime I still think we should make sure that 1-3 are in the drm-intel-next-queued (dinq) tree before merging the i915 changes. Both to make sure that the intel-gfx CI system does not become unhappy and for bisecting reasons. The involved acpi_lpss and pwm drivers do not see a whole lot of churn, so we could just merge everything through dinq, or we could use immutable branch and merge those into dinq. So Rafael and Thierry, can I either get your Acked-by for directly merging this into dinq, or can you provide an immutable branch with these patches? This series has been tested (and re-tested after adding various bug-fixes) extensively. It has been tested on the following devices: -Asus T100TABYT + CRC-PMIC PWM -Toshiba WT8-A BYT + CRC-PMIC PWM -Thundersoft TS178 BYT + CRC-PMIC PWM, inverse PWM -Asus T100HACHT + CRC-PMIC PWM -Terra Pad 1061 BYT + LPSS PWM -Trekstor Twin 10.1 BYT + LPSS PWM -Asus T101HACHT + CRC-PMIC PWM -GPD Pocket CHT + CRC-PMIC PWM Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 02/16] ACPI / LPSS: Save Cherry Trail PWM ctx registers only once (at activation)
Hi, On 6/7/20 7:03 PM, Andy Shevchenko wrote: On Sat, Jun 06, 2020 at 10:25:47PM +0200, Hans de Goede wrote: The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets turned off from the _PS3 method of the graphics-card dev: Method (_PS3, 0, Serialized) // _PS3: Power State 3 { ... PWMB = PWMC /* \_SB_.PCI0.GFX0.PWMC */ PSAT |= 0x03 Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ ... } Where PSAT is the power-status register of the PWM controller. Since the i915 driver will do a pwm_get on the pwm device as it uses it to control the LCD panel backlight, there is a device-link marking the i915 device as a consumer of the pwm device. So that the PWM controller will always be suspended after the i915 driver suspends (which is the right thing to do). This causes the above GFX0 PS3 AML code to run before acpi_lpss.c calls acpi_lpss_save_ctx(). So on these devices the PWM controller will already be off when acpi_lpss_save_ctx() runs. This causes it to read/save all 1-s (0x) as ctx register values. When these bogus values get restored on resume the PWM controller actually keeps working, since most bits are reserved, but this does set bit 3 of the LPSS General purpose register, which for the PWM controller has the following function: "This bit is re-used to support 32kHz slow mode. Default is 19.2MHz as PWM source clock". This causes the clock of the PWM controller to switch from 19.2MHz to 32KHz, which is a slow-down of a factor 600. Suprisingly enough so far there have been few bug reports about this. This is likely because the i915 driver was hardcoding the PWM frequency to 46 KHz, which divided by 600 would result in a PWM frequency of approx. 78 Hz, which mostly still works fine. There are some bug reports about the LCD backlight flickering after suspend/resume which are likely caused by this issue. But with the upcoming patch-series to finally switch the i915 drivers code for external PWM controllers to use the atomic API and to honor the PWM frequency specified in the video BIOS (VBT), this becomes a much bigger problem. On most cases the VBT specifies either 200 Hz or 20 KHz as PWM frequency, which with the mentioned issue ends up being either 1/3 Hz, where the backlight actually visible blinks on and off every 3s, or in 33 Hz and horrible flickering of the backlight. There are a number of possible solutions to this problem: 1. Make acpi_lpss_save_ctx() run before GFX0._PS3 Pro: Clean solution from pov of not medling with save/restore ctx code Con: As mentioned the current ordering is the right thing to do Con: Requires assymmetry in at what suspend/resume phase we do the save vs restore, requiring more suspend/resume ordering hacks in already convoluted acpi_lpss.c suspend/resume code. 2. Do some sort of save once mode for the LPSS ctx Pro: Reasonably clean Con: Needs a new LPSS flag + code changes to handle the flag 3. Detect we have failed to save the ctx registers and do not restore them Pro: Not PWM specific, might help with issues on other LPSS devices too Con: If we can get away with not restoring the ctx why bother with it at all? 4. Do not save the ctx for CHT PWM controllers Pro: Clean, as simple as dropping a flag? Con: Not so simple as dropping a flag, needs a new flag to ensure that we still do lpss_deassert_reset() on device activation. 5. Make the pwm-lpss code fixup the LPSS-context registers Pro: Keeps acpi_lpss.c code clean Con: Moves knowledge of LPSS-context into the pwm-lpss.c code 1 and 5 both do not seem to be a desirable way forward. 3 and 4 seem ok, but they both assume that restoring the LPSS-context registers is not necessary. I have done a couple of test and those do show that restoring the LPSS-context indeed does not seem to be necessary on devices using s2idle suspend (and successfully reaching S0i3). But I have no hardware to test deep / S3 suspend. So I'm not sure that not restoring the context is safe. That leaves solution 2, which is about as simple / clean as 3 and 4, so this commit fixes the described problem by implementing a new LPSS_SAVE_CTX_ONCE flag and setting that for the CHT PWM controllers. Signed-off-by: Hans de Goede --- drivers/acpi/acpi_lpss.c | 19 --- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 67892fc0b822..26933e6b7b8c 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -68,6 +68,14 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) #define LPSS_NO_D3_DELAY BIT(5) +/* + * For some devices the DSDT AML code for another device turns off the device + * before our suspend hand
[PATCH 15/16] drm/i915: panel: Honor the VBT PWM min setting for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the minimum allowed PWM level to 0. But several of these devices specify a non 0 minimum setting in their VBT. Change pwm_setup_backlight() to use get_backlight_min_vbt() to get the minimum level. Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 14e611c92194..cb28b9908ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1925,8 +1925,8 @@ static int pwm_setup_backlight(struct intel_connector *connector, */ pwm_apply_args(panel->backlight.pwm); - panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ + panel->backlight.min = get_backlight_min_vbt(connector); level = intel_panel_compute_brightness(connector, 100); ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); @@ -1941,8 +1941,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, level = DIV_ROUND_UP(pwm_get_duty_cycle(panel->backlight.pwm) * 100, panel->backlight.pwm_period_ns); - panel->backlight.level = - intel_panel_compute_brightness(connector, level); + level = intel_panel_compute_brightness(connector, level); + panel->backlight.level = clamp(level, panel->backlight.min, + panel->backlight.max); panel->backlight.enabled = panel->backlight.level != 0; drm_info(_priv->drm, "Using %s PWM for LCD backlight control\n", -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 13/16] drm/i915: panel: Add get_vbt_pwm_freq() helper
Factor the code which checks and drm_dbg_kms-s the VBT PWM frequency out of get_backlight_max_vbt(). This is a preparation patch for honering the VBT PWM frequency for devices which use an external PWM controller (devices using pwm_setup_backlight()). Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_panel.c | 27 ++ 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 3c5056dbf607..8efdd9f08a08 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -1543,18 +1543,9 @@ static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul); } -static u32 get_backlight_max_vbt(struct intel_connector *connector) +static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_panel *panel = >panel; u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz; - u32 pwm; - - if (!panel->backlight.hz_to_pwm) { - drm_dbg_kms(_priv->drm, - "backlight frequency conversion not supported\n"); - return 0; - } if (pwm_freq_hz) { drm_dbg_kms(_priv->drm, @@ -1567,6 +1558,22 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector) pwm_freq_hz); } + return pwm_freq_hz; +} + +static u32 get_backlight_max_vbt(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_panel *panel = >panel; + u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv); + u32 pwm; + + if (!panel->backlight.hz_to_pwm) { + drm_dbg_kms(_priv->drm, + "backlight frequency conversion not supported\n"); + return 0; + } + pwm = panel->backlight.hz_to_pwm(connector, pwm_freq_hz); if (!pwm) { drm_dbg_kms(_priv->drm, -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 04/16] pwm: lpss: Fix off by one error in base_unit math in pwm_lpss_prepare()
According to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. So assuming e.g. a 16 bit counter this means that if base_unit is set to 1, after 65535 input clock-cycles the counter has been increased from 0 to 65535 and it will overflow on the next cycle, so it will overflow after every 65536 clock cycles and thus the calculations done in pwm_lpss_prepare() should use 65536 and not 65535. This commit fixes this. Note this also aligns the calculations in pwm_lpss_prepare() with those in pwm_lpss_get_state(). Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index cae74ce61654..a764e062103b 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -93,7 +93,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, * The equation is: * base_unit = round(base_unit_range * freq / c) */ - base_unit_range = BIT(lpwm->info->base_unit_bits) - 1; + base_unit_range = BIT(lpwm->info->base_unit_bits); freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); @@ -112,7 +112,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; - ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); + ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 01/16] ACPI / LPSS: Resume Cherry Trail PWM controller in no-irq phase
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets poked from the _PS0 method of the graphics-card device: Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ If (((Local0 & 0x03) == 0x03)) { PSAT &= 0xFFFC Local1 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ RSTA = Zero RSTF = Zero RSTA = One RSTF = One PWMB |= 0xC000 PWMC = PWMB /* \_SB_.PCI0.GFX0.PWMB */ } Where PSAT is the power-status register of the PWM controller, so if it is in D3 when the GFX0 device's PS0 method runs then it will turn it on and restore the PWM ctrl register value it saved from its PS3 handler. Note not only does it restore it, it ors it with 0xC000 turning it on at a time where we may not want it to get turned on at all. The pwm_get call which the i915 driver does to get a reference to the PWM controller, already adds a device-link making the GFX0 device a consumer of the PWM device. So it should already have been resumed when the above AML runs and the AML should thus not do its undesirable poking of the PWM controller register. But the PCI core powers on PCI devices in the no-irq resume phase and thus calls the troublesome PS0 method in the no-irq resume phase. Where as LPSS devices by default are resumed in the early resume phase. This commit sets the resume_from_noirq flag in the bsw_pwm_dev_desc struct, so that Cherry Trail PWM controllers will be resumed in the no-irq phase. Together with the device-link added by the pwm-get this ensures that the PWM controller will be on when the troublesome PS0 method runs, which stops it from poking the PWM controller. Signed-off-by: Hans de Goede --- drivers/acpi/acpi_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 5e2bfbcf526f..67892fc0b822 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -257,6 +257,7 @@ static const struct lpss_device_desc bsw_pwm_dev_desc = { .flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, .prv_offset = 0x800, .setup = bsw_pwm_setup, + .resume_from_noirq = true, }; static const struct lpss_device_desc byt_uart_dev_desc = { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 06/16] pwm: lpss: Add debug prints, test patch for moving i915 to atomic PWM
Add debug prints, test patch for moving i915 to atomic PWM. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 2cb0e2a9c08c..c1f8e6da0cd7 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -119,6 +119,8 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, ctrl |= PWM_ENABLE; if (orig_ctrl != ctrl) { + dev_err(pwm->chip->dev, "prepare 0x%08x -> 0x%08lx\n", + orig_ctrl, ctrl | PWM_SW_UPDATE); pwm_lpss_write(pwm, ctrl); pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE); } @@ -126,8 +128,15 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond) { - if (cond) - pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); + if (cond) { + u32 orig_ctrl, ctrl; + + orig_ctrl = ctrl = pwm_lpss_read(pwm); + ctrl |= PWM_ENABLE; + dev_err(pwm->chip->dev, "enable 0x%08x -> 0x%08x\n", + orig_ctrl, ctrl); + pwm_lpss_write(pwm, ctrl); + } } static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -200,6 +209,9 @@ static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->enabled = !!(ctrl & PWM_ENABLE); pm_runtime_put(chip->dev); + + dev_err(pwm->chip->dev, "initial state 0x%08x period %d duty_cycle %d enabled %d\n", + ctrl, state->period, state->duty_cycle, state->enabled); } static const struct pwm_ops pwm_lpss_ops = { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 11/16] pwm: crc: Implement apply() method to support the new atomic PWM API
Replace the enable, disable and config pwm_ops with an apply op, to support the new atomic PWM API. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 107 +++--- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 53734bcf67e1..58c7e9ef7278 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -41,70 +41,81 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) return container_of(pc, struct crystalcove_pwm, chip); } -static int crc_pwm_calc_clk_div(int period_ns) +static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +const struct pwm_state *state) { - int clk_div; - - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); - /* clk_div 1 - 128, maps to register values 0-127 */ - if (clk_div > 0) - clk_div--; - - return clk_div; -} - -static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); - - return 0; -} - -static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); - - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); -} - -static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + int err, clk_div, level, pwm_output_enable; struct device *dev = crc_pwm->chip.dev; - int level; - if (period_ns > PWM_MAX_PERIOD_NS) { + if (state->period > PWM_MAX_PERIOD_NS) { dev_err(dev, "un-supported period_ns\n"); return -EINVAL; } - if (pwm_get_period(pwm) != period_ns) { - int clk_div = crc_pwm_calc_clk_div(period_ns); + if (state->polarity != PWM_POLARITY_NORMAL) + return -ENOTSUPP; + + if (pwm_is_enabled(pwm) && !state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + if (err) { + dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err); + return err; + } + } + + if (pwm_get_duty_cycle(pwm) != state->duty_cycle || + pwm_get_period(pwm) != state->period) { + level = state->duty_cycle * PWM_MAX_LEVEL / state->period; + err = regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (err) { + dev_err(dev, "Error writing PWM0_DUTY_CYCLE %d\n", err); + return err; + } + } + + if (pwm_is_enabled(pwm) && state->enabled && + pwm_get_period(pwm) != state->period) { /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, - clk_div | PWM_OUTPUT_ENABLE); + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } } - /* change the pwm duty cycle */ - level = duty_ns * PWM_MAX_LEVEL / period_ns; - regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + if (pwm_get_period(pwm) != state->period || + pwm_is_enabled(pwm) != state->enabled) { + clk_div = PWM_BASE_CLK_MHZ * state->period / + (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; + + pwm_output_enable = state->enabled ? PWM_OUTPUT_ENABLE : 0; + + err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, + clk_div | pwm_output_enable); + if (err) { + dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err); + return err; + } + } + + if (!pwm_is_enabled(pwm) && state->enabled) { + err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); + if (err) { +
[PATCH 05/16] pwm: lpss: Set SW_UPDATE bit when enabling the PWM
On the LPSS PWM controller found on Bay Trail (BYT) and Cherry Trail (CHT) platforms, the following sequence results in an output duty-cycle of 100% independent of what the duty-cycle requested in the ctrl-reg is: 1. Clear ENABLE bit in ctrl register 2. Let the machine reach a S0i3 low power state 3. Set the ENABLE bit in the ctrl register The LPSS PWM controller has a mechanism where the ctrl register value and the actual base-unit and on-time-div values used are latched. When software sets the SW_UPDATE bit then at the end of the current PWM cycle, the new values from the ctrl-register will be latched into the actual registers, and the SW_UPDATE bit will be cleared. Note on BYT and CHT the ENABLE bit must be set before waiting for the SW_UPDATE bit to clear, otherwise the SW_UPDATE bit will never clear (this is indicated in the pwm-lpss.c code by lpwm->info->bypass being false). My theory about why this is happening is that when we hit S0i3 the part which holds the latched values gets turned off and when its turned back on again at least the on-time-div value has been lost and gets reset to 0 which corresponds to an output duty-cycle of 100%. Testing has shown that setting the SW_UPDATE bit to request latching the ctrl-register values into the actual registers (again) fixes this, confirming this theory. In the past there have been issues where setting the SW_UPDATE bit when nothing has changed would lead to the next ctrl register changing being ignored, see commit 2153bbc12f77 ("pwm: lpss: Only set update bit if we are actually changing the settings"), so we should only set the SW_UPDATE bit when actually changing the ENABLE bit from 0 to 1. When looking into how to fix this I noticed that on platforms where lpwm->info->bypass == false we unnecessarily do 2 read-modify-write cycles of the ctrl register, one to set the base-unit and on-time-div, immediately followed by another to set the ENABLE bit. This commit fixes the 100% duty cycle issue by folding the setting of the ENABLE bit into pwm_lpss_prepare(), which already checks if any bits in the ctrl-register have actually changed and if that is the case then sets the SW_UPDATE bit. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index a764e062103b..2cb0e2a9c08c 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -80,7 +80,7 @@ static inline int pwm_lpss_is_updating(struct pwm_device *pwm) } static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, -int duty_ns, int period_ns) +int duty_ns, int period_ns, bool enable) { unsigned long long on_time_div; unsigned long c = lpwm->info->clk_rate, base_unit_range; @@ -115,6 +115,8 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; + if (enable) + ctrl |= PWM_ENABLE; if (orig_ctrl != ctrl) { pwm_lpss_write(pwm, ctrl); @@ -142,8 +144,9 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, pm_runtime_put(chip->dev); return ret; } - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); - pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false); + pwm_lpss_prepare(lpwm, pwm, +state->duty_cycle, state->period, +lpwm->info->bypass == false); ret = pwm_lpss_wait_for_update(pwm); if (ret) { pm_runtime_put(chip->dev); @@ -154,7 +157,8 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, ret = pwm_lpss_is_updating(pwm); if (ret) return ret; - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); + pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, +state->period, false); return pwm_lpss_wait_for_update(pwm); } } else if (pwm_is_enabled(pwm)) { -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 07/16] pwm: crc: Fix period / duty_cycle times being off by a factor of 256
While looking into adding atomic-pwm support to the pwm-crc driver I noticed something odd, there is a PWM_BASE_CLK define of 6 MHz and there is a clock-divider which divides this with a value between 1-128, and there are 256 duty-cycle steps. The pwm-crc code before this commit assumed that a clock-divider setting of 1 means that the PWM output is running at 6 MHZ, if that is true, where do these 256 duty-cycle steps come from? This would require an internal frequency of 256 * 6 MHz = 1.5 GHz, that seems unlikely for a PMIC which is using a silicon process optimized for power-switching transistors. It is way more likely that there is an 8 bit counter for the duty cycle which acts as an extra fixed divider wrt the PWM output frequency. The main user of the pwm-crc driver is the i915 GPU driver which uses it for backlight control. Lets compare the PWM register values set by the video-BIOS (the GOP), assuming the extra fixed divider is present versus the PWM frequency specified in the Video-BIOS-Tables: Device: PWM Hz set by BIOS PWM Hz specified in VBT Asus T100TA 200 200 Asus T100HA 200 200 Lenovo Miix 2 8 23437 2 Toshiba WT8-A 23437 2 So as we can see if we assume the extra division by 256 then the register values set by the GOP are an exact match for the VBT values, where as otherwise the values would be of by a factor of 256. This commit fixes the period / duty_cycle calculations to take the extra division by 256 into account. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 272eeb071147..43fc912c1fe9 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -21,8 +21,10 @@ #define PWM_MAX_LEVEL 0xFF -#define PWM_BASE_CLK 600 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 21333/* 46.875KHz */ +#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ +#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ + +#define NSEC_PER_MHZ 1000 /** * struct crystalcove_pwm - Crystal Cove PWM controller @@ -72,7 +74,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); - clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC; + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 03/16] pwm: lpss: Add range limit check for the base_unit register value
When the user requests a high enough period ns value, then the calculations in pwm_lpss_prepare() might result in a base_unit value of 0. But according to the data-sheet the way the PWM controller works is that each input clock-cycle the base_unit gets added to a N bit counter and that counter overflowing determines the PWM output frequency. Adding 0 to the counter is a no-op. The data-sheet even explicitly states that writing 0 to the base_unit bits will result in the PWM outputting a continuous 0 signal. base_unit values > (base_unit_range / 256), or iow base_unit values using the 8 most significant bits, cause loss of resolution of the duty-cycle. E.g. assuming a base_unit_range of 65536 steps, then a base_unit value of 768 (256 * 3), limits the duty-cycle resolution to 65536 / 768 = 85 steps. Clamp the max base_unit value to base_unit_range / 32 to ensure a duty-cycle resolution of at least 32 steps. This limits the maximum output frequency to 600 KHz / 780 KHz depending on the base clock. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-lpss.c | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 9d965ffe66d1..cae74ce61654 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -97,6 +97,14 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); + /* +* base_unit must not be 0 and for values > (base_unit_range / 256) +* (values using the 8 most significant bits) the duty-cycle resolution +* degrades. Clamp the maximum value to base_unit_range / 32 which +* leaves a duty-cycle resolution of 32 steps. +*/ + base_unit = clamp_t(unsigned long long, base_unit, 1, + base_unit_range / 32); on_time_div = 255ULL * duty_ns; do_div(on_time_div, period_ns); @@ -105,7 +113,6 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, orig_ctrl = ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); - base_unit &= base_unit_range; ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 09/16] pwm: crc: Fix period changes not having any effect
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register I strongly suspect that the BACKLIGHT_EN register at address 0x51 really controls a separate output-only GPIO which is connected to the LCD panels backlight-enable input. Like how the PANEL_EN register at address 0x52 controls an output-only GPIO which is earmarked for the LCD panel's enable pin. If this is correct then this GPIO should really be added to the gpio-crystalcove.c driver and the PWM driver should stop poking it. But I've been unable to come up with a definitive answer here, so I'm keeping this as is for now. As the comment in the old code already indicates we must disable the PWM before we can change the clock divider. But the crc_pwm_disable() and crc_pwm_enable() calls the old code make for this only change the BACKLIGHT_EN register; and the value of that register does not matter for changing the period / the divider. What does matter is that the PWM_OUTPUT_ENABLE bit must be cleared before a new value can be written. This commit modifies crc_pwm_config() to clear PWM_OUTPUT_ENABLE instead when changing the period, so that period changes actually work. Note this fix will cause a significant behavior change on some devices using the CRC PWM output to drive their backlight. Before the PWM would always run with the output frequency configured by the BIOS at boot, now the period time specified by the i915 driver will actually be honored. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 8 +++- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 5ba2a65c524c..ef49a6e3c4d6 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -72,8 +72,9 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, if (pwm_get_period(pwm) != period_ns) { int clk_div; - /* changing the clk divisor, need to disable fisrt */ - crc_pwm_disable(c, pwm); + /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); /* clk_div 1 - 128, maps to register values 0-127 */ if (clk_div > 0) @@ -81,9 +82,6 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); - - /* enable back */ - crc_pwm_enable(c, pwm); } /* change the pwm duty cycle */ -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 14/16] drm/i915: panel: Honor the VBT PWM frequency for devs with an external PWM controller
So far for devices using an external PWM controller (devices using pwm_setup_backlight()), we have been hardcoding the period-time passed to pwm_config() to 21333 ns. I suspect this was done because many VBTs set the PWM frequency to 200 which corresponds to a period-time of 500 ns, which greatly exceeds the PWM_MAX_PERIOD_NS define in the Crystal Cove PMIC PWM driver, which used to be 21333. This PWM_MAX_PERIOD_NS define was actually based on a bug in the PWM driver where its period and duty-cycle times where off by a factor of 256. Due to this bug the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 would result in the PWM driver using its divider of 128, which would result in a PWM output frequency of 600 Hz / 256 / 128 = 183 Hz. So actually pretty close to the default VBT value of 200 Hz. Now that this bug in the pwm-crc driver is fixed, we can actually use the VBT defined frequency. This is important because: a) With the pwm-crc driver fixed it will now translate the hardcoded CRC_PMIC_PWM_PERIOD_NS value of 21333 ns / 46 Khz to a PWM output frequency of 23 KHz (the max it can do). b) The pwm-lpss driver used on many models has always honored the 21333 ns / 46 Khz request Some panels do not like such high output frequencies. E.g. on a Terra Pad 1061 tablet, using the LPSS PWM controller, the backlight would go from off to max, when changing the sysfs backlight brightness value from 90-100%, anything under aprox. 90% would turn the backlight fully off. Honoring the VBT specified PWM frequency will also hopefully fix the various bug reports which we have received about users perceiving the backlight to flicker after a suspend/resume cycle. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 1 + drivers/gpu/drm/i915/display/intel_panel.c| 19 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b24266c624fa..24ea4a7b6dde 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -223,6 +223,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; + int pwm_period_ns; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 8efdd9f08a08..14e611c92194 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -40,8 +40,6 @@ #include "intel_dsi_dcs_backlight.h" #include "intel_panel.h" -#define CRC_PMIC_PWM_PERIOD_NS 21333 - void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -597,7 +595,7 @@ static u32 pwm_get_backlight(struct intel_connector *connector) int duty_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); + return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -671,9 +669,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); + pwm_config(panel->backlight.pwm, duty_ns, + panel->backlight.pwm_period_ns); } static void @@ -1917,6 +1916,9 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } + panel->backlight.pwm_period_ns = NSEC_PER_SEC / +get_vbt_pwm_freq(dev_priv); + /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. @@ -1926,9 +1928,10 @@ static int pwm_setup_backlight(struct intel_connector *connector, panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ level = intel_panel_compute_brightness(connector, 100); - ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - retval = pwm_config(panel->backlight.pwm, ns, CRC_PMIC_PWM_PERIOD_NS); + retval = pwm_config(panel->backlight.pwm, ns, +
[PATCH 02/16] ACPI / LPSS: Save Cherry Trail PWM ctx registers only once (at activation)
The DSDTs on most Cherry Trail devices have an ugly clutch where the PWM controller gets turned off from the _PS3 method of the graphics-card dev: Method (_PS3, 0, Serialized) // _PS3: Power State 3 { ... PWMB = PWMC /* \_SB_.PCI0.GFX0.PWMC */ PSAT |= 0x03 Local0 = PSAT /* \_SB_.PCI0.GFX0.PSAT */ ... } Where PSAT is the power-status register of the PWM controller. Since the i915 driver will do a pwm_get on the pwm device as it uses it to control the LCD panel backlight, there is a device-link marking the i915 device as a consumer of the pwm device. So that the PWM controller will always be suspended after the i915 driver suspends (which is the right thing to do). This causes the above GFX0 PS3 AML code to run before acpi_lpss.c calls acpi_lpss_save_ctx(). So on these devices the PWM controller will already be off when acpi_lpss_save_ctx() runs. This causes it to read/save all 1-s (0x) as ctx register values. When these bogus values get restored on resume the PWM controller actually keeps working, since most bits are reserved, but this does set bit 3 of the LPSS General purpose register, which for the PWM controller has the following function: "This bit is re-used to support 32kHz slow mode. Default is 19.2MHz as PWM source clock". This causes the clock of the PWM controller to switch from 19.2MHz to 32KHz, which is a slow-down of a factor 600. Suprisingly enough so far there have been few bug reports about this. This is likely because the i915 driver was hardcoding the PWM frequency to 46 KHz, which divided by 600 would result in a PWM frequency of aprox. 78 Hz, which mostly still works fine. There are some bug reports about the LCD backlight flickering after suspend/resume which are likely caused by this issue. But with the upcoming patch-series to finally switch the i915 drivers code for external PWM controllers to use the atomic API and to honor the PWM frequency specified in the video BIOS (VBT), this becomes a much bigger problem. On most cases the VBT specifies either 200 Hz or 20 KHz as PWM frequency, which with the mentioned issue ends up being either 1/3 Hz, where the backlight actually visible blinks on and off every 3s, or in 33 Hz and horrible flickering of the backlight. There are a number of possible solutions to this problem: 1. Make acpi_lpss_save_ctx() run before GFX0._PS3 Pro: Clean solution from pov of not medling with save/restore ctx code Con: As mentioned the current ordering is the right thing to do Con: Requires assymmetry in at what suspend/resume phase we do the save vs restore, requiring more suspend/resume ordering hacks in already convoluted acpi_lpss.c suspend/resume code. 2. Do some sort of save once mode for the LPSS ctx Pro: Reasonably clean Con: Needs a new LPSS flag + code changes to handle the flag 3. Detect we have failed to save the ctx registers and do not restore them Pro: Not PWM specific, might help with issues on other LPSS devices too Con: If we can get away with not restoring the ctx why bother with it at all? 4. Do not save the ctx for CHT PWM controllers Pro: Clean, as simple as dropping a flag? Con: Not so simple as dropping a flag, needs a new flag to ensure that we still do lpss_deassert_reset() on device activation. 5. Make the pwm-lpss code fixup the LPSS-context registers Pro: Keeps acpi_lpss.c code clean Con: Moves knowledge of LPSS-context into the pwm-lpss.c code 1 and 5 both do not seem to be a desirable way forward. 3 and 4 seem ok, but they both assume that restoring the LPSS-context registers is not necessary. I have done a couple of test and those do show that restoring the LPSS-context indeed does not seem to be necessary on devices using s2idle suspend (and successfully reaching S0i3). But I have no hardware to test deep / S3 suspend. So I'm not sure that not restoring the context is safe. That leaves solution 2, which is about as simple / clean as 3 and 4, so this commit fixes the described problem by implementing a new LPSS_SAVE_CTX_ONCE flag and setting that for the CHT PWM controllers. Signed-off-by: Hans de Goede --- drivers/acpi/acpi_lpss.c | 19 --- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 67892fc0b822..26933e6b7b8c 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -68,6 +68,14 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) #define LPSS_NO_D3_DELAY BIT(5) +/* + * For some devices the DSDT AML code for another device turns off the device + * before our suspend handler runs, causing us to read/save all 1-s (0x) + * as ctx register values. + * Luckily these devices always use the same ctx re
pwm/i915: Convert pwm-crc and i915 driver's PWM code to use the atomic PWM API
Hi All, This patch series converts the i915 driver's cpde for controlling the panel's backlight with an external PWM controller to use the atomic PWM API. Initially the plan was for this series to consist of 2 parts: 1. convert the pwm-crc driver to support the atomic PWM API and 2. convert the i915 driver's PWM code to use the atomic PWM API. But during testing I've found a number of bugs in the pwm-lpss and I found that the acpi_lpss code needs some special handling because of some ugliness found in most Cherry Trail DSDTs. So now this series has grown somewhat large and consists of 4 parts: 1. acpi_lpss fixes workarounds for Cherry Trail DSTD nastiness 2. various fixes to the pwm-lpss driver 3. convert the pwm-crc driver to support the atomic PWM API and 4. convert the i915 driver's PWM code to use the atomic PWM API So we need to discuss how to merge this (once it passes review). Although the inter-dependencies are only runtime I still think we should make sure that 1-3 are in the drm-intel-next-queued (dinq) tree before merging the i915 changes. Both to make sure that the intel-gfx CI system does not become unhappy and for bisecting reasons. The involved acpi_lpss and pwm drivers do not see a whole lot of churn, so we could just merge everything through dinq, or we could use immutable branch and merge those into dinq. So Rafael and Thierry, can I either get your Acked-by for directly merging this into dinq, or can you provide an immutable branch with these patches? This series has been tested (and re-tested after adding various bug-fixes) extensively. It has been tested on the following devices: -Asus T100TABYT + CRC-PMIC PWM -Toshiba WT8-A BYT + CRC-PMIC PWM -Thundersoft TS178 BYT + CRC-PMIC PWM, inverse PWM -Asus T100HACHT + CRC-PMIC PWM -Terra Pad 1061 BYT + LPSS PWM -Trekstor Twin 10.1 BYT + LPSS PWM -Asus T101HACHT + CRC-PMIC PWM -GPD Pocket CHT + CRC-PMIC PWM Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 08/16] pwm: crc: Fix off-by-one error in the clock-divider calculations
The CRC PWM controller has a clock-divider which divides the clock with a value between 1-128. But as can seen from the PWM_DIV_CLK_xxx defines, this range maps to a register value of 0-127. So after calculating the clock-divider we must subtract 1 to get the register value, unless the requested frequency was so high that the calculation has already resulted in a (rounded) divider value of 0. Note that before this fix, setting a period of PWM_MAX_PERIOD_NS which corresponds to the max. divider value of 128 could have resulted in a bug where the code would use 128 as divider-register value which would have resulted in an actual divider value of 0 (and the enable bit being set). A rounding error stopped this bug from actually happen. This same rounding error means that after the subtraction of 1 it is impossible to set the divider to 128. Also bump PWM_MAX_PERIOD_NS by 1 ns to allow setting a divider of 128 (register-value 127). Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 43fc912c1fe9..5ba2a65c524c 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -22,7 +22,7 @@ #define PWM_MAX_LEVEL 0xFF #define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 5461333 /* 183 Hz */ +#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */ #define NSEC_PER_MHZ 1000 @@ -75,6 +75,9 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, /* changing the clk divisor, need to disable fisrt */ crc_pwm_disable(c, pwm); clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 16/16] drm/i915: panel: Use atomic PWM API for devs with an external PWM controller
Now that the PWM drivers which we use have been converted to the atomic PWM API, we can move the i915 panel code over to using the atomic PWM API. The removes a long standing FIXME and this removes a flicker where the backlight brightness would jump to 100% when i915 loads even if using the fastset path. Signed-off-by: Hans de Goede --- .../drm/i915/display/intel_display_types.h| 3 +- drivers/gpu/drm/i915/display/intel_panel.c| 73 +-- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 24ea4a7b6dde..48afb2925271 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -223,7 +224,7 @@ struct intel_panel { bool util_pin_active_low; /* bxt+ */ u8 controller; /* bxt+ only */ struct pwm_device *pwm; - int pwm_period_ns; + struct pwm_state pwm_state; /* DPCD backlight */ u8 pwmgen_bit_count; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index cb28b9908ca4..a0f76343f381 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -592,10 +592,11 @@ static u32 bxt_get_backlight(struct intel_connector *connector) static u32 pwm_get_backlight(struct intel_connector *connector) { struct intel_panel *panel = >panel; - int duty_ns; + int duty_ns, period_ns; duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, panel->backlight.pwm_period_ns); + period_ns = pwm_get_period(panel->backlight.pwm); + return DIV_ROUND_UP(duty_ns * 100, period_ns); } static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) @@ -669,10 +670,10 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32 static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_panel *panel = _intel_connector(conn_state->connector)->panel; - int duty_ns = DIV_ROUND_UP(level * panel->backlight.pwm_period_ns, 100); - pwm_config(panel->backlight.pwm, duty_ns, - panel->backlight.pwm_period_ns); + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void @@ -841,10 +842,8 @@ static void pwm_disable_backlight(const struct drm_connector_state *old_conn_sta struct intel_connector *connector = to_intel_connector(old_conn_state->connector); struct intel_panel *panel = >panel; - /* Disable the backlight */ - intel_panel_actually_set_backlight(old_conn_state, 0); - usleep_range(2000, 3000); - pwm_disable(panel->backlight.pwm); + panel->backlight.pwm_state.enabled = false; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state) @@ -1176,9 +1175,14 @@ static void pwm_enable_backlight(const struct intel_crtc_state *crtc_state, { struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_panel *panel = >panel; + int level = panel->backlight.level; - pwm_enable(panel->backlight.pwm); - intel_panel_actually_set_backlight(conn_state, panel->backlight.level); + level = intel_panel_compute_brightness(connector, level); + + panel->backlight.pwm_state.duty_cycle = + DIV_ROUND_UP(level * panel->backlight.pwm_state.period, 100); + panel->backlight.pwm_state.enabled = true; + pwm_apply_state(panel->backlight.pwm, >backlight.pwm_state); } static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state, @@ -1897,8 +1901,7 @@ static int pwm_setup_backlight(struct intel_connector *connector, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_panel *panel = >panel; const char *desc; - u32 level, ns; - int retval; + u32 level; /* Get the right PWM chip for DSI backlight according to VBT */ if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) { @@ -1916,36 +1919,30 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; } - panel->backlight.pwm_period_ns = NSEC_PER_SEC / -get_vbt_pwm_freq(dev_priv); - - /* -
[PATCH 12/16] pwm: crc: Implement get_state() method
Implement the pwm_ops.get_state() method to complete the support for the new atomic PWM API. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 58c7e9ef7278..6c75a3470bc8 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -114,8 +114,37 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + struct device *dev = crc_pwm->chip.dev; + unsigned int clk_div, clk_div_reg, duty_cycle_reg; + int error; + + error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, _div_reg); + if (error) { + dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error); + return; + } + + error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, _cycle_reg); + if (error) { + dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error); + return; + } + + clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1; + + state->period = clk_div * NSEC_PER_MHZ * 256 / PWM_BASE_CLK_MHZ; + state->duty_cycle = duty_cycle_reg * state->period / PWM_MAX_LEVEL; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled= !!(clk_div_reg & PWM_OUTPUT_ENABLE); +} + static const struct pwm_ops crc_pwm_ops = { .apply = crc_pwm_apply, + .get_state = crc_pwm_get_state, }; static int crystalcove_pwm_probe(struct platform_device *pdev) -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 10/16] pwm: crc: Enable/disable PWM output on enable/disable
The pwm-crc code is using 2 different enable bits: 1. bit 7 of the PWM0_CLK_DIV (PWM_OUTPUT_ENABLE) 2. bit 0 of the BACKLIGHT_EN register So far we've kept the PWM_OUTPUT_ENABLE bit set when disabling the PWM, this commit makes crc_pwm_disable() clear it on disable and makes crc_pwm_enable() set it again on re-enable. This should disable the internal (divided) PWM clock and tri-state the PWM output pin when disabled, saving some power. Signed-off-by: Hans de Goede --- drivers/pwm/pwm-crc.c | 24 +--- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index ef49a6e3c4d6..53734bcf67e1 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -41,10 +41,24 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) return container_of(pc, struct crystalcove_pwm, chip); } +static int crc_pwm_calc_clk_div(int period_ns) +{ + int clk_div; + + clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); + /* clk_div 1 - 128, maps to register values 0-127 */ + if (clk_div > 0) + clk_div--; + + return clk_div; +} + static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div | PWM_OUTPUT_ENABLE); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); return 0; @@ -53,8 +67,10 @@ static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) { struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int div = crc_pwm_calc_clk_div(pwm_get_period(pwm)); regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, div); } static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, @@ -70,16 +86,10 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, } if (pwm_get_period(pwm) != period_ns) { - int clk_div; + int clk_div = crc_pwm_calc_clk_div(period_ns); /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */ regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0); - - clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_MHZ); - /* clk_div 1 - 128, maps to register values 0-127 */ - if (clk_div > 0) - clk_div--; - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, clk_div | PWM_OUTPUT_ENABLE); } -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/2] drm: panel-orientation-quirks: Add quirk for Asus T101HA panel
Like the Asus T100HA the Asus T101HA also uses a panel which has been mounted 90 degrees rotated, add a quirk for this. Signed-off-by: Hans de Goede --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index ffd95bfeaa94..d11d83703931 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -121,6 +121,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), }, .driver_data = (void *)_t100ha, + }, {/* Asus T101HA */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T101HA"), + }, + .driver_data = (void *)_rightside_up, }, {/* GPD MicroPC (generic strings, also match on bios date) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/2] drm: panel-orientation-quirks: Use generic orientation-data for Acer S1003
The Acer S1003 has proper DMI strings for sys-vendor and product-name, so we do not need to match by BIOS-date. This means that the Acer S1003 can use the generic lcd800x1280_rightside_up drm_dmi_panel_orientation_data struct which is also used by other quirks. Signed-off-by: Hans de Goede --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 8 +--- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index d11d83703931..d00ea384dcbf 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -30,12 +30,6 @@ struct drm_dmi_panel_orientation_data { int orientation; }; -static const struct drm_dmi_panel_orientation_data acer_s1003 = { - .width = 800, - .height = 1280, - .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, -}; - static const struct drm_dmi_panel_orientation_data asus_t100ha = { .width = 800, .height = 1280, @@ -114,7 +108,7 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"), }, - .driver_data = (void *)_s1003, + .driver_data = (void *)_rightside_up, }, {/* Asus T100HA */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), -- 2.26.2 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH resend] drm: Add DRM_MODE_TYPE_USERDEF flag to probed modes matching a video= argument
Hi, On 5/14/20 11:53 AM, Emil Velikov wrote: Hi Hans, On Thu, 30 Apr 2020 at 15:55, Hans de Goede wrote: Hi, On 4/30/20 4:52 PM, Ville Syrjälä wrote: On Thu, Apr 30, 2020 at 02:47:00PM +0100, Emil Velikov wrote: Hi Hans, On Fri, 21 Feb 2020 at 17:33, Hans de Goede wrote: drm_helper_probe_add_cmdline_mode() prefers using a probed mode matching a video= argument over calculating our own timings for the user specified mode using CVT or GTF. But userspace code which is auto-configuring the mode may want to know that the user has specified that mode on the kernel commandline so that it can pick that mode over the mode which is marked as DRM_MODE_TYPE_PREFERRED. This commit sets the DRM_MODE_TYPE_USERDEF flag on the matching mode, just as we would do on the user-specified mode when no matching probed mode is found. Signed-off-by: Hans de Goede I was skimming around wrt Ville's compact drm_display_mode series and noticed that this never landed. The commit brings extra consistency when dealing with user defined modes, and is: Reviewed-by: Emil Velikov Ville this may trivially conflict with your work. I suspect you can do the honours, and apply on top of your series? That is if you agree with the patch. Quick glance at the original thread says maybe there were still some userspace issues unresolved? Not sure. IIRC the thread ended with Daniel agreeing on the userspace interface, but asking for some docs and me pointing out that the patch already updated/clarified the existing docs. After that things got quiet. So I believe that this is (still) ready to go upstream. Having read through the full discussion, couple of times, I believe you're spot on. Daniel requested documentation, which the patch provides. I'd say let's poke him on IRC a few times, if he doesn't object let's push it? Sounds good to me, I'm usually not on IRC (too distracting for me), canyou ping Daniel about this on IRC? Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC v2] drm/connector: Add support for privacy-screen properties (v2)
Hi, On 5/12/20 10:44 PM, mario.limoncie...@dell.com wrote: -Original Message- From: Hans de Goede Sent: Monday, May 11, 2020 12:47 PM To: Maarten Lankhorst; Maxime Ripard; Thomas Zimmermann; Daniel Vetter; David Airlie; Rajat Jain; Jani Nikula Cc: Hans de Goede; Pekka Paalanen; Limonciello, Mario; Quintanilla, Sonny; Jared Dominguez; Mark Pearson; dri-devel@lists.freedesktop.org Subject: [RFC v2] drm/connector: Add support for privacy-screen properties (v2) [EXTERNAL EMAIL] From: Rajat Jain Add support for generic electronic privacy screen properties, that can be added by systems that have an integrated EPS. Changes in v2 (Hans de Goede) - Create 2 properties, "privacy-screen sw-state" and "privacy-screen hw-state", to deal with devices where the OS might be locked out of making state changes - Write kerneldoc explaining how the 2 properties work together, what happens when changes to the state are made outside of the DRM code's control, etc. Signed-off-by: Rajat Jain Co-authored-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/gpu/drm-kms.rst | 2 + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++ drivers/gpu/drm/drm_connector.c | 100 ++ include/drm/drm_connector.h | 50 +++ 4 files changed, 158 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 906771e03103..b72b1e0db343 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -445,6 +445,8 @@ Property Types and Blob Property Support .. kernel-doc:: drivers/gpu/drm/drm_property.c :export: +.. _standard_connector_properties: + Standard Connector Properties - diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a1e5e262bae2..e56a11208515 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -766,6 +766,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, fence_ptr); } else if (property == connector->max_bpc_property) { state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -842,6 +844,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = 0; } else if (property == connector->max_bpc_property) { *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; + } else if (property == connector->privacy_screen_hw_state_property) { + *val = state->privacy_screen_hw_state; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 644f0ad10671..01360edc2376 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1186,6 +1186,45 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { *can also expose this property to external outputs, in which case they *must support "None", which should be the default (since external screens *have a built-in scaler). + * + * privacy-screen sw-state, privacy-screen hw-state: + * These 2 optional properties can be used to query the state of the + * electronic privacy screen that is available on some displays; and in + * some cases also control the state. If a driver implements these + * properties then both properties must be present. + * + * "privacy-screen hw-state" is read-only and reflects the actual state + * of the privacy-screen, possible values: "Enabled", "Disabled, + * "Enabled, locked", "Disabled, locked". The locked states indicate + * that the state cannot be changed through the DRM API. E.g. there + * might be devices where the firmware-setup options, or a hardware + * slider-switch, offer always on / off modes. + * + * "privacy-screen sw-state" can be set to change the privacy-screen state + * when not locked. In this case the driver must update the hw-state + * property to reflect the new state on completion of the commit of the + * sw-state property. Setting the sw-state property when the hw-state is + * locked must be interpreted by the driver as a request to change the + *
Re: [RFC v2 0/1] drm/connector: Add support for privacy-screen properties
Hi, On 5/12/20 4:20 PM, Pekka Paalanen wrote: On Tue, 12 May 2020 10:18:31 +0200 Hans de Goede wrote: Hi, On 5/11/20 9:55 PM, Rajat Jain wrote: Hi Hans, On Mon, May 11, 2020 at 10:47 AM Hans de Goede mailto:hdego...@redhat.com>> wrote: Hi All, This RFC takes Rajat's earlier patch for adding privacy-screen properties infra to drm_connector.c and then adds the results of the discussion from the "RFC: Drm-connector properties managed by another driver / privacy screen support" mail thread on top, hence the v2. Thank you so much for doing this. I was following the said discussion and eventually it became quite complex for me to understand and follow :-) I hope the new doc text makes things clear again? The most important thing here is big kernel-doc comment which gets added in the first patch-chunk modifying drm_connector.c, this summarizes, or at least tries to summarize, the conclusions of our previous discussion on the userspace API and lays down the ground rules for how the 2 new "privacy-screen sw-state" and "privacy-screen hw-state" properties are to be used both from the driver side as well as from the userspace side. Other then that this modifies Rajat's patch to add 2 properties instead of one, without much other changes. Rajat, perhaps you can do a new version of your patch-set integration / using this version of the properties and then if everyone is ok with the proposed userspace API Jani can hopefully merge the whole set through the i915 tree sometime during the 5.9 cycle. SGTM. I have actually moved to working on something else now, so I will most likely wait for this patch to get merged, before rebasing my other / remaining patches on top of that. We have the rule that code like this will not be merged until it has at least one in kernel user. I plan to eventually use this too, but that is still a while away as I first need to write a lcdshadow subsystem which the thinkpad_acpi code can then use to register a lcdshadow device; and when that all is in place, then I can hook it up on the drm code. Hi, I believe this falls under "new UAPI" rules, because this is adding new KMS properties. Hence an in-kernel user is not enough: https://www.kernel.org/doc/html/latest/gpu/drm-uapi.html#open-source-userspace-requirements Hmm, I believe that that mostly applies to new DRI (ioclt) interfaces for submitting rendering commands to new GPUs and other complex new APIs and not necessarily to introducing new properties.Also note that all properties are exported under X through Xrandr, at least reading them, not sure about setting them. Anyways I do plan to write some mutter code to test my lcdshadow subsys <-> DRM driver integration when that is all more then just vaporware. But I would prefer for Rajat's series to land before that so that I can build on top of it. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC v2 0/1] drm/connector: Add support for privacy-screen properties
Hi, On 5/11/20 9:55 PM, Rajat Jain wrote: Hi Hans, On Mon, May 11, 2020 at 10:47 AM Hans de Goede mailto:hdego...@redhat.com>> wrote: Hi All, This RFC takes Rajat's earlier patch for adding privacy-screen properties infra to drm_connector.c and then adds the results of the discussion from the "RFC: Drm-connector properties managed by another driver / privacy screen support" mail thread on top, hence the v2. Thank you so much for doing this. I was following the said discussion and eventually it became quite complex for me to understand and follow :-) I hope the new doc text makes things clear again? The most important thing here is big kernel-doc comment which gets added in the first patch-chunk modifying drm_connector.c, this summarizes, or at least tries to summarize, the conclusions of our previous discussion on the userspace API and lays down the ground rules for how the 2 new "privacy-screen sw-state" and "privacy-screen hw-state" properties are to be used both from the driver side as well as from the userspace side. Other then that this modifies Rajat's patch to add 2 properties instead of one, without much other changes. Rajat, perhaps you can do a new version of your patch-set integration / using this version of the properties and then if everyone is ok with the proposed userspace API Jani can hopefully merge the whole set through the i915 tree sometime during the 5.9 cycle. SGTM. I have actually moved to working on something else now, so I will most likely wait for this patch to get merged, before rebasing my other / remaining patches on top of that. We have the rule that code like this will not be merged until it has at least one in kernel user. I plan to eventually use this too, but that is still a while away as I first need to write a lcdshadow subsystem which the thinkpad_acpi code can then use to register a lcdshadow device; and when that all is in place, then I can hook it up on the drm code. So since Jani said your patch-set was more or less ready I think it would be best if you add my version of this to your patch-set and then post a new version of your patch-set. But first let me do a v3 addressing the remarks on doc text. Note I will wait a bit before sending out v3 to see if I get more feedback. Regards, Hans Thanks & Best Regards, Rajat This RFC takes Rajat's earlier patch for adding privacy-screen properties infra to drm_connector.c and then adds the results of the discussion from the "RFC: Drm-connector properties managed by another driver / privacy screen support" mail thread on top, hence the v2. The most important thing here is big kernel-doc comment which gets added in the first patch-chunk modifying drm_connector.c, this summarizes, or at least tries to summarize, the conclusions of our previous discussion on the userspace API and lays down the ground rules for how the 2 new "privacy-screen sw-state" and "privacy-screen hw-state" properties are to be used both from the driver side as well as from the userspace side. Other then that this modifies Rajat's patch to add 2 properties instead of one, without much other changes. Rajat, perhaps you can do a new version of your patch-set integration / using this version of the properties and then if everyone is ok with the proposed userspace API Jani can hopefully merge the whole set through the i915 tree sometime during the 5.9 cycle. Regards, Hans p.s. I plan to start working on the lcdshadow subsystem next. As discussed the plan for this subsystem is to allow drivers outside of the DRM subsys, such as for example the thinkpad_acpi driver, to register a lcdshadow device, which DRM drivers can then get a reference to and use to implement these properties. ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC v2] drm/connector: Add support for privacy-screen properties (v2)
Hi, On 5/11/20 10:04 PM, Rajat Jain wrote: On Mon, May 11, 2020 at 10:47 AM Hans de Goede wrote: From: Rajat Jain Add support for generic electronic privacy screen properties, that can be added by systems that have an integrated EPS. Changes in v2 (Hans de Goede) - Create 2 properties, "privacy-screen sw-state" and "privacy-screen hw-state", to deal with devices where the OS might be locked out of making state changes - Write kerneldoc explaining how the 2 properties work together, what happens when changes to the state are made outside of the DRM code's control, etc. Signed-off-by: Rajat Jain Co-authored-by: Hans de Goede Signed-off-by: Hans de Goede Ack, Thanks for doing this. --- Documentation/gpu/drm-kms.rst | 2 + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++ drivers/gpu/drm/drm_connector.c | 100 ++ include/drm/drm_connector.h | 50 +++ 4 files changed, 158 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 906771e03103..b72b1e0db343 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -445,6 +445,8 @@ Property Types and Blob Property Support .. kernel-doc:: drivers/gpu/drm/drm_property.c :export: +.. _standard_connector_properties: + Standard Connector Properties - diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a1e5e262bae2..e56a11208515 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -766,6 +766,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, fence_ptr); } else if (property == connector->max_bpc_property) { state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -842,6 +844,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = 0; } else if (property == connector->max_bpc_property) { *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; + } else if (property == connector->privacy_screen_hw_state_property) { + *val = state->privacy_screen_hw_state; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 644f0ad10671..01360edc2376 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1186,6 +1186,45 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * can also expose this property to external outputs, in which case they * must support "None", which should be the default (since external screens * have a built-in scaler). + * + * privacy-screen sw-state, privacy-screen hw-state: + * These 2 optional properties can be used to query the state of the + * electronic privacy screen that is available on some displays; and in + * some cases also control the state. If a driver implements these + * properties then both properties must be present. + * + * "privacy-screen hw-state" is read-only and reflects the actual state + * of the privacy-screen, possible values: "Enabled", "Disabled, + * "Enabled, locked", "Disabled, locked". The locked states indicate + * that the state cannot be changed through the DRM API. E.g. there + * might be devices where the firmware-setup options, or a hardware + * slider-switch, offer always on / off modes. May be add: "This is what the userspace tools must use to query and report the actual status at the moment, if needed" Thank you, suggestions for improving the doc are always very welcome, so I have tried adding this, both as is and with slightly changed wording. But it always feels like it is just repeating earlier info. To me the "reflect the actual state" from the beginning of the paragraph makes it abundantly clear that this indeed is what userspace should use to get, well, the actual state. + * + * "privacy-screen sw-state" can be set to change the privacy-screen state + * when not locked. In this case the driver must update the hw-state + * property to reflect the new state on
Re: [RFC v2] drm/connector: Add support for privacy-screen properties (v2)
Hi, On 5/12/20 9:49 AM, Pekka Paalanen wrote: On Mon, 11 May 2020 19:47:24 +0200 Hans de Goede wrote: From: Rajat Jain Add support for generic electronic privacy screen properties, that can be added by systems that have an integrated EPS. Changes in v2 (Hans de Goede) - Create 2 properties, "privacy-screen sw-state" and "privacy-screen hw-state", to deal with devices where the OS might be locked out of making state changes - Write kerneldoc explaining how the 2 properties work together, what happens when changes to the state are made outside of the DRM code's control, etc. Signed-off-by: Rajat Jain Co-authored-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/gpu/drm-kms.rst | 2 + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++ drivers/gpu/drm/drm_connector.c | 100 ++ include/drm/drm_connector.h | 50 +++ 4 files changed, 158 insertions(+) ... diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 644f0ad10671..01360edc2376 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1186,6 +1186,45 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { *can also expose this property to external outputs, in which case they *must support "None", which should be the default (since external screens *have a built-in scaler). + * + * privacy-screen sw-state, privacy-screen hw-state: + * These 2 optional properties can be used to query the state of the + * electronic privacy screen that is available on some displays; and in + * some cases also control the state. If a driver implements these + * properties then both properties must be present. + * + * "privacy-screen hw-state" is read-only and reflects the actual state + * of the privacy-screen, possible values: "Enabled", "Disabled, + * "Enabled, locked", "Disabled, locked". The locked states indicate + * that the state cannot be changed through the DRM API. E.g. there + * might be devices where the firmware-setup options, or a hardware + * slider-switch, offer always on / off modes. + * + * "privacy-screen sw-state" can be set to change the privacy-screen state + * when not locked. In this case the driver must update the hw-state + * property to reflect the new state on completion of the commit of the + * sw-state property. Setting the sw-state property when the hw-state is + * locked must be interpreted by the driver as a request to change the + * state to the set state when the hw-state becomes unlocked. E.g. if + * "privacy-screen hw-state" is "Enabled, locked" and the sw-state + * gets set to "Disabled" followed by the user unlocking the state by + * changing the slider-switch position, then the driver must set the + * state to "Disabled" upon receiving the unlock event. + * + * In some cases the privacy-screen state might change outside of control + * of the DRM code. E.g. there might be a firmware handled hotkey which + * toggles the state, or the state might be changed through another Hi, in the above three lines, I'd use the term "hardware state" instead of just "state" to be explicit. Or should it be "physical state" since "hardware state" might be confused with "hw-state" property state? Maybe "actual state"? That is what is used a few lines higher: '"privacy-screen hw-state" is read-only and reflects the actual state' And you use it yourself to describe what we want below: I don't mind as long as it's unambiguous and distinguishes explicitly between actual and the two property states. So since you and I both naturally described it as "actual state" without thinking too much what we where writing at the time (I guess that applies to your use too), "actual state" seems a good fit ? + * userspace API such as writing /proc/acpi/ibm/lcdshadow. In this case + * the driver must update both the hw-state and the sw-state to reflect + * the new value, overwriting any pending state requests in the sw-state. + * + * Note that the ability for the state to change outside of control of + * the DRM master process means that userspace must not cache the value + * of the sw-state. Ccaching the sw-state value and including it in later Extra 'c' in "Caching". Ack, will fix. + * atomic commits may lead to overriding a state change done through e.g. + * a firmware handled hotkey. Therefor userspace must not include the + * privacy-screen sw-state in an atomic commit unless it wants to change + * its value. */ This documentation and intended behaviour looks perfect to me. Great I'm glad you like it. If y
[RFC v2 0/1] drm/connector: Add support for privacy-screen properties
Hi All, This RFC takes Rajat's earlier patch for adding privacy-screen properties infra to drm_connector.c and then adds the results of the discussion from the "RFC: Drm-connector properties managed by another driver / privacy screen support" mail thread on top, hence the v2. The most important thing here is big kernel-doc comment which gets added in the first patch-chunk modifying drm_connector.c, this summarizes, or at least tries to summarize, the conclusions of our previous discussion on the userspace API and lays down the ground rules for how the 2 new "privacy-screen sw-state" and "privacy-screen hw-state" properties are to be used both from the driver side as well as from the userspace side. Other then that this modifies Rajat's patch to add 2 properties instead of one, without much other changes. Rajat, perhaps you can do a new version of your patch-set integration / using this version of the properties and then if everyone is ok with the proposed userspace API Jani can hopefully merge the whole set through the i915 tree sometime during the 5.9 cycle. Regards, Hans p.s. I plan to start working on the lcdshadow subsystem next. As discussed the plan for this subsystem is to allow drivers outside of the DRM subsys, such as for example the thinkpad_acpi driver, to register a lcdshadow device, which DRM drivers can then get a reference to and use to implement these properties. ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC v2] drm/connector: Add support for privacy-screen properties (v2)
From: Rajat Jain Add support for generic electronic privacy screen properties, that can be added by systems that have an integrated EPS. Changes in v2 (Hans de Goede) - Create 2 properties, "privacy-screen sw-state" and "privacy-screen hw-state", to deal with devices where the OS might be locked out of making state changes - Write kerneldoc explaining how the 2 properties work together, what happens when changes to the state are made outside of the DRM code's control, etc. Signed-off-by: Rajat Jain Co-authored-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/gpu/drm-kms.rst | 2 + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++ drivers/gpu/drm/drm_connector.c | 100 ++ include/drm/drm_connector.h | 50 +++ 4 files changed, 158 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 906771e03103..b72b1e0db343 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -445,6 +445,8 @@ Property Types and Blob Property Support .. kernel-doc:: drivers/gpu/drm/drm_property.c :export: +.. _standard_connector_properties: + Standard Connector Properties - diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a1e5e262bae2..e56a11208515 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -766,6 +766,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, fence_ptr); } else if (property == connector->max_bpc_property) { state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -842,6 +844,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = 0; } else if (property == connector->max_bpc_property) { *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; + } else if (property == connector->privacy_screen_hw_state_property) { + *val = state->privacy_screen_hw_state; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 644f0ad10671..01360edc2376 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1186,6 +1186,45 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * can also expose this property to external outputs, in which case they * must support "None", which should be the default (since external screens * have a built-in scaler). + * + * privacy-screen sw-state, privacy-screen hw-state: + * These 2 optional properties can be used to query the state of the + * electronic privacy screen that is available on some displays; and in + * some cases also control the state. If a driver implements these + * properties then both properties must be present. + * + * "privacy-screen hw-state" is read-only and reflects the actual state + * of the privacy-screen, possible values: "Enabled", "Disabled, + * "Enabled, locked", "Disabled, locked". The locked states indicate + * that the state cannot be changed through the DRM API. E.g. there + * might be devices where the firmware-setup options, or a hardware + * slider-switch, offer always on / off modes. + * + * "privacy-screen sw-state" can be set to change the privacy-screen state + * when not locked. In this case the driver must update the hw-state + * property to reflect the new state on completion of the commit of the + * sw-state property. Setting the sw-state property when the hw-state is + * locked must be interpreted by the driver as a request to change the + * state to the set state when the hw-state becomes unlocked. E.g. if + * "privacy-screen hw-state" is "Enabled, locked" and the sw-state + * gets set to "Disabled" followed by the user unlocking the state by + * changing the slider-switch position, then the driver must set the + * state to "Disabled" upon receiving the unlock event. + * + * In some cases the privacy-screen state might change outside of control + * of the DRM code. E.g. the
Re: Linux GOP initialization is wrong
Hi, On 4/30/20 3:22 AM, David Santamaría Rogado wrote: I'm thinking also about the i915 part. Could be something different that Intel does in the Windows version of their driver? Seem very strange that Microsoft could patch something because of this particular hardwares. On Windows devices often ship with vendor modified drivers, for the graphics driver Intel even allowed the vendor to ship its own driver and opt out of generic Intel driver updates (this changes very recently). Also chances are that this is all controlled by some registry setting, so as long as the device ships with the registry setting telling the driver to deal with this, then on upgrades of the driver, the driver will still see the old registry setting and behave accordingly. I've very carefully checked the VBT (Video Bios Tables) and I see no hints there which the windows driver could use. So I believe that this is taking care of with device specific settings done by the device's vendor. Regards, Hans El mar., 28 abr. 2020 a las 10:45, Hans de Goede () escribió: Hi David, On 4/28/20 1:58 AM, David Santamaría Rogado wrote: This is related to the issues at least on some devices for panel orientation quirks where added. Thank you for looking into this. My tests have been done over a Lenovo ideapad D330. This devices like the other ones that need panel orientation quirks, shows the initramfs with wrong stride and x and y swapped. By applying the panel orientation quirks this gets solved but many parts of the systems components needs to be patched. Hans has done a great job with plymouth, mutter... but always appears a new problem derived as for example vnc desktop sharing with this devices doesn't work and the output is send messed up. When I first started adding support for devices which have their screen mounted 90 degrees rotated my first attempts where aimed at solving this transparently in the kernel. Unfortunately this is not possible. On most affected devices the hardware does not support 90 degrees rotation for the primary display layer; or if it does this requires the framebuffer being in a hardware-specific tiled format rather then being a linear framebuffer. Using these tiled formats requires userspace to be aware of this, which rules out transparently handling this in the kernel. Other layers (cursor layer, video overlay layers) have similar issues which require userspace to be aware of what is happening, so unfortunately there is no other way to deal with this then fixing involved userspace components. I'm a bit surprised that you sat that vnc desktop sharing does not work, I guess that also depends on how the desktop sharing works. If it pokes directly at the framebuffer somehow, then yes it will be messed up. But if it goes through the display server then things might work. I guess that it is possible that the code doing this cannot deal with Xrandr output rotation ... The strange thing is that bootloaders like GRUB or rEFInd seems to be able to handle this and they paint themselves right, despite when booting Windows directly Windows paints itself right and booted with GRUB or rEFInd the first second also paint itself wrong. Haven't tested this too much but the interesting thing is in the next paragraph. My experience with bootloaders showing themselves the right way up is mixed. It seems that the firmware is doing some hacks for this on some devices, at least for the EFI text console. Funnily enough (for some definition of fun) on at least one of the devices where the firmware is playing tricks (Asus T100HA IIRC) the position of the carret for text-editing is off by one, which is very annoying when editing the kernel commandline and which clearly shows that things are being emulated in software here. I decided to get the UEFI GOP video modes and found that the D330 have these ones: Mode 0: 1200x1920 Mode 1: 640x480 Mode 2: 800x600 Mode 3: 1024x768 Mode 4: 1920x1200 (this is the default one started by the firmware) Mode 5: 480x640 Mode 6: 600x800 Mode 7: 768x1024 So I thought that Linux is taking the first mode despite is not the active one and that's why the display is messed up. Nope, Linux does not touch the mode at all (nor does grub by default). Doing a EFI/GOP modeset has the risk of triggering all sort of firmware bugs. So we stick with what we get. This has interesting side effects where on some systems you get a different mode when turning on the machine and letting it boot, vs turning it on, pressing e.g. F12 to get the boot menu and then boot Linux. Playing a little I could modify the GOP video mode before booting with the UEFI Shell by simple using the mode 150 101. This causes GOP video mode 5 to be switched to video mode 0, the first one. Booting now makes initramfs messages to be correctly rendered but in the wrong orientation. Right, the rendering on the side thing is expected. As said the hardware cannot do 90 degrees rotation with a linear framebuffer and the GOP
Re: [PATCH 02/10] drm: Add backlight helper
Hi, On 4/29/20 8:40 PM, Noralf Trønnes wrote: Den 29.04.2020 16.13, skrev Hans de Goede: Hi Noralf, On 4/29/20 2:48 PM, Noralf Trønnes wrote: This adds a function that creates a backlight device for a connector. It does not deal with the KMS backlight ABI proposition[1] to add a connector property. It only takes the current best practise to standardise the creation of a backlight device for DRM drivers while we wait for the property. The brightness value is set using a connector state variable and an atomic commit. I have looked through some of the backlight users and this is what I've found: GNOME [2] - Brightness range: 0-100 Scale: Assumes perceptual I'm afraid that this is an incaccurate view of how GNOME handles the brightness. gnome-settings-daemon (g-s-d) exports a DBUS property which has a range of 0 - 100%. But it also offers step-up and step-down DBUS methods which are used for handling brightness hotkey presses. This is important because g-s-d internally also keeps a step_size variable which depends on the brightness_max value of the sysfs backlight interface, like this: BRIGHTNESS_STEP_AMOUNT(max) ((max) < 20 ? 1 : (max) / 20) This is important because some older laptops where we depend on the vendor specific ACPI method (from e.g. dell-laptop or thinkpad_acpi) there are only 8 levels. So if g-s-d where to simply fake a 1-100 range and would leave the stepping up to the DBus API user and that user would want 20 steps, so 5 % per step, then the user would get Start -> 100% -> level 8 Press down -> 95% -> level 7 Press down -> 90% -> level 7 *no change* etc. Somewhat related on some embedded ARM devices there are tricks where when the entire scene being rendered does not use 100% white as color, the entire scene has all its rgb values upscaled (too a curve) so that the brightest colors do hit 100% of one of r/g/b, combined with dimming the backlight a bit to save power. As you can imagine for tricks like these you want as much backlight control precision as possible. So any backlight infra we add must expose the true range of the backlight control and not normalize it to a 0-100 range. So sorry, but nack for the current version because of the hardcoding of the range. No problem, I just had to start from somewhere, and I started with: Give the driver developer as few options as possible, no more than necessary, but I didn't really know what was necessary :-) The reason I chose a 0-100 range is because the backlight property ABI proposal had this range and it maps so nicely to percent. And can the ordinary human see brightness changes in more than 100 steps? This helper is only to be used by drm drivers and I assumed that all the current drivers registering a backlight device could at least do that range. Looking through the drivers and their max_brightness values that assumption isn't quite right: amd: 255 gma500: 100 i915: nouveau/nv40: 31 nouveau/nv50: 100 radeon: 255 shmobile: panel-dsi-cm.c: 255 panel-jdi-lt070me05000.c: 255 panel-orisetech-otm8009a.c: 255 panel-raydium-rm67191.c: 255 panel-samsung-s6e63m0.c: 10 panel-sony-acx424akp.c: 1023 panel-samsung-s6e3ha2.c: 100 panel-samsung-s6e63j0x03.c: 100 panel-sony-acx565akm.c: 255 bridge/parade-ps8622.c: 255 I'll add max_brightness as an argument together with scale which you commented on below. Ok, sounds good. Also the scale really should be specified by the driver, or be hardcoded to BACKLIGHT_SCALE_UNKNOWN for now. In many cases we do not really know. But for e.g. the acpi_video firmware backlight interface a good guess is that it actually represents a perceptual scale rather then controlling the wattage. Where as the native i915 backlight interface really is controlling the wattage without any perceptual correction. Another problem with your proposal is that it seems to assume that the backlight is controlled by the drm/kms driver. On x86 we have Yes, this backlight device is just for drm drivers. I was hoping you might have some clever ideas how to deal with / prepare for the backlight driver outside of drm-driver case too. But I completely understand that you want to limit your scope. The reason I spend time on this is because I want to pass backlight brightness changes through the atomic machinery like any other state change. I understand. Regards, Hans atleast 3 different drivers for the backlight: 1) The i915 (or amd/nouveau) native driver which more or less directly pokes the PWM controller of the GPU. 2) The ACPI video standard backlight interface 3) Vendor specific ACPI interfaces from older laptops ATM we always register 1. which could remain unchanged with your code and then also register 2/3 if we (the kernel) think that will work better (*) and then rely on userspace prefering these (they have a different backlight_type) over 1. Ideally any infra we add will also offer the option to tie 2. or 3. to the connector... Reg
Re: [PATCH resend] drm: Add DRM_MODE_TYPE_USERDEF flag to probed modes matching a video= argument
Hi, On 4/30/20 4:52 PM, Ville Syrjälä wrote: On Thu, Apr 30, 2020 at 02:47:00PM +0100, Emil Velikov wrote: Hi Hans, On Fri, 21 Feb 2020 at 17:33, Hans de Goede wrote: drm_helper_probe_add_cmdline_mode() prefers using a probed mode matching a video= argument over calculating our own timings for the user specified mode using CVT or GTF. But userspace code which is auto-configuring the mode may want to know that the user has specified that mode on the kernel commandline so that it can pick that mode over the mode which is marked as DRM_MODE_TYPE_PREFERRED. This commit sets the DRM_MODE_TYPE_USERDEF flag on the matching mode, just as we would do on the user-specified mode when no matching probed mode is found. Signed-off-by: Hans de Goede I was skimming around wrt Ville's compact drm_display_mode series and noticed that this never landed. The commit brings extra consistency when dealing with user defined modes, and is: Reviewed-by: Emil Velikov Ville this may trivially conflict with your work. I suspect you can do the honours, and apply on top of your series? That is if you agree with the patch. Quick glance at the original thread says maybe there were still some userspace issues unresolved? Not sure. IIRC the thread ended with Daniel agreeing on the userspace interface, but asking for some docs and me pointing out that the patch already updated/clarified the existing docs. After that things got quiet. So I believe that this is (still) ready to go upstream. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 02/10] drm: Add backlight helper
Hi Noralf, On 4/29/20 2:48 PM, Noralf Trønnes wrote: This adds a function that creates a backlight device for a connector. It does not deal with the KMS backlight ABI proposition[1] to add a connector property. It only takes the current best practise to standardise the creation of a backlight device for DRM drivers while we wait for the property. The brightness value is set using a connector state variable and an atomic commit. I have looked through some of the backlight users and this is what I've found: GNOME [2] - Brightness range: 0-100 Scale: Assumes perceptual I'm afraid that this is an incaccurate view of how GNOME handles the brightness. gnome-settings-daemon (g-s-d) exports a DBUS property which has a range of 0 - 100%. But it also offers step-up and step-down DBUS methods which are used for handling brightness hotkey presses. This is important because g-s-d internally also keeps a step_size variable which depends on the brightness_max value of the sysfs backlight interface, like this: BRIGHTNESS_STEP_AMOUNT(max) ((max) < 20 ? 1 : (max) / 20) This is important because some older laptops where we depend on the vendor specific ACPI method (from e.g. dell-laptop or thinkpad_acpi) there are only 8 levels. So if g-s-d where to simply fake a 1-100 range and would leave the stepping up to the DBus API user and that user would want 20 steps, so 5 % per step, then the user would get Start -> 100% -> level 8 Press down -> 95% -> level 7 Press down -> 90% -> level 7 *no change* etc. Somewhat related on some embedded ARM devices there are tricks where when the entire scene being rendered does not use 100% white as color, the entire scene has all its rgb values upscaled (too a curve) so that the brightest colors do hit 100% of one of r/g/b, combined with dimming the backlight a bit to save power. As you can imagine for tricks like these you want as much backlight control precision as possible. So any backlight infra we add must expose the true range of the backlight control and not normalize it to a 0-100 range. So sorry, but nack for the current version because of the hardcoding of the range. Also the scale really should be specified by the driver, or be hardcoded to BACKLIGHT_SCALE_UNKNOWN for now. In many cases we do not really know. But for e.g. the acpi_video firmware backlight interface a good guess is that it actually represents a perceptual scale rather then controlling the wattage. Where as the native i915 backlight interface really is controlling the wattage without any perceptual correction. Another problem with your proposal is that it seems to assume that the backlight is controlled by the drm/kms driver. On x86 we have atleast 3 different drivers for the backlight: 1) The i915 (or amd/nouveau) native driver which more or less directly pokes the PWM controller of the GPU. 2) The ACPI video standard backlight interface 3) Vendor specific ACPI interfaces from older laptops ATM we always register 1. which could remain unchanged with your code and then also register 2/3 if we (the kernel) think that will work better (*) and then rely on userspace prefering these (they have a different backlight_type) over 1. Ideally any infra we add will also offer the option to tie 2. or 3. to the connector... Regards, Hans *) e.g. it will work while the others will not work at all Avoids setting the sysfs brightness value to zero if max_brightness >= 99. Can connect connector and backlight using the sysfs device. KDE [3] --- Brightness range: 0-100 Scale: Assumes perceptual Weston [4] -- Brightness range: 0-255 Scale: Assumes perceptual Chromium OS [5] --- Brightness range: 0-100 Scale: Depends on the sysfs file 'scale' which is a recent addition (2019) xserver [6] --- Brightness range: 0-x (driver specific) (1 is minimum, 0 is OFF) Scale: Assumes perceptual The builtin modesetting driver[7] does not support Backlight, Intel[8] does. [1] https://lore.kernel.org/dri-devel/4b17ba08-39f3-57dd-5aad-d37d844b0...@linux.intel.com/ [2] https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/blob/master/plugins/power/gsd-backlight.c [3] https://github.com/KDE/powerdevil/blob/master/daemon/backends/upower/backlighthelper.cpp [4] https://gitlab.freedesktop.org/wayland/weston/-/blob/master/libweston/backend-drm/drm.c [5] https://chromium.googlesource.com/chromiumos/platform2/+/refs/heads/master/power_manager/powerd/system/internal_backlight.cc [6] https://github.com/freedesktop/xorg-randrproto/blob/master/randrproto.txt [7] https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/hw/xfree86/drivers/modesetting/drmmode_display.c [8] https://gitlab.freedesktop.org/xorg/driver/xf86-video-intel/-/blob/master/src/backlight.c Cc: Hans de Goede Cc: Jani Nikula Cc: Martin Peres Cc: Daniel Thompson Signed-off-by: Noralf Trønnes --- Documentation/gpu/drm-kms-helpers.rst | 6 + drivers/gpu/drm/Kcon
Re: Linux GOP initialization is wrong
Hi David, On 4/28/20 1:58 AM, David Santamaría Rogado wrote: This is related to the issues at least on some devices for panel orientation quirks where added. Thank you for looking into this. My tests have been done over a Lenovo ideapad D330. This devices like the other ones that need panel orientation quirks, shows the initramfs with wrong stride and x and y swapped. By applying the panel orientation quirks this gets solved but many parts of the systems components needs to be patched. Hans has done a great job with plymouth, mutter... but always appears a new problem derived as for example vnc desktop sharing with this devices doesn't work and the output is send messed up. When I first started adding support for devices which have their screen mounted 90 degrees rotated my first attempts where aimed at solving this transparently in the kernel. Unfortunately this is not possible. On most affected devices the hardware does not support 90 degrees rotation for the primary display layer; or if it does this requires the framebuffer being in a hardware-specific tiled format rather then being a linear framebuffer. Using these tiled formats requires userspace to be aware of this, which rules out transparently handling this in the kernel. Other layers (cursor layer, video overlay layers) have similar issues which require userspace to be aware of what is happening, so unfortunately there is no other way to deal with this then fixing involved userspace components. I'm a bit surprised that you sat that vnc desktop sharing does not work, I guess that also depends on how the desktop sharing works. If it pokes directly at the framebuffer somehow, then yes it will be messed up. But if it goes through the display server then things might work. I guess that it is possible that the code doing this cannot deal with Xrandr output rotation ... The strange thing is that bootloaders like GRUB or rEFInd seems to be able to handle this and they paint themselves right, despite when booting Windows directly Windows paints itself right and booted with GRUB or rEFInd the first second also paint itself wrong. Haven't tested this too much but the interesting thing is in the next paragraph. My experience with bootloaders showing themselves the right way up is mixed. It seems that the firmware is doing some hacks for this on some devices, at least for the EFI text console. Funnily enough (for some definition of fun) on at least one of the devices where the firmware is playing tricks (Asus T100HA IIRC) the position of the carret for text-editing is off by one, which is very annoying when editing the kernel commandline and which clearly shows that things are being emulated in software here. I decided to get the UEFI GOP video modes and found that the D330 have these ones: Mode 0: 1200x1920 Mode 1: 640x480 Mode 2: 800x600 Mode 3: 1024x768 Mode 4: 1920x1200 (this is the default one started by the firmware) Mode 5: 480x640 Mode 6: 600x800 Mode 7: 768x1024 So I thought that Linux is taking the first mode despite is not the active one and that's why the display is messed up. Nope, Linux does not touch the mode at all (nor does grub by default). Doing a EFI/GOP modeset has the risk of triggering all sort of firmware bugs. So we stick with what we get. This has interesting side effects where on some systems you get a different mode when turning on the machine and letting it boot, vs turning it on, pressing e.g. F12 to get the boot menu and then boot Linux. Playing a little I could modify the GOP video mode before booting with the UEFI Shell by simple using the mode 150 101. This causes GOP video mode 5 to be switched to video mode 0, the first one. Booting now makes initramfs messages to be correctly rendered but in the wrong orientation. Right, the rendering on the side thing is expected. As said the hardware cannot do 90 degrees rotation with a linear framebuffer and the GOP provided efifb is a linear framebuffer. So without telling the kernel to software rotate its text console the text will always be on its side. What your little EFI shell hack is doing is working around what seems to be a bug on these Lenovo devices gives us the wrong stride and dimensions for the EFI framebuffer. Note that this very much is a Lenovo bug, all the other devices with 90 degree rotated screens let us render the text console on its side just fine. They correctly tells us the real size and stride of the screen (so its portrait dimensions since it is a portrait screen). Even though this is a Lenovo bug we should probably still try to find a way to deal with this though, so that the efifb works correctly on these devices... A look at drivers/firmware/efi/libstub/gop.c seems to be what is happening, the first available video mode is used despite it could not be the active one in GOP and the active mode is not switched to the discovered one by Linux. Both GRUB and rEFInd are able to respect the video mode that GOP has active so it's
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi all, On 4/24/20 11:08 AM, Pekka Paalanen wrote: On Fri, 24 Apr 2020 10:24:31 +0200 Hans de Goede wrote: Agree on the hw-state prop reflecting the actual hardware state at all times, that one is easy. However, when userspace sets "privacy-screen-sw-state", the driver should attempt to change hardware state regardless of whether the "privacy-screen-sw-state" value changes compared to its old value or not. Otherwise userspace cannot intentionally override a hardware hotkey setting if possible (or would need two atomic commits to do it). Ack / agreed. Mind, the above paragraph is only what I interpreted from this email thread here. Previously I did not think that with atomic KMS, setting a property to a value it already has could trigger anything. But I guess it can? In a way. My idea for the "privacy-screen-sw-state" is for it to reflect the last requested value, where the request could come from either a firmware controlled hotkey; or from userspace (this seems to be where our ideas of how to handle this diverts). So what can happen is (with both props being always in sync) -userspace reads privacy screen being off -user toggles privacy screen on through firmware controlled hotkey -kernel gets notified about state toggle, updates both property states to on -userspace commits its old knowledge of the property (off), thereby triggering the kernel to turn the privacy screen back off So in this case from the kernel pov the property is actually set to a new value, not to "a value it already has". Hi, that is an interesting point of view. You are keeping the separation between "wanted" and "actual" state, but counting firmware/hardware hotkeys as "want" instead of letting them silently change hardware state. Right, that seems more natural to me, as mentioned already, this way the wanted and hw state only get out of sync if the hw is locked to a certain state. That seems ok. Note there can be races here of course, but lets ignore those (for now). Both the hotkey event as well as userspace changing the setting will be end-user triggered events and will not happen at high frequency. Also I see no way to completely eliminate racing here. Luckily the side effects of the race or pretty harmless (annoying maybe, but not causing crashes, etc). This design is based on that it can. What is not clear to me is if any change to"privacy-screen-hw-state" shall be propagated to "privacy-screen-sw-state"? - If yes, then I think we are not solving any problems of single property. - If no, then why do we require userspace to write to sw state only if something has changed? No. As already written, the kernel must not change the value of "privacy-screen-sw-state", only userspace can. So this is where out view of how to handle this differs, I do not see the hotkey changing the state as different from userspace changing it. The reason for me to have both a sw- and a hw-state is in case there is a physical switch (typically a slider style switch) which forces the state to on / off. In this case userspace could still set the "privacy-screen-sw-state" prop and then the 2 could differ. Yes, the locked switch case definitely makes sense to me. If userspace has to avoid setting the sw property unless it actually intends to change it, then the sw property being controlled from multiple sources (firmware, hotkey, the /proc file below) could work. It would even tell the KMS client when someone else changed the "wanted" state. Right, that is the idea and telling the KMS client definitely is a feature we want, so that we can show an OSD notifcation on the firmware handled hotkey presses, like we already do for volume control/mute, (kbd) backlight changes, etc. Lets add one more complication to this, which I think helps. Currently the thinkpad_acpi driver exports the privacy screen as: /proc/acpi/ibm/lcdshadow Userspace can write this and then change the privacy-screen setting, this is in shipped kernels and cannot be dropped because it is part if the kernel's uABI now. This means that another userspace process can change the property underneath a kms client. I do not see how this is different from the firmware changing the setting based on a hotkey press. Yet if we stick with your "only userspace can" change the sw-state setting, then does this count as userspace, or do you mean only a kms client can ? And then how is another kms-client changing the setting different ? To me that would be similar to firmware changing hardware state: it's not the KMS client (the display server) doing it, but something else behind its back while it thinks it's in full control. Doing things behind the display server's back is what creates all the mess here. Right, unfortunately this is not something which we can change. I have asked Lenovo if it will be possible to jus
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi all, Pekka, Rajat, Thank you for your input in this. On 4/24/20 9:40 AM, Pekka Paalanen wrote: On Thu, 23 Apr 2020 11:21:47 -0700 Rajat Jain wrote: On Tue, Apr 21, 2020 at 7:46 AM Pekka Paalanen wrote: On Tue, 21 Apr 2020 14:37:41 +0200 Hans de Goede wrote: TL;DR: Yes there will be races, because of both userspace + the firmware having; and potentially using r/w access to the privacy-screen state. But in practice I expect these to not really be an issue. Important here is that userspace only commits the property in a transaction to commit if it actually intends to change the property so as to not needlessly create a situation where we might hit the race. As for 1 vs 2 properties for this I guess that in preparation for potential devices where the state is locked, having a r/w sw-state + a ro hw-state property makes sense. So I suggest that we replace the current "privacy-screen" property from Rajat's patch-set with 2 props named: "privacy-screen-sw-state" (r/w) "privacy-screen-hw-state" (ro) Where for current gen hardware the privacy-screen-hw-state is just a mirror of the sw-state. Just to make sure I understand the semantics correctly: - The "privacy-screen-hw-state" shall be read-only, and can be modified by: - Hardware (e.g. HW kill switch). - Firmware. - (Potentially) needs a notification/irq to the kernel when this changes (or may be kernel can read it only when userspace queries for it). - The "privacy-screen-sw-state" shall be read-write, and can only be modified by user space. - If user space toggles it, the kernel will attempt to "request" the change to hardware. - Whether the request to hardware was successful or not, the "privacy-screen-sw-state" will always reflect the latest value userspace wrote. - If the request to hardware was successful, the "privacy-screen-hw-state" will also change (probably via a separate notification/irq from HW). - We expect the user space to write to "privacy-screen-sw-state" only if it really wants to toggle the value. Hi, yes, to my understanding, that seems to be the correct idea from this thread. The hw-state property must reflect the actual hardware state at all times. Agree on the hw-state prop reflecting the actual hardware state at all times, that one is easy. However, when userspace sets "privacy-screen-sw-state", the driver should attempt to change hardware state regardless of whether the "privacy-screen-sw-state" value changes compared to its old value or not. Otherwise userspace cannot intentionally override a hardware hotkey setting if possible (or would need two atomic commits to do it). Ack / agreed. Mind, the above paragraph is only what I interpreted from this email thread here. Previously I did not think that with atomic KMS, setting a property to a value it already has could trigger anything. But I guess it can? In a way. My idea for the "privacy-screen-sw-state" is for it to reflect the last requested value, where the request could come from either a firmware controlled hotkey; or from userspace (this seems to be where our ideas of how to handle this diverts). So what can happen is (with both props being always in sync) -userspace reads privacy screen being off -user toggles privacy screen on through firmware controlled hotkey -kernel gets notified about state toggle, updates both property states to on -userspace commits its old knowledge of the property (off), thereby triggering the kernel to turn the privacy screen back off So in this case from the kernel pov the property is actually set to a new value, not to "a value it already has". Note there can be races here of course, but lets ignore those (for now). Both the hotkey event as well as userspace changing the setting will be end-user triggered events and will not happen at high frequency. Also I see no way to completely eliminate racing here. Luckily the side effects of the race or pretty harmless (annoying maybe, but not causing crashes, etc). This design is based on that it can. What is not clear to me is if any change to"privacy-screen-hw-state" shall be propagated to "privacy-screen-sw-state"? - If yes, then I think we are not solving any problems of single property. - If no, then why do we require userspace to write to sw state only if something has changed? No. As already written, the kernel must not change the value of "privacy-screen-sw-state", only userspace can. So this is where out view of how to handle this differs, I do not see the hotkey changing the state as different from userspace changing it. The reason for me to have both a sw- and a hw-state is in case there is a physical switch (typically a slider style switch) which forces the state to on / off. In this case userspace could still set the &quo
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/17/20 1:55 PM, Jani Nikula wrote: On Fri, 17 Apr 2020, Pekka Paalanen wrote: On Wed, 15 Apr 2020 17:40:46 +0200 Hans de Goede wrote: Hi, On 4/15/20 5:28 PM, Jani Nikula wrote: On Wed, 15 Apr 2020, Hans de Goede wrote: ii. Currently the "privacy-screen" property added by Rajat's patch-set is an enum with 2 possible values: "Enabled" "Disabled" We could add a third value "Not Available", which would be the default and then for internal panels always add the property so that we avoid the problem that detecting if the laptop has an internal privacy screen needs to be done before the connector is registered. Then we can add some hooks which allow an lcdshadow-driver to register itself against a connector later (which is non trivial wrt probe order, but lets ignore that for now). I regret dropping the ball on Rajat's series (sorry!). I do think having the connector property for this is the way to go. I 100% agree. Even if we couldn't necessarily figure out all the details on the kernel internal connections, can we settle on the property though, so we could move forward with Rajat's series? Yes please, this will also allow us to move forward with userspace support even if for testing that we do some hacks for the kernel's internal connections for now. Moreover, do we actually need two properties, one which could indicate userspace's desire for the property, and another that tells the hardware state? No I do not think so. I would expect there to just be one property, I guess that if the state is (partly) firmware controlled then there might be a race, but we will need a notification mechanism (*) for firmware triggered state changes anyways, so shortly after loosing the race userspace will process the notification and it will know about it. One thing which might be useful is a way to signal that the property is read-only in case we ever hit hw where that is the case. I'd so very much like to have no in-kernel/in-firmware shortcuts to enable/disable the privacy screen, and instead have any hardware buttons just be events that the userspace could react to. However I don't think that'll be the case unfortunately. In my experience with keyboard-backlight support, we will (unfortunately) see a mix and in some case we will get a notification that the firmware has adjusted the state, rather then just getting a keypress and dealing with that ourselves. In some cases we may even be able to choose, so the fw will deal with it by default but we can ask it to just send a key-press. But I do believe that we can *not* expect that we will always just get a keypress for userspace to deal with. Hi, let's think about how userspace uses atomic KMS UAPI. The simplest way to use atomic correctly is that userspace will for every update send the full, complete set of all properties that exist, both known and unknown to userspace (to recover from temporarily VT-switching to another KMS program that changes unknown properties). Attempting to track which properties already have their correct values in the kernel is extra work for just extra bugs. Assuming the property is userspace-writable: if kernel goes and changes the property value on its own, it will very likely be just overwritten by userspace right after if userspace does not manage to process the uevent first. If that happens and userspace later processes the uevent, userspace queries the kernel for the current proprerty state which is now what userspace wrote, not what firmware set. Therefore you end up with the firmware hotkey working only randomly. It would be much better to have the hotkey events delivered to userspace so that userspace can control the privacy screen and everything will be reliable, both the hotkeys and any GUI for it. I'd like this too. However I fear this is out of our control, and OEMs have and will anyway fiddle with the privacy screen directly no matter what we say, and we can't prevent that. From their POV it's easier for them to do their value-add in components they have total control over. I emphatize with that view, even if it's counter-productive from the Linux ecosystem POV. So we'll just have to deal with it. Ack, at least that is the case for the current generation Lenovo devices. The other reliable option is that userspace must never be able to change privacy screen state, only the hardware hotkeys can. That, in turn, discourages anyone from doing the right thing, and blocks us from adding any nice additional features for privacy screens that only the userspace is capable of managing. For example, controlling privacy screen based on content, which seems like an obvious feature. Right. So we have the case here were both the firmware and userspace may change the privacyscreen state (on/off) at any time. This means that the atomic API use described by Pekka for this, where userspace keeps all properties in memory, updates the one which it wants to ch
Re: [PATCH 05/59] drm/vboxvidoe: use managed pci functions
Hi, On 4/15/20 7:44 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 05:03:55PM +0200, Hans de Goede wrote: Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Allows us to drop the cleanup code on the floor. Sam noticed in his review: With this change we avoid calling pci_disable_device() twise in case vbox_mm_init() fails. Once in vbox_hw_fini() and once in the error path. v2: Include Sam's review remarks Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede --- drivers/gpu/drm/vboxvideo/vbox_drv.c | 6 ++ drivers/gpu/drm/vboxvideo/vbox_main.c | 7 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index cfa4639c5142..cf2e3e6a2388 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -55,13 +55,13 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, vbox); mutex_init(>hw_mutex); - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; ret = vbox_hw_init(vbox); if (ret) - goto err_pci_disable; + return ret; ret = vbox_mm_init(vbox); if (ret) @@ -91,8 +91,6 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vbox_mm_fini(vbox); err_hw_fini: vbox_hw_fini(vbox); -err_pci_disable: - pci_disable_device(pdev); return ret; } diff --git a/drivers/gpu/drm/vboxvideo/vbox_main.c b/drivers/gpu/drm/vboxvideo/vbox_main.c index 9dcab115a261..1336ab9795fc 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_main.c +++ b/drivers/gpu/drm/vboxvideo/vbox_main.c @@ -71,8 +71,6 @@ static void vbox_accel_fini(struct vbox_private *vbox) for (i = 0; i < vbox->num_crtcs; ++i) vbva_disable(>vbva_info[i], vbox->guest_pool, i); - - pci_iounmap(vbox->ddev.pdev, vbox->vbva_buffers); } /* Do we support the 4.3 plus mode hint reporting interface? */ This seems to be missing the conversion of the iomap_range call to the devm equivalent ? : drivers/gpu/drm/vboxvideo/vbox_main.c 44: vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0, ... pcim_enable_device is pure magic, it converts _all_ pci_ calls on that device to the managed version. There's no other manged pci_ functions (ok 1-2 more, but they're rather special). Ah I see, magic indeed. Well with that explained, this is: Reviewed-by: Hans de Goede Regards, Hans @@ -125,7 +123,7 @@ int vbox_hw_init(struct vbox_private *vbox) /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ vbox->guest_pool = gen_pool_create(4, -1); if (!vbox->guest_pool) - goto err_unmap_guest_heap; + return -ENOMEM; ret = gen_pool_add_virt(vbox->guest_pool, (unsigned long)vbox->guest_heap, @@ -168,8 +166,6 @@ int vbox_hw_init(struct vbox_private *vbox) err_destroy_guest_pool: gen_pool_destroy(vbox->guest_pool); -err_unmap_guest_heap: - pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); return ret; } @@ -177,5 +173,4 @@ void vbox_hw_fini(struct vbox_private *vbox) { vbox_accel_fini(vbox); gen_pool_destroy(vbox->guest_pool); - pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); } ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 11:10 PM, Jani Nikula wrote: On Wed, 15 Apr 2020, Rajat Jain wrote: Hello, On Wed, Apr 15, 2020 at 8:40 AM Hans de Goede wrote: Hi, On 4/15/20 5:28 PM, Jani Nikula wrote: On Wed, 15 Apr 2020, Hans de Goede wrote: ii. Currently the "privacy-screen" property added by Rajat's patch-set is an enum with 2 possible values: "Enabled" "Disabled" We could add a third value "Not Available", which would be the default and then for internal panels always add the property so that we avoid the problem that detecting if the laptop has an internal privacy screen needs to be done before the connector is registered. Then we can add some hooks which allow an lcdshadow-driver to register itself against a connector later (which is non trivial wrt probe order, but lets ignore that for now). I regret dropping the ball on Rajat's series (sorry!). I do think having the connector property for this is the way to go. I 100% agree. Even if we couldn't necessarily figure out all the details on the kernel internal connections, can we settle on the property though, so we could move forward with Rajat's series? Thanks, it would be great!. Yes please, this will also allow us to move forward with userspace support even if for testing that we do some hacks for the kernel's internal connections for now. Moreover, do we actually need two properties, one which could indicate userspace's desire for the property, and another that tells the hardware state? No I do not think so. I would expect there to just be one property, I guess that if the state is (partly) firmware controlled then there might be a race, but we will need a notification mechanism (*) for firmware triggered state changes anyways, so shortly after loosing the race userspace will process the notification and it will know about it. I agree with Hans here that I think it would be better if we could do it with one property. * I can imagine demand for laptops that have a "hardware kill switch" for privacy screen (just like there are for camera etc today). So I think in future we may have to deal with this case anyway. In such devices it's the hardware (as opposite to firmware) that will change the state. The HW will likely provide an interrupt to the software to notify of the change. This is all imaginative at this point though. * I think having 2 properties might be a confusing UAPI. Also, we have existing properties like link-status that can be changed by both the user and the hardware. I think the consensus is that all properties that get changed by both userspace and the kernel are mistakes, and the way to handle it is to have two properties. But the actual privacy screen has only 1 state, having two properties for this will only be confusing. As I mentioned before we have a similar case with e.g. keyboard backlighting and there the userspace API also has a single sysfs attribute for the brightness, with change notifications to userspace if the firmware changes the brightness on its own and this works well. What would the semantics of these 2 different properties be? And what sort of extra functionality would these semantics offer which the single property versions does not offer? Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 8:29 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 8:19 PM Hans de Goede wrote: Hi, On 4/15/20 7:54 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 03:02:53PM +0200, Hans de Goede wrote: Hi, On 4/15/20 2:01 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 01:39:23PM +0200, Hans de Goede wrote: Hi, On 4/15/20 12:22 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 12:11 PM Hans de Goede wrote: Hi, On 4/15/20 11:52 AM, Daniel Vetter wrote: iv. What every SoC subsystem does: - lcdshadow drivers register drivers - drm drivers look them up - if stuff isn't there yet, we delay loading with EPROBE_DEFER until the entire thing is assembled. That's what we're doing already for other standardized components like drm_bridge or drm_panel, and I think that's also the right approach for backlight and anything else like that. Hand-rolling our own EPROBE_DEFER handling, or some other duct-tape monsters imo just leads to real pain. Also, with EPROBE_DEFER we have one standard way of building a driver from component, which spans subsystems and is also the underlying magic that makes stuff like component.c work. On the SoCs we have devicetree telling us what components there are, so we can wait for them to show up. The only way to figure out if the lcdshadow thing is there on a ThinkPad is asking thinkpad_acpi, or duplicating a lot of code from thinkpad_acpi. Edit: also see below for a possible solution. Yup it sucks. I think all we can do is have a small acpi match function (which yes will duplicate some of the thinkpad_acpi driver logic) to re-create that information and give us a "should we have a lcdshadow driver for this $pci_device" answer. Ok, so questions about this solution: 1. Where should that match-function live ? 2. An acpi_thinkpad derived match-function will only be able to answer if there is an lcdshadow device/driver for the internal panel. It will not be able to tie this info to a certain PCI device. My plan is to pass NULL as dev_name when registering the lcdshadow-device and have lcdshadow_get(dev, ) skip device-name matching (consider everything a match) for lcdshadow-devices registered with NULL as dev_name. So I guess in this case the mini match function should just ignore the passed in device? Yeah I think we can't really avoid that. I also expect that we'll need ACPI and dt versions of this, and driver needs to know which one to call. Since at least in a dt world the driver knows exactly for which dt node it needs a lcdshadow driver for (with the phandle stuff), so we can be a lot more strict. For the acpi version I'm not even sure we can do more than provide the struct device * pointer of the gpu. I think if we ever get more than 1 lcdshadow driver on acpi systems we can add more stuff later on, for now I'd just leave that out. So maybe acpi_lcdshadow_get(struct device *dev); of_lcdshadow_get(struct device_node *np); And with maybe a future plan to add some kind of enum or whatever to acpi_lcdshadow_get(). Both would return either the lcdshadow pointer, or an PTR_ERR() so that we could encode EPROBE_DEFER vs ENOENT. Ok, note I plan to only implement the acpi version for now, I do expect some non ACPI/x86 devices to show up with his feature eventually but I believe it is best to implement this once those actually show up. Esp. since this will also involve adding some devicetree bindings for this. ofc, just wanted to lay out the entire thing. The DT version needs some good bikeshed on the dt schema first anyway (so that the helper can decode that directly). We might also want a low-level lcdshadow_get() which only returns ENOENT when the driver isn't there, and which leaves "do we really need one?" to higher levels to answer. Right, so my latest idea on that is indeed a high-level lcdshadow_get() which takes a struct device * and a connector-name and which never returns EPROBE_DEFER. As for leaving things to the higher levels to answer, as explained in my other follow-up email I think that we should probably add a lcdshadow_probe_defer() helper for this and call that early on in the PCI-driver probe functions for the 3 major x86 GPU drivers. Does that sound ok to you? Uh ... not pretty. There's still a lifetime problem that strictly speaking there's nothing stopping the other driver from getting unloaded between your _probe_defer and the subsequent _get. I think fixing this properly (and screaming a bit at the error code, oh well) is better. I would really like to separate the discussion and the work on getting the 3 major x86 GPU drivers ready to deal with EPROBE_DEFER from the lcdshadow discussion and work. I expect getting these 3 drivers ready for EPROBE_DEFER is going to be a major undertaking and I would like avoid introducing this significant scope creep to the lcdshadow discussion, because it simply is a too big undertaking to undertake without us g
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 7:54 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 03:02:53PM +0200, Hans de Goede wrote: Hi, On 4/15/20 2:01 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 01:39:23PM +0200, Hans de Goede wrote: Hi, On 4/15/20 12:22 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 12:11 PM Hans de Goede wrote: Hi, On 4/15/20 11:52 AM, Daniel Vetter wrote: iv. What every SoC subsystem does: - lcdshadow drivers register drivers - drm drivers look them up - if stuff isn't there yet, we delay loading with EPROBE_DEFER until the entire thing is assembled. That's what we're doing already for other standardized components like drm_bridge or drm_panel, and I think that's also the right approach for backlight and anything else like that. Hand-rolling our own EPROBE_DEFER handling, or some other duct-tape monsters imo just leads to real pain. Also, with EPROBE_DEFER we have one standard way of building a driver from component, which spans subsystems and is also the underlying magic that makes stuff like component.c work. On the SoCs we have devicetree telling us what components there are, so we can wait for them to show up. The only way to figure out if the lcdshadow thing is there on a ThinkPad is asking thinkpad_acpi, or duplicating a lot of code from thinkpad_acpi. Edit: also see below for a possible solution. Yup it sucks. I think all we can do is have a small acpi match function (which yes will duplicate some of the thinkpad_acpi driver logic) to re-create that information and give us a "should we have a lcdshadow driver for this $pci_device" answer. Ok, so questions about this solution: 1. Where should that match-function live ? 2. An acpi_thinkpad derived match-function will only be able to answer if there is an lcdshadow device/driver for the internal panel. It will not be able to tie this info to a certain PCI device. My plan is to pass NULL as dev_name when registering the lcdshadow-device and have lcdshadow_get(dev, ) skip device-name matching (consider everything a match) for lcdshadow-devices registered with NULL as dev_name. So I guess in this case the mini match function should just ignore the passed in device? Yeah I think we can't really avoid that. I also expect that we'll need ACPI and dt versions of this, and driver needs to know which one to call. Since at least in a dt world the driver knows exactly for which dt node it needs a lcdshadow driver for (with the phandle stuff), so we can be a lot more strict. For the acpi version I'm not even sure we can do more than provide the struct device * pointer of the gpu. I think if we ever get more than 1 lcdshadow driver on acpi systems we can add more stuff later on, for now I'd just leave that out. So maybe acpi_lcdshadow_get(struct device *dev); of_lcdshadow_get(struct device_node *np); And with maybe a future plan to add some kind of enum or whatever to acpi_lcdshadow_get(). Both would return either the lcdshadow pointer, or an PTR_ERR() so that we could encode EPROBE_DEFER vs ENOENT. Ok, note I plan to only implement the acpi version for now, I do expect some non ACPI/x86 devices to show up with his feature eventually but I believe it is best to implement this once those actually show up. Esp. since this will also involve adding some devicetree bindings for this. ofc, just wanted to lay out the entire thing. The DT version needs some good bikeshed on the dt schema first anyway (so that the helper can decode that directly). We might also want a low-level lcdshadow_get() which only returns ENOENT when the driver isn't there, and which leaves "do we really need one?" to higher levels to answer. Right, so my latest idea on that is indeed a high-level lcdshadow_get() which takes a struct device * and a connector-name and which never returns EPROBE_DEFER. As for leaving things to the higher levels to answer, as explained in my other follow-up email I think that we should probably add a lcdshadow_probe_defer() helper for this and call that early on in the PCI-driver probe functions for the 3 major x86 GPU drivers. Does that sound ok to you? Uh ... not pretty. There's still a lifetime problem that strictly speaking there's nothing stopping the other driver from getting unloaded between your _probe_defer and the subsequent _get. I think fixing this properly (and screaming a bit at the error code, oh well) is better. I would really like to separate the discussion and the work on getting the 3 major x86 GPU drivers ready to deal with EPROBE_DEFER from the lcdshadow discussion and work. I expect getting these 3 drivers ready for EPROBE_DEFER is going to be a major undertaking and I would like avoid introducing this significant scope creep to the lcdshadow discussion, because it simply is a too big undertaking to undertake without us getting a significant amount of manpower specifically for this from somewhere. Note I do agree with you that get
Re: [External] Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi Mark, On 4/15/20 7:14 PM, Mark Pearson wrote: Hi, -Original Message- From: Hans de Goede Sent: Wednesday, April 15, 2020 11:41 AM On 4/15/20 5:28 PM, Jani Nikula wrote: On Wed, 15 Apr 2020, Hans de Goede wrote: Moreover, do we actually need two properties, one which could indicate userspace's desire for the property, and another that tells the hardware state? No I do not think so. I would expect there to just be one property, I guess that if the state is (partly) firmware controlled then there might be a race, but we will need a notification mechanism (*) for firmware triggered state changes anyways, so shortly after loosing the race userspace will process the notification and it will know about it. One thing which might be useful is a way to signal that the property is read-only in case we ever hit hw where that is the case. I'd so very much like to have no in-kernel/in-firmware shortcuts to enable/disable the privacy screen, and instead have any hardware buttons just be events that the userspace could react to. However I don't think that'll be the case unfortunately. In my experience with keyboard-backlight support, we will (unfortunately) see a mix and in some case we will get a notification that the firmware has adjusted the state, rather then just getting a keypress and dealing with that ourselves. In some cases we may even be able to choose, so the fw will deal with it by default but we can ask it to just send a key-press. But I do believe that we can *not* expect that we will always just get a keypress for userspace to deal with. Afraid, the "hotkeys" control for ePrivacy (Fn+D I believe) is very unlikely to change - Windows uses it as well... We can do notification of any hotkey presses to update the DRM layer (and userspace) if that helps We are not asking for changing the hotkey, what we would like is for the hotkey to only send a notification that it was pressed and for it to not actually do anything with the ePrivacy screen state. This does not need to be it defaults behavior, but we would like to be able to ask the firmware to not act on it itself, just like we can already disable the firmware/embedded controller responding to e.g. brightness up/down key presses itself. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 5:28 PM, Jani Nikula wrote: On Wed, 15 Apr 2020, Hans de Goede wrote: ii. Currently the "privacy-screen" property added by Rajat's patch-set is an enum with 2 possible values: "Enabled" "Disabled" We could add a third value "Not Available", which would be the default and then for internal panels always add the property so that we avoid the problem that detecting if the laptop has an internal privacy screen needs to be done before the connector is registered. Then we can add some hooks which allow an lcdshadow-driver to register itself against a connector later (which is non trivial wrt probe order, but lets ignore that for now). I regret dropping the ball on Rajat's series (sorry!). I do think having the connector property for this is the way to go. I 100% agree. Even if we couldn't necessarily figure out all the details on the kernel internal connections, can we settle on the property though, so we could move forward with Rajat's series? Yes please, this will also allow us to move forward with userspace support even if for testing that we do some hacks for the kernel's internal connections for now. Moreover, do we actually need two properties, one which could indicate userspace's desire for the property, and another that tells the hardware state? No I do not think so. I would expect there to just be one property, I guess that if the state is (partly) firmware controlled then there might be a race, but we will need a notification mechanism (*) for firmware triggered state changes anyways, so shortly after loosing the race userspace will process the notification and it will know about it. One thing which might be useful is a way to signal that the property is read-only in case we ever hit hw where that is the case. I'd so very much like to have no in-kernel/in-firmware shortcuts to enable/disable the privacy screen, and instead have any hardware buttons just be events that the userspace could react to. However I don't think that'll be the case unfortunately. In my experience with keyboard-backlight support, we will (unfortunately) see a mix and in some case we will get a notification that the firmware has adjusted the state, rather then just getting a keypress and dealing with that ourselves. In some cases we may even be able to choose, so the fw will deal with it by default but we can ask it to just send a key-press. But I do believe that we can *not* expect that we will always just get a keypress for userspace to deal with. Regards, Hans *) Some udev event I guess, I sorta assume there already is a notification mechanism for property change notifications ? ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 22/59] drm/gm12u320: Don't use drm_device->dev_private
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Upcasting using a container_of macro is more typesafe, faster and easier for the compiler to optimize. Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/tiny/gm12u320.c | 11 ++- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 907739a67bf6..cc397671f689 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -98,6 +98,8 @@ struct gm12u320_device { } fb_update; }; +#define to_gm12u320(__dev) container_of(__dev, struct gm12u320_device, dev) + static const char cmd_data[CMD_SIZE] = { 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, @@ -408,7 +410,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, struct drm_rect *dirty) { - struct gm12u320_device *gm12u320 = fb->dev->dev_private; + struct gm12u320_device *gm12u320 = to_gm12u320(fb->dev); struct drm_framebuffer *old_fb = NULL; bool wakeup = false; @@ -558,7 +560,7 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) { struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT }; - struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; + struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev); gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT; gm12u320_fb_mark_dirty(plane_state->fb, ); @@ -566,7 +568,7 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) { - struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; + struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev); gm12u320_stop_fb_update(gm12u320); } @@ -641,7 +643,6 @@ static int gm12u320_usb_probe(struct usb_interface *interface, mutex_init(>fb_update.lock); dev = >dev; - dev->dev_private = gm12u320; ret = drmm_mode_config_init(dev); if (ret) @@ -706,7 +707,7 @@ static __maybe_unused int gm12u320_suspend(struct usb_interface *interface, static __maybe_unused int gm12u320_resume(struct usb_interface *interface) { struct drm_device *dev = usb_get_intfdata(interface); - struct gm12u320_device *gm12u320 = dev->dev_private; + struct gm12u320_device *gm12u320 = to_gm12u320(dev); gm12u320_set_ecomode(gm12u320); ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 21/59] drm/gm12u320: Use devm_drm_dev_alloc
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Already using devm_drm_dev_init, so very simple replacment. Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/tiny/gm12u320.c | 13 - 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 6f0ea2827d62..907739a67bf6 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -631,22 +631,17 @@ static int gm12u320_usb_probe(struct usb_interface *interface, if (interface->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; - gm12u320 = kzalloc(sizeof(*gm12u320), GFP_KERNEL); - if (gm12u320 == NULL) - return -ENOMEM; + gm12u320 = devm_drm_dev_alloc(>dev, _drm_driver, + struct gm12u320_device, dev); + if (IS_ERR(gm12u320)) + return PTR_ERR(gm12u320); gm12u320->udev = interface_to_usbdev(interface); INIT_DELAYED_WORK(>fb_update.work, gm12u320_fb_update_work); mutex_init(>fb_update.lock); dev = >dev; - ret = devm_drm_dev_init(>dev, dev, _drm_driver); - if (ret) { - kfree(gm12u320); - return ret; - } dev->dev_private = gm12u320; - drmm_add_final_kfree(dev, gm12u320); ret = drmm_mode_config_init(dev); if (ret) ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 06/59] drm/vboxvideo: Use devm_gen_pool_create
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Aside from deleting all the cleanup code we're now also setting a name for the pool Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/vboxvideo/vbox_main.c | 22 -- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_main.c b/drivers/gpu/drm/vboxvideo/vbox_main.c index 1336ab9795fc..d68d9bad7674 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_main.c +++ b/drivers/gpu/drm/vboxvideo/vbox_main.c @@ -121,7 +121,8 @@ int vbox_hw_init(struct vbox_private *vbox) return -ENOMEM; /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ - vbox->guest_pool = gen_pool_create(4, -1); + vbox->guest_pool = devm_gen_pool_create(vbox->ddev.dev, 4, -1, + "vboxvideo-accel"); if (!vbox->guest_pool) return -ENOMEM; @@ -130,12 +131,12 @@ int vbox_hw_init(struct vbox_private *vbox) GUEST_HEAP_OFFSET(vbox), GUEST_HEAP_USABLE_SIZE, -1); if (ret) - goto err_destroy_guest_pool; + return ret; ret = hgsmi_test_query_conf(vbox->guest_pool); if (ret) { DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); - goto err_destroy_guest_pool; + return ret; } /* Reduce available VRAM size to reflect the guest heap. */ @@ -147,30 +148,23 @@ int vbox_hw_init(struct vbox_private *vbox) if (!have_hgsmi_mode_hints(vbox)) { ret = -ENOTSUPP; - goto err_destroy_guest_pool; + return ret; } vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, sizeof(struct vbva_modehint), GFP_KERNEL); - if (!vbox->last_mode_hints) { - ret = -ENOMEM; - goto err_destroy_guest_pool; - } + if (!vbox->last_mode_hints) + return -ENOMEM; ret = vbox_accel_init(vbox); if (ret) - goto err_destroy_guest_pool; + return ret; return 0; - -err_destroy_guest_pool: - gen_pool_destroy(vbox->guest_pool); - return ret; } void vbox_hw_fini(struct vbox_private *vbox) { vbox_accel_fini(vbox); - gen_pool_destroy(vbox->guest_pool); } ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 05/59] drm/vboxvidoe: use managed pci functions
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Allows us to drop the cleanup code on the floor. Sam noticed in his review: With this change we avoid calling pci_disable_device() twise in case vbox_mm_init() fails. Once in vbox_hw_fini() and once in the error path. v2: Include Sam's review remarks Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede --- drivers/gpu/drm/vboxvideo/vbox_drv.c | 6 ++ drivers/gpu/drm/vboxvideo/vbox_main.c | 7 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index cfa4639c5142..cf2e3e6a2388 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -55,13 +55,13 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, vbox); mutex_init(>hw_mutex); - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; ret = vbox_hw_init(vbox); if (ret) - goto err_pci_disable; + return ret; ret = vbox_mm_init(vbox); if (ret) @@ -91,8 +91,6 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vbox_mm_fini(vbox); err_hw_fini: vbox_hw_fini(vbox); -err_pci_disable: - pci_disable_device(pdev); return ret; } diff --git a/drivers/gpu/drm/vboxvideo/vbox_main.c b/drivers/gpu/drm/vboxvideo/vbox_main.c index 9dcab115a261..1336ab9795fc 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_main.c +++ b/drivers/gpu/drm/vboxvideo/vbox_main.c @@ -71,8 +71,6 @@ static void vbox_accel_fini(struct vbox_private *vbox) for (i = 0; i < vbox->num_crtcs; ++i) vbva_disable(>vbva_info[i], vbox->guest_pool, i); - - pci_iounmap(vbox->ddev.pdev, vbox->vbva_buffers); } /* Do we support the 4.3 plus mode hint reporting interface? */ This seems to be missing the conversion of the iomap_range call to the devm equivalent ? : drivers/gpu/drm/vboxvideo/vbox_main.c 44: vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0, ... Regards, Hans @@ -125,7 +123,7 @@ int vbox_hw_init(struct vbox_private *vbox) /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ vbox->guest_pool = gen_pool_create(4, -1); if (!vbox->guest_pool) - goto err_unmap_guest_heap; + return -ENOMEM; ret = gen_pool_add_virt(vbox->guest_pool, (unsigned long)vbox->guest_heap, @@ -168,8 +166,6 @@ int vbox_hw_init(struct vbox_private *vbox) err_destroy_guest_pool: gen_pool_destroy(vbox->guest_pool); -err_unmap_guest_heap: - pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); return ret; } @@ -177,5 +173,4 @@ void vbox_hw_fini(struct vbox_private *vbox) { vbox_accel_fini(vbox); gen_pool_destroy(vbox->guest_pool); - pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); } ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 02/59] drm/vboxvideo: drop DRM_MTRR_WC #define
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Doesn't apply to upstream kernels since a rather long time. Acked-by: Sam Ravnborg Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/vboxvideo/vbox_ttm.c | 12 1 file changed, 12 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_ttm.c b/drivers/gpu/drm/vboxvideo/vbox_ttm.c index 976423d0c3cc..f5a06675da43 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_ttm.c +++ b/drivers/gpu/drm/vboxvideo/vbox_ttm.c @@ -24,25 +24,13 @@ int vbox_mm_init(struct vbox_private *vbox) return ret; } -#ifdef DRM_MTRR_WC - vbox->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), -pci_resource_len(dev->pdev, 0), -DRM_MTRR_WC); -#else vbox->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); -#endif return 0; } void vbox_mm_fini(struct vbox_private *vbox) { -#ifdef DRM_MTRR_WC - drm_mtrr_del(vbox->fb_mtrr, -pci_resource_start(vbox->ddev.pdev, 0), -pci_resource_len(vbox->ddev.pdev, 0), DRM_MTRR_WC); -#else arch_phys_wc_del(vbox->fb_mtrr); -#endif drm_vram_helper_release_mm(>ddev); } ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 03/59] drm/vboxvideo: Use devm_drm_dev_alloc
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: Straightforward conversion. Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/vboxvideo/vbox_drv.c | 19 +-- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 282348e071fe..7dd25c7a3768 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -46,25 +46,19 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; - vbox = kzalloc(sizeof(*vbox), GFP_KERNEL); - if (!vbox) - return -ENOMEM; - - ret = drm_dev_init(>ddev, , >dev); - if (ret) { - kfree(vbox); - return ret; - } + vbox = devm_drm_dev_alloc(>dev, , + struct vbox_private, ddev); + if (IS_ERR(vbox)) + return PTR_ERR(vbox); vbox->ddev.pdev = pdev; vbox->ddev.dev_private = vbox; pci_set_drvdata(pdev, vbox); - drmm_add_final_kfree(>ddev, vbox); mutex_init(>hw_mutex); ret = pci_enable_device(pdev); if (ret) - goto err_dev_put; + return ret; ret = vbox_hw_init(vbox); if (ret) @@ -100,8 +94,6 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vbox_hw_fini(vbox); err_pci_disable: pci_disable_device(pdev); -err_dev_put: - drm_dev_put(>ddev); return ret; } @@ -114,7 +106,6 @@ static void vbox_pci_remove(struct pci_dev *pdev) vbox_mode_fini(vbox); vbox_mm_fini(vbox); vbox_hw_fini(vbox); - drm_dev_put(>ddev); } #ifdef CONFIG_PM_SLEEP ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 04/59] drm/vboxvideo: Stop using drm_device->dev_private
Hi, On 4/15/20 9:39 AM, Daniel Vetter wrote: We use the baseclass pattern here, so lets to the proper (and more typesafe) upcasting. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Daniel Vetter Cc: Hans de Goede LGTM: Reviewed-by: Hans de Goede Regards, Hans --- drivers/gpu/drm/vboxvideo/vbox_drv.c | 1 - drivers/gpu/drm/vboxvideo/vbox_drv.h | 1 + drivers/gpu/drm/vboxvideo/vbox_irq.c | 2 +- drivers/gpu/drm/vboxvideo/vbox_mode.c | 10 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 7dd25c7a3768..cfa4639c5142 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -52,7 +52,6 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return PTR_ERR(vbox); vbox->ddev.pdev = pdev; - vbox->ddev.dev_private = vbox; pci_set_drvdata(pdev, vbox); mutex_init(>hw_mutex); diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.h b/drivers/gpu/drm/vboxvideo/vbox_drv.h index 87421903816c..ac7c2effc46f 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.h +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.h @@ -127,6 +127,7 @@ struct vbox_encoder { #define to_vbox_crtc(x) container_of(x, struct vbox_crtc, base) #define to_vbox_connector(x) container_of(x, struct vbox_connector, base) #define to_vbox_encoder(x) container_of(x, struct vbox_encoder, base) +#define to_vbox_dev(x) container_of(x, struct vbox_private, ddev) bool vbox_check_supported(u16 id); int vbox_hw_init(struct vbox_private *vbox); diff --git a/drivers/gpu/drm/vboxvideo/vbox_irq.c b/drivers/gpu/drm/vboxvideo/vbox_irq.c index 16a1e29f5292..631657fa554f 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_irq.c +++ b/drivers/gpu/drm/vboxvideo/vbox_irq.c @@ -34,7 +34,7 @@ void vbox_report_hotplug(struct vbox_private *vbox) irqreturn_t vbox_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *)arg; - struct vbox_private *vbox = (struct vbox_private *)dev->dev_private; + struct vbox_private *vbox = to_vbox_dev(dev); u32 host_flags = vbox_get_flags(vbox); if (!(host_flags & HGSMIHOSTFLAGS_IRQ)) diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index 0883a435e62b..d9a5af62af89 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -36,7 +36,7 @@ static void vbox_do_modeset(struct drm_crtc *crtc) u16 flags; s32 x_offset, y_offset; - vbox = crtc->dev->dev_private; + vbox = to_vbox_dev(crtc->dev); width = vbox_crtc->width ? vbox_crtc->width : 640; height = vbox_crtc->height ? vbox_crtc->height : 480; bpp = fb ? fb->format->cpp[0] * 8 : 32; @@ -77,7 +77,7 @@ static void vbox_do_modeset(struct drm_crtc *crtc) static int vbox_set_view(struct drm_crtc *crtc) { struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); - struct vbox_private *vbox = crtc->dev->dev_private; + struct vbox_private *vbox = to_vbox_dev(crtc->dev); struct vbva_infoview *p; /* @@ -174,7 +174,7 @@ static void vbox_crtc_set_base_and_mode(struct drm_crtc *crtc, int x, int y) { struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(fb->obj[0]); - struct vbox_private *vbox = crtc->dev->dev_private; + struct vbox_private *vbox = to_vbox_dev(crtc->dev); struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); bool needs_modeset = drm_atomic_crtc_needs_modeset(crtc->state); @@ -272,7 +272,7 @@ static void vbox_primary_atomic_update(struct drm_plane *plane, { struct drm_crtc *crtc = plane->state->crtc; struct drm_framebuffer *fb = plane->state->fb; - struct vbox_private *vbox = fb->dev->dev_private; + struct vbox_private *vbox = to_vbox_dev(fb->dev); struct drm_mode_rect *clips; uint32_t num_clips, i; @@ -704,7 +704,7 @@ static int vbox_get_modes(struct drm_connector *connector) int preferred_width, preferred_height; vbox_connector = to_vbox_connector(connector); - vbox = connector->dev->dev_private; + vbox = to_vbox_dev(connector->dev); hgsmi_report_flags_location(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) + HOST_FLAGS_OFFSET); ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 2:01 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 01:39:23PM +0200, Hans de Goede wrote: Hi, On 4/15/20 12:22 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 12:11 PM Hans de Goede wrote: Hi, On 4/15/20 11:52 AM, Daniel Vetter wrote: iv. What every SoC subsystem does: - lcdshadow drivers register drivers - drm drivers look them up - if stuff isn't there yet, we delay loading with EPROBE_DEFER until the entire thing is assembled. That's what we're doing already for other standardized components like drm_bridge or drm_panel, and I think that's also the right approach for backlight and anything else like that. Hand-rolling our own EPROBE_DEFER handling, or some other duct-tape monsters imo just leads to real pain. Also, with EPROBE_DEFER we have one standard way of building a driver from component, which spans subsystems and is also the underlying magic that makes stuff like component.c work. On the SoCs we have devicetree telling us what components there are, so we can wait for them to show up. The only way to figure out if the lcdshadow thing is there on a ThinkPad is asking thinkpad_acpi, or duplicating a lot of code from thinkpad_acpi. Edit: also see below for a possible solution. Yup it sucks. I think all we can do is have a small acpi match function (which yes will duplicate some of the thinkpad_acpi driver logic) to re-create that information and give us a "should we have a lcdshadow driver for this $pci_device" answer. Ok, so questions about this solution: 1. Where should that match-function live ? 2. An acpi_thinkpad derived match-function will only be able to answer if there is an lcdshadow device/driver for the internal panel. It will not be able to tie this info to a certain PCI device. My plan is to pass NULL as dev_name when registering the lcdshadow-device and have lcdshadow_get(dev, ) skip device-name matching (consider everything a match) for lcdshadow-devices registered with NULL as dev_name. So I guess in this case the mini match function should just ignore the passed in device? Yeah I think we can't really avoid that. I also expect that we'll need ACPI and dt versions of this, and driver needs to know which one to call. Since at least in a dt world the driver knows exactly for which dt node it needs a lcdshadow driver for (with the phandle stuff), so we can be a lot more strict. For the acpi version I'm not even sure we can do more than provide the struct device * pointer of the gpu. I think if we ever get more than 1 lcdshadow driver on acpi systems we can add more stuff later on, for now I'd just leave that out. So maybe acpi_lcdshadow_get(struct device *dev); of_lcdshadow_get(struct device_node *np); And with maybe a future plan to add some kind of enum or whatever to acpi_lcdshadow_get(). Both would return either the lcdshadow pointer, or an PTR_ERR() so that we could encode EPROBE_DEFER vs ENOENT. Ok, note I plan to only implement the acpi version for now, I do expect some non ACPI/x86 devices to show up with his feature eventually but I believe it is best to implement this once those actually show up. Esp. since this will also involve adding some devicetree bindings for this. We might also want a low-level lcdshadow_get() which only returns ENOENT when the driver isn't there, and which leaves "do we really need one?" to higher levels to answer. Right, so my latest idea on that is indeed a high-level lcdshadow_get() which takes a struct device * and a connector-name and which never returns EPROBE_DEFER. As for leaving things to the higher levels to answer, as explained in my other follow-up email I think that we should probably add a lcdshadow_probe_defer() helper for this and call that early on in the PCI-driver probe functions for the 3 major x86 GPU drivers. Does that sound ok to you? I'd also lean towards putting lcdshadow headers/interfaces into drivers/gpu, Ack, I think we should even make this drm specific and prefix it with drm_ so that we get drm_lcdshadow_foo as functions, just to make clear that this is maintained together with the other drm bits. But my question about "where should this live" was mainly about the light weight match helpers you suggested to use to figure out if the device supports lcdshadow at all (and we thus should wait for a driver) or not. E.g. I can see us adding a: drivers/gpu/drm/drm_lcdshadow.c file for the core bits and then maybe a: drivers/gpu/drm/drm_lcdshadow_detect.c file with the light weight match helpers, with each helper wrapped in #if IS_ENABLED(CONFIG_THINKPAD_ACPI), etc. ? with driver implementations all over. Ack. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 1:39 PM, Hans de Goede wrote: /* Add comment explaining why we need this messy stuff here */ const char * const shadow_providers[] = { #ifdef CONFIG_THINKPAD_ACPI_MODULE "thinkpad_acpi", #endif #ifdef CONFIG_OTHER_MODULE "other", #endif NULL }; int module_init(void) { /* do actual setup of the ?class? */ for (i = 0; shadow_providers[i]; i++) request_module(shadow_providers[i]); return 0; } Hm I think explicitly loading drivers feels very much not device model like. Don't these drivers auto-load, matching on acpi functions? thinkpad_acpi does autoload based on a number of ACPI device-ids, the idea behind the above request_module is to avoid the need to have the acpi-match function you mentioned above. Basically what would happen is e.g. : 1. i915 loads, calls lcdshadow_get(dev, "eDP-1"); 2. if this is the first lcdshadow_get() call then the lcdshadow core will do the request_module calls, allowing any of these modules to get loaded + probed and call e.g. lcdshadow_register(, , "eDP-1"); 3. After the request modules the lcdshadow_get() will walk over the list of registered devices, including the ones just registered by the request_module calls and then hopefully find a match So by doing the request-module calls before checking for a matching lcdshadow dev, we avoid the need of having some of the knowledge currently abstracted away in the thinkpad_acpi driver duplicated inside the drm code somewhere. I guess if that doesn't exist, then we'd need to fix that one first :-/ In general no request_module please, that shouldn't be needed either. The trouble with request_module is also that (afaiui) it doesn't really work well with parallel module load and all that, for EPROBE_DEFER to work we do need to be able to answer "should we have a driver?" independently of whether that driver has loaded already or not. The idea here is to avoid using EPROBE_DEFER (on x86 at least) and either directly return the lcdshadow_dev or ENOENT. Also see below. Assuming we are going to add some device/model specific lcdshadow knowledge inside the lcdshadow core as you suggested with your "small acpi match function" above, we could do something similar to what the vga_switcheroo code is doing for this and have a lcdshadow_defer_probe() helper and call that really early in i915_pci_probe(), which currently already has this for the vgaswitcheroo case: if (vga_switcheroo_client_probe_defer(pdev)) return -EPROBE_DEFER; So thinking more about this and given the total lack of EPROBE_DEFER handling in the 3 major X86 GPU/kms drivers I think that adding a lcdshadow_defer_probe() helper is the way to go. This will also avoid the need for duplicating the lcdshadow detect functionality in the small ACPI-match functions you mentioned (although that might still be interesting to speedup the boot). When everything is builtin then each enabled "module"-s module_init function will get called, we can call a lcdshadow_probe_done("module-name") function from those and the lcdshadow core can then track if all potential lcdhadow providers have initialized before it stops returning non 0 from lcdshadow_defer_probe(). Or if we still do the small match functions it could be even smarter with this... And for the modular case it can call request_module on all (enabled as module) potential lcdhadow providers (or again we could rely on the small match function instead). Then (on x86 at least) we can have lcdshadow_get never return -EPROBE_DEFER and avoid the need to solve the lack of EPROBE_DEFER support in the 3 major x86 drivers. And this is all kernel internal, so if that lack of EPROBE_DEFER support ever gets fixed then we can drop the lcdshadow_defer_probe() hack and make lcdshadow_get also return -EPROBE_DEFER on x86 in some cases. Regards, Hans ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: RFC: Drm-connector properties managed by another driver / privacy screen support
Hi, On 4/15/20 12:22 PM, Daniel Vetter wrote: On Wed, Apr 15, 2020 at 12:11 PM Hans de Goede wrote: Hi, On 4/15/20 11:52 AM, Daniel Vetter wrote: iv. What every SoC subsystem does: - lcdshadow drivers register drivers - drm drivers look them up - if stuff isn't there yet, we delay loading with EPROBE_DEFER until the entire thing is assembled. That's what we're doing already for other standardized components like drm_bridge or drm_panel, and I think that's also the right approach for backlight and anything else like that. Hand-rolling our own EPROBE_DEFER handling, or some other duct-tape monsters imo just leads to real pain. Also, with EPROBE_DEFER we have one standard way of building a driver from component, which spans subsystems and is also the underlying magic that makes stuff like component.c work. On the SoCs we have devicetree telling us what components there are, so we can wait for them to show up. The only way to figure out if the lcdshadow thing is there on a ThinkPad is asking thinkpad_acpi, or duplicating a lot of code from thinkpad_acpi. Edit: also see below for a possible solution. Yup it sucks. I think all we can do is have a small acpi match function (which yes will duplicate some of the thinkpad_acpi driver logic) to re-create that information and give us a "should we have a lcdshadow driver for this $pci_device" answer. Ok, so questions about this solution: 1. Where should that match-function live ? 2. An acpi_thinkpad derived match-function will only be able to answer if there is an lcdshadow device/driver for the internal panel. It will not be able to tie this info to a certain PCI device. My plan is to pass NULL as dev_name when registering the lcdshadow-device and have lcdshadow_get(dev, ) skip device-name matching (consider everything a match) for lcdshadow-devices registered with NULL as dev_name. So I guess in this case the mini match function should just ignore the passed in device? This need for an in-kernel source of truth for "which backlight, if any, do I need" is why the backlight stuff never got fixed. It's a really hard problem, and doing the table flip and just letting userspace deal with the mess at least avoids having to face the fact that the kernel is totally failing here. It's made worse for backlight because of 20 years of hacked up systems that "work", and we can't regress any of them. Right, as discussed during last plumbers Christian Kellner and I have written a plan to slowly resolve this. Unfortunately Christian has not found the time to work on this yet. I really want to avoid that situation for anything new like lcdshadow. Ack. Wrt the actual userspace interface, I think the drm core should handle this as much as possible. Same way we let drm core handle a lot of the atomic property stuff, to make sure things are standardized. Agreed. So - add an lcdshadow pointer to struct drm_connector - implement the property glue code in drm core completely, at least for the read side - for the write side we might want to have some drm wrappers drivers can call to at the appropriate times to e.g. restore privacy screen settings when the panel gets enabled. In case that's needed. Also one thing that the EPROBE_DEFER stuff forces us to handle correctly is to track these depencies. That's the other nightmare in backlight land, essentially you have no idea of knowing (in userspace) whether the backlight driver you want is actually loaded, resulting in lots of fun. The kernel is the only thing that can know, and for hw that's built-in there's really no excuse to not know. So a model where stuff gets assembled after drm_dev_register() is imo just plain buggy. This means that the lcdshadow subsystem needs to have some idea of whether it needs a driver, so that it can correctly differentiate between EPROBE_DEFER and ENOENT error cases. In the latter the driver should continue loading ofc. Right, so how would the lcdshadow subsystem do this? The only way would be for it to say try and modprobe thinkpad_acpi from its core init function (if thinkpad_acpi is enabled). IOW it is the usual x86 mess. I guess we could have something like this in a theoretical to be added lcdshadow subsystem: /* Add comment explaining why we need this messy stuff here */ const char * const shadow_providers[] = { #ifdef CONFIG_THINKPAD_ACPI_MODULE "thinkpad_acpi", #endif #ifdef CONFIG_OTHER_MODULE "other", #endif NULL }; int module_init(void) { /* do actual setup of the ?class? */ for (i = 0; shadow_providers[i]; i++) request_module(shadow_providers[i]); return 0; } Hm I think explicitly loading drivers feels very much not device model like. Don't these drivers auto-load, matching on acpi functions? thinkpad_acpi does autoload based on a number of ACPI device-ids, the idea behind the above request_module is to