From b7a0c7c5ba289a7d063d92c162b53e110757e331 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 24 Aug 2016 01:42:54 +0900 Subject: [PATCH 01/24] drm/gma500: remove unnecessary config_enabled() guard Commit d112a8163f83 ("gma500/cdv: Add eDP support") replaced the code inside this if-conditional with gma_backlight_set(), which becomes a nop stub if CONFIG_BACKLIGHT_CLASS_DEVICE is disabled. So, there is no need to guard the caller with config_enabled(). Note: This is one of remaining TODOs to deprecate config_enabled() macro. Refer to commit 97f2645f358b ("tree-wide: replace config_enabled() with IS_ENABLED()"). Signed-off-by: Masahiro Yamada Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1471970574-23906-1-git-send-email-yamada.masahiro@socionext.com --- drivers/gpu/drm/gma500/opregion.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index ab696ca7eeec..eab6d889bde9 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -163,10 +163,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { - int max = bd->props.max_brightness; - gma_backlight_set(dev, bclp * max / 255); - } + gma_backlight_set(dev, bclp * bd->props.max_brightness / 255); asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; From 20353335099b1b7cba63002fd53da5725a1fadba Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 24 Aug 2016 12:19:15 +0200 Subject: [PATCH 02/24] drm/doc: Fix vga_switcheroo xref Reported-by: Chris Wilson Cc: Chris Wilson Fixes: b754b35b089d ("vgaarbiter: rst-ifiy and polish kerneldoc") Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160824101916.30125-1-daniel.vetter@ffwll.ch --- Documentation/gpu/vga-switcheroo.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/gpu/vga-switcheroo.rst b/Documentation/gpu/vga-switcheroo.rst index 463a74fc40d1..cbbdb994f1dd 100644 --- a/Documentation/gpu/vga-switcheroo.rst +++ b/Documentation/gpu/vga-switcheroo.rst @@ -1,3 +1,5 @@ +.. _vga_switcheroo: + ============== VGA Switcheroo ============== From d92d9c3a14488e5af9d7015189f50e02346950f2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 4 Aug 2016 10:06:56 +0200 Subject: [PATCH 03/24] drm: hide legacy drivers with CONFIG_DRM_LEGACY Lets move forward and hide the remaining DRI1 drivers behind a config option, so we have a central place to disable them all. Furthermore, we can provide a clear warning to anyone enabling them. Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160804080657.9664-1-dh.herrmann@gmail.com --- drivers/gpu/drm/Kconfig | 135 +++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index fe1e86ea6d27..cf1609be73fb 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -108,24 +108,8 @@ config DRM_KMS_CMA_HELPER source "drivers/gpu/drm/i2c/Kconfig" -config DRM_TDFX - tristate "3dfx Banshee/Voodoo3+" - depends on DRM && PCI - help - Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), - graphics card. If M is selected, the module will be called tdfx. - source "drivers/gpu/drm/arm/Kconfig" -config DRM_R128 - tristate "ATI Rage 128" - depends on DRM && PCI - select FW_LOADER - help - Choose this option if you have an ATI Rage 128 graphics card. If M - is selected, the module will be called r128. AGP support for - this card is strongly suggested (unless you have a PCI version). - config DRM_RADEON tristate "ATI Radeon" depends on DRM && PCI @@ -169,49 +153,8 @@ source "drivers/gpu/drm/amd/acp/Kconfig" source "drivers/gpu/drm/nouveau/Kconfig" -config DRM_I810 - tristate "Intel I810" - # !PREEMPT because of missing ioctl locking - depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN) - help - Choose this option if you have an Intel I810 graphics card. If M is - selected, the module will be called i810. AGP support is required - for this driver to work. - source "drivers/gpu/drm/i915/Kconfig" -config DRM_MGA - tristate "Matrox g200/g400" - depends on DRM && PCI - select FW_LOADER - help - Choose this option if you have a Matrox G200, G400 or G450 graphics - card. If M is selected, the module will be called mga. AGP - support is required for this driver to work. - -config DRM_SIS - tristate "SiS video cards" - depends on DRM && AGP - depends on FB_SIS || FB_SIS=n - help - Choose this option if you have a SiS 630 or compatible video - chipset. If M is selected the module will be called sis. AGP - support is required for this driver to work. - -config DRM_VIA - tristate "Via unichrome video cards" - depends on DRM && PCI - help - Choose this option if you have a Via unichrome or compatible video - chipset. If M is selected the module will be called via. - -config DRM_SAVAGE - tristate "Savage video cards" - depends on DRM && PCI - help - Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister - chipset. If M is selected the module will be called savage. - config DRM_VGEM tristate "Virtual GEM provider" depends on DRM @@ -282,3 +225,81 @@ source "drivers/gpu/drm/arc/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" source "drivers/gpu/drm/mediatek/Kconfig" + +# Keep legacy drivers last + +menuconfig DRM_LEGACY + bool "Enable legacy drivers (DANGEROUS)" + depends on DRM + help + Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous + APIs to user-space, which can be used to circumvent access + restrictions and other security measures. For backwards compatibility + those drivers are still available, but their use is highly + inadvisable and might harm your system. + + You are recommended to use the safe modeset-only drivers instead, and + perform 3D emulation in user-space. + + Unless you have strong reasons to go rogue, say "N". + +if DRM_LEGACY + +config DRM_TDFX + tristate "3dfx Banshee/Voodoo3+" + depends on DRM && PCI + help + Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), + graphics card. If M is selected, the module will be called tdfx. + +config DRM_R128 + tristate "ATI Rage 128" + depends on DRM && PCI + select FW_LOADER + help + Choose this option if you have an ATI Rage 128 graphics card. If M + is selected, the module will be called r128. AGP support for + this card is strongly suggested (unless you have a PCI version). + +config DRM_I810 + tristate "Intel I810" + # !PREEMPT because of missing ioctl locking + depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN) + help + Choose this option if you have an Intel I810 graphics card. If M is + selected, the module will be called i810. AGP support is required + for this driver to work. + +config DRM_MGA + tristate "Matrox g200/g400" + depends on DRM && PCI + select FW_LOADER + help + Choose this option if you have a Matrox G200, G400 or G450 graphics + card. If M is selected, the module will be called mga. AGP + support is required for this driver to work. + +config DRM_SIS + tristate "SiS video cards" + depends on DRM && AGP + depends on FB_SIS || FB_SIS=n + help + Choose this option if you have a SiS 630 or compatible video + chipset. If M is selected the module will be called sis. AGP + support is required for this driver to work. + +config DRM_VIA + tristate "Via unichrome video cards" + depends on DRM && PCI + help + Choose this option if you have a Via unichrome or compatible video + chipset. If M is selected the module will be called via. + +config DRM_SAVAGE + tristate "Savage video cards" + depends on DRM && PCI + help + Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister + chipset. If M is selected the module will be called savage. + +endif # DRM_LEGACY From d10571fc4fba7d57fb8157f0be61dcbe4a7965ca Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 4 Aug 2016 10:06:57 +0200 Subject: [PATCH 04/24] drm: make DRI1 drivers depend on BROKEN The legacy DRI1 drivers expose highly broken interfaces to user-space. No modern system should enable them, or you will effectively allow user-space to circumvent most of your kernel security measures. The DRI1 kernel APIs are simply broken. User-space can always use vesafb/efifb/simplefb and friends to get working graphics. Lets hide the old drivers behind CONFIG_BROKEN. In case they turn out to be still used (really?), we can easily revert this and figure out a way to move them out of sight (e.g., moving all DRI1 drivers to drivers/gpu/dri1/). Signed-off-by: David Herrmann Acked-by: Chris Wilson Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160804080657.9664-2-dh.herrmann@gmail.com --- drivers/gpu/drm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index cf1609be73fb..c02be6a24c47 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -231,6 +231,7 @@ source "drivers/gpu/drm/mediatek/Kconfig" menuconfig DRM_LEGACY bool "Enable legacy drivers (DANGEROUS)" depends on DRM + depends on BROKEN help Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous APIs to user-space, which can be used to circumvent access From a8c798186b932e048abfd0adbec295e9a0cc847a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 25 Aug 2016 16:35:05 +0200 Subject: [PATCH 05/24] drm: reduce GETCLIENT to a minimum The *only* known user of GETCLIENT is libva, which uses it to check whether its own context is authenticated. It used to iterate all clients, look for one that matches its own pid and then check its state. The entire purpose for us to still have a GETCLIENT implementation is to serve libva. So lets not pretend we do anything else: Make this function return information on the caller's context only, fake the PID to the caller's pid so they always match, and just fill in the "authenticated" bit, nothing else. This patch reduces the complexity of GETCLIENT to a bare minimum, avoids any dependency on priv->uid or priv->pid (allows us to get rid of them), and makes libva happy by always *exactly* returning the information it wants. Signed-off-by: David Herrmann Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160825143505.7447-1-dh.herrmann@gmail.com --- drivers/gpu/drm/drm_ioctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index bb51ee97956d..630754684014 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -189,9 +189,8 @@ static int drm_getclient(struct drm_device *dev, void *data, */ if (client->idx == 0) { client->auth = file_priv->authenticated; - client->pid = pid_vnr(file_priv->pid); - client->uid = from_kuid_munged(current_user_ns(), - file_priv->uid); + client->pid = task_pid_vnr(current); + client->uid = overflowuid; client->magic = 0; client->iocs = 0; From 6dcf0de7ef10244b17442f47956a1d9fabe2abe1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 23 Aug 2016 08:25:40 +0200 Subject: [PATCH 06/24] drm/simple-helpers: Always add planes to the state update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our update function is hooked to the single plane, which might not get called for crtc-only updates. Which is surprising, so fix this by always adding the plane. While at it document how&when the event should be sent out better in the kerneldoc. Cc: Noralf Trønnes Cc: andrea.merello@gmail.com Tested-and-Reported-by: andrea.merello@gmail.com Signed-off-by: Daniel Vetter Reviewed-by: Noralf Trønnes Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1471933540-31131-1-git-send-email-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_simple_kms_helper.c | 7 +++++++ include/drm/drm_simple_kms_helper.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index bada17166512..447631018426 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -34,6 +34,12 @@ static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return drm_atomic_add_affected_planes(state->state, crtc); +} + static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc) { struct drm_simple_display_pipe *pipe; @@ -57,6 +63,7 @@ static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc) } static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { + .atomic_check = drm_simple_kms_crtc_check, .disable = drm_simple_kms_crtc_disable, .enable = drm_simple_kms_crtc_enable, }; diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 269039722f91..826946ca2b82 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -60,6 +60,12 @@ struct drm_simple_display_pipe_funcs { * * This function is called when the underlying plane state is updated. * This hook is optional. + * + * This is the function drivers should submit the + * &drm_pending_vblank_event from. Using either + * drm_crtc_arm_vblank_event(), when the driver supports vblank + * interrupt handling, or drm_crtc_send_vblank_event() directly in case + * the hardware lacks vblank support entirely. */ void (*update)(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state); From cf3bef95e1014233cb1ce89da8cfa5cd22e7412c Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Thu, 25 Aug 2016 11:04:32 +0200 Subject: [PATCH 07/24] drm/bridge: introduce bridge detaching mechanism Up to now, once a bridge has been attached to a DRM device, it cannot be undone. In particular you couldn't rmmod/insmod a DRM driver that uses a bridge, because the bridge would remain bound to the first (dead) driver instance. This patch fixes this by introducing drm_encoder_detach() and a ->detach callback in drm_bridge_funcs for the bridge to be notified about detaches. It's DRM/KMS driver responsibility to call drm_encoder_detach(). While adding the bridge detach callback, with its kerneldoc, I also added kerneldoc for attach callback. Few other kerneldocs fixes around there are included. Suggested-by: Daniel Vetter Suggested-by: Lucas Stach Signed-off-by: Andrea Merello Cc: Archit Taneja Cc: David Airlie Cc: Daniel Vetter Cc: Lucas Stach Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1472115874-6219-1-git-send-email-andrea.merello@gmail.com --- drivers/gpu/drm/drm_bridge.c | 29 +++++++++++++++++++++++++++-- include/drm/drm_crtc.h | 26 +++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 255543086590..484046664d6c 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -98,11 +98,11 @@ EXPORT_SYMBOL(drm_bridge_remove); * @dev: DRM device * @bridge: bridge control structure * - * called by a kms driver to link one of our encoder/bridge to the given + * Called by a kms driver to link one of our encoder/bridge to the given * bridge. * * Note that setting up links between the bridge and our encoder/bridge - * objects needs to be handled by the kms driver itself + * objects needs to be handled by the kms driver itself. * * RETURNS: * Zero on success, error code on failure @@ -124,6 +124,31 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_attach); +/** + * drm_bridge_detach - deassociate given bridge from its DRM device + * + * @bridge: bridge control structure + * + * Called by a kms driver to unlink the given bridge from its DRM device. + * + * Note that tearing down links between the bridge and our encoder/bridge + * objects needs to be handled by the kms driver itself. + */ +void drm_bridge_detach(struct drm_bridge *bridge) +{ + if (WARN_ON(!bridge)) + return; + + if (WARN_ON(!bridge->dev)) + return; + + if (bridge->funcs->detach) + bridge->funcs->detach(bridge); + + bridge->dev = NULL; +} +EXPORT_SYMBOL(drm_bridge_detach); + /** * DOC: bridge callbacks * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3fa0275e509f..bb214a114329 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1118,11 +1118,32 @@ struct drm_plane { /** * struct drm_bridge_funcs - drm_bridge control functions - * @attach: Called during drm_bridge_attach */ struct drm_bridge_funcs { + /** + * @attach: + * + * This callback is invoked whenever our bridge is being attached to a + * &drm_encoder. + * + * The attach callback is optional. + * + * RETURNS: + * + * Zero on success, error code on failure. + */ int (*attach)(struct drm_bridge *bridge); + /** + * @detach: + * + * This callback is invoked whenever our bridge is being detached from a + * &drm_encoder. + * + * The detach callback is optional. + */ + void (*detach)(struct drm_bridge *bridge); + /** * @mode_fixup: * @@ -1137,6 +1158,8 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. * + * The mode_fixup callback is optional. + * * NOTE: * * This function is called in the check phase of atomic modesets, which @@ -2426,6 +2449,7 @@ extern int drm_bridge_add(struct drm_bridge *bridge); extern void drm_bridge_remove(struct drm_bridge *bridge); extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge); +extern void drm_bridge_detach(struct drm_bridge *bridge); bool drm_bridge_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, From 4f993973d274151975a59f50dc509ca6598be501 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Thu, 25 Aug 2016 11:04:33 +0200 Subject: [PATCH 08/24] drm: simple_kms_helper: make connector optional at init time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_simple_display_pipe_init() pretends to attach a connector to the display pipe. In case a drm bridge has to be used, then it's the bridge that takes care of connectors. This patch makes the connector parameter optional for drm_simple_display_pipe_init(), so that a drm bridge could handle connector by itself later. Signed-off-by: Andrea Merello Reviewed-by: Daniel Vetter Cc: David Airlie Cc: Noralf Trønnes Cc: Daniel Vetter Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1472115874-6219-2-git-send-email-andrea.merello@gmail.com --- drivers/gpu/drm/drm_simple_kms_helper.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 447631018426..3301e4a9f7cd 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -146,10 +146,15 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { * @funcs: callbacks for the display pipe (optional) * @formats: array of supported formats (DRM_FORMAT\_\*) * @format_count: number of elements in @formats - * @connector: connector to attach and register + * @connector: connector to attach and register (optional) * * Sets up a display pipeline which consist of a really simple - * plane-crtc-encoder pipe coupled with the provided connector. + * plane-crtc-encoder pipe. + * + * If a connector is supplied, the pipe will be coupled with the provided + * connector. You may supply a NULL connector when using drm bridges, that + * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). + * * Teardown of a simple display pipe is all handled automatically by the drm * core through calling drm_mode_config_cleanup(). Drivers afterwards need to * release the memory for the structure themselves. @@ -188,7 +193,7 @@ int drm_simple_display_pipe_init(struct drm_device *dev, encoder->possible_crtcs = 1 << drm_crtc_index(crtc); ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); - if (ret) + if (ret || !connector) return ret; return drm_mode_connector_attach_encoder(connector, encoder); From 315486c665333a49be20643c9137d9641f32e2b7 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Thu, 25 Aug 2016 11:04:34 +0200 Subject: [PATCH 09/24] drm: simple_kms_helper: add support for bridges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce drm_simple_display_pipe_attach_bridge() and drm_simple_display_pipe_detach_bridge() in order to make it possible to use drm encoders with the simple display pipes managed by simple_kms_helpers Suggested-by: Daniel Vetter Signed-off-by: Andrea Merello Reviewed-by: Daniel Vetter Cc: Noralf Trønnes Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1472115874-6219-3-git-send-email-andrea.merello@gmail.com --- drivers/gpu/drm/drm_simple_kms_helper.c | 40 +++++++++++++++++++++++++ include/drm/drm_simple_kms_helper.h | 5 ++++ 2 files changed, 45 insertions(+) diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 3301e4a9f7cd..7b6d26e64977 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -139,6 +139,46 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; +/** + * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe + * @pipe: simple display pipe object + * @bridge: bridge to attach + * + * Makes it possible to still use the drm_simple_display_pipe helpers when + * a DRM bridge has to be used. + * + * Note that you probably want to initialize the pipe by passing a NULL + * connector to drm_simple_display_pipe_init(). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, + struct drm_bridge *bridge) +{ + bridge->encoder = &pipe->encoder; + pipe->encoder.bridge = bridge; + return drm_bridge_attach(pipe->encoder.dev, bridge); +} +EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); + +/** + * drm_simple_display_pipe_detach_bridge - Detach the bridge from the display pipe + * @pipe: simple display pipe object + * + * Detaches the drm bridge previously attached with + * drm_simple_display_pipe_attach_bridge() + */ +void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe) +{ + if (WARN_ON(!pipe->encoder.bridge)) + return; + + drm_bridge_detach(pipe->encoder.bridge); + pipe->encoder.bridge = NULL; +} +EXPORT_SYMBOL(drm_simple_display_pipe_detach_bridge); + /** * drm_simple_display_pipe_init - Initialize a simple display pipeline * @dev: DRM device diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 826946ca2b82..5d112f75e04c 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -91,6 +91,11 @@ struct drm_simple_display_pipe { const struct drm_simple_display_pipe_funcs *funcs; }; +int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, + struct drm_bridge *bridge); + +void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe); + int drm_simple_display_pipe_init(struct drm_device *dev, struct drm_simple_display_pipe *pipe, const struct drm_simple_display_pipe_funcs *funcs, From c9ac8b4c5caf493fba8c43f1bd02f687ffccb429 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Fri, 26 Aug 2016 15:30:38 +0800 Subject: [PATCH 10/24] drm/atomic-helper: Add atomic_disable CRTC helper callback Some display controllers need plane(s) to be disabled together with the relevant CRTC, e.g., the IPUv3 display controller for imx-drm. This patch adds atomic_disable CRTC helper callback so that old_crtc_state(as a parameter of the callback) could be used to get the active plane(s) of the old CRTC state for disable operation. Suggested-by: Daniel Vetter Cc: Philipp Zabel Cc: David Airlie Cc: Russell King Cc: Peter Senna Tschudin Cc: Lucas Stach Signed-off-by: Liu Ying Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1472196644-30563-2-git-send-email-gnuiyl@gmail.com --- drivers/gpu/drm/drm_atomic_helper.c | 2 ++ include/drm/drm_modeset_helper_vtables.h | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9abe0a242f96..4828b9b860de 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -749,6 +749,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ if (crtc->state->enable && funcs->prepare) funcs->prepare(crtc); + else if (funcs->atomic_disable) + funcs->atomic_disable(crtc, old_crtc_state); else if (funcs->disable) funcs->disable(crtc); else diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 6c8d3dad66ec..10e449c86dbd 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -266,6 +266,8 @@ struct drm_crtc_helper_funcs { * disable anything at the CRTC level. To ensure that runtime PM * handling (using either DPMS or the new "ACTIVE" property) works * @disable must be the inverse of @enable for atomic drivers. + * Atomic drivers should consider to use @atomic_disable instead of + * this one. * * NOTE: * @@ -391,6 +393,28 @@ struct drm_crtc_helper_funcs { */ void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state); + + /** + * @atomic_disable: + * + * This callback should be used to disable the CRTC. With the atomic + * drivers it is called after all encoders connected to this CRTC have + * been shut off already using their own ->disable hook. If that + * sequence is too simple drivers can just add their own hooks and call + * it from this CRTC callback here by looping over all encoders + * connected to it using for_each_encoder_on_crtc(). + * + * This hook is used only by atomic helpers. Atomic drivers don't + * need to implement it if there's no need to disable anything at the + * CRTC level. + * + * Comparing to @disable, this one provides the additional input + * parameter @old_crtc_state which could be used to access the old + * state. Atomic drivers should consider to use this one instead + * of @disable. + */ + void (*atomic_disable)(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); }; /** From 28500291c29ae2174eb0322e839cc3204d06b963 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Fri, 26 Aug 2016 15:30:39 +0800 Subject: [PATCH 11/24] drm/atomic-helper: Disable appropriate planes in disable_planes_on_crtc() Currently, the helper drm_atomic_helper_disable_planes_on_crtc() calls ->atomic_disable for all planes _to be_ enabled on a particular CRTC. This is obviously wrong for those planes which are not scanning out frames when the helper is called. Instead, it's sane to disable active planes of old_crtc_state in the helper. Suggested-by: Daniel Vetter Cc: Philipp Zabel Cc: David Airlie Cc: Russell King Cc: Peter Senna Tschudin Cc: Lucas Stach Signed-off-by: Liu Ying Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1472196644-30563-3-git-send-email-gnuiyl@gmail.com --- drivers/gpu/drm/drm_atomic_helper.c | 16 +++++++++------- include/drm/drm_atomic_helper.h | 5 +++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4828b9b860de..5f82290f2f0f 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1842,12 +1842,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); /** * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes - * @crtc: CRTC + * @old_crtc_state: atomic state object with the old CRTC state * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks * * Disables all planes associated with the given CRTC. This can be - * used for instance in the CRTC helper disable callback to disable - * all planes before shutting down the display pipeline. + * used for instance in the CRTC helper atomic_disable callback to disable + * all planes. * * If the atomic-parameter is set the function calls the CRTC's * atomic_begin hook before and atomic_flush hook after disabling the @@ -1856,9 +1856,11 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); * It is a bug to call this function without having implemented the * ->atomic_disable() plane hook. */ -void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, - bool atomic) +void +drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state, + bool atomic) { + struct drm_crtc *crtc = old_crtc_state->crtc; const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_plane *plane; @@ -1866,11 +1868,11 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, if (atomic && crtc_funcs && crtc_funcs->atomic_begin) crtc_funcs->atomic_begin(crtc, NULL); - drm_for_each_plane(plane, crtc->dev) { + drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) { const struct drm_plane_helper_funcs *plane_funcs = plane->helper_private; - if (plane->state->crtc != crtc || !plane_funcs) + if (!plane_funcs) continue; WARN_ON(!plane_funcs->atomic_disable); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 5a02e499f32b..1abf2c0227a6 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -71,8 +71,9 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, void drm_atomic_helper_cleanup_planes(struct drm_device *dev, struct drm_atomic_state *old_state); void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state); -void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, - bool atomic); +void +drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state, + bool atomic); void drm_atomic_helper_swap_state(struct drm_atomic_state *state, bool stall); From 2b58e98d42af854037439f51bd89f83dbfa8e30d Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 29 Aug 2016 17:12:03 +0800 Subject: [PATCH 12/24] drm/atomic-helper: Add NO_DISABLE_AFTER_MODESET flag support for plane commit Drivers may set the NO_DISABLE_AFTER_MODESET flag in the 'flags' parameter of the helper drm_atomic_helper_commit_planes() if the relevant display controllers(e.g., IPUv3 for imx-drm) require to disable a CRTC's planes when the CRTC is disabled. The helper would skip the ->atomic_disable call for a plane if the CRTC of the old plane state needs a modesetting operation. Of course, the drivers need to disable the planes in their CRTC disable callbacks since no one else would do that. Suggested-by: Daniel Vetter Cc: Philipp Zabel Cc: David Airlie Cc: Russell King Cc: Peter Senna Tschudin Cc: Lucas Stach Signed-off-by: Liu Ying Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1472461923-14364-1-git-send-email-gnuiyl@gmail.com --- drivers/gpu/drm/arm/malidp_drv.c | 3 +- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 43 ++++++++++++++------ drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- drivers/gpu/drm/imx/imx-drm-core.c | 3 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 6 ++- drivers/gpu/drm/msm/msm_atomic.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 3 +- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 3 +- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/tegra/drm.c | 3 +- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 2 +- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- drivers/gpu/drm/virtio/virtgpu_display.c | 3 +- include/drm/drm_atomic_helper.h | 6 ++- 16 files changed, 59 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 82171d223f2d..c383d724527f 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -91,7 +91,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, true); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); malidp_atomic_commit_hw_done(state); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index d4a3d61b7b06..8e7483d90c47 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -457,7 +457,7 @@ atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_modeset_enables(dev, old_state); drm_atomic_helper_wait_for_vblanks(dev, old_state); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5f82290f2f0f..6fdd7ba47896 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1148,7 +1148,8 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * * drm_atomic_helper_commit_modeset_enables(dev, state); * - * drm_atomic_helper_commit_planes(dev, state, true); + * drm_atomic_helper_commit_planes(dev, state, + * DRM_PLANE_COMMIT_ACTIVE_ONLY); * * for committing the atomic update to hardware. See the kerneldoc entries for * these three functions for more details. @@ -1159,7 +1160,7 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); @@ -1678,7 +1679,7 @@ bool plane_crtc_active(struct drm_plane_state *state) * drm_atomic_helper_commit_planes - commit plane state * @dev: DRM device * @old_state: atomic state object with old state structures - * @active_only: Only commit on active CRTC if set + * @flags: flags for committing plane state * * This function commits the new plane state using the plane and atomic helper * functions for planes and crtcs. It assumes that the atomic state has already @@ -1698,25 +1699,34 @@ bool plane_crtc_active(struct drm_plane_state *state) * most drivers don't need to be immediately notified of plane updates for a * disabled CRTC. * - * Unless otherwise needed, drivers are advised to set the @active_only - * parameters to true in order not to receive plane update notifications related - * to a disabled CRTC. This avoids the need to manually ignore plane updates in + * Unless otherwise needed, drivers are advised to set the ACTIVE_ONLY flag in + * @flags in order not to receive plane update notifications related to a + * disabled CRTC. This avoids the need to manually ignore plane updates in * driver code when the driver and/or hardware can't or just don't need to deal * with updates on disabled CRTCs, for example when supporting runtime PM. * - * The drm_atomic_helper_commit() default implementation only sets @active_only - * to false to most closely match the behaviour of the legacy helpers. This should - * not be copied blindly by drivers. + * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant + * display controllers require to disable a CRTC's planes when the CRTC is + * disabled. This function would skip the ->atomic_disable call for a plane if + * the CRTC of the old plane state needs a modesetting operation. Of course, + * the drivers need to disable the planes in their CRTC disable callbacks + * since no one else would do that. + * + * The drm_atomic_helper_commit() default implementation doesn't set the + * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers. + * This should not be copied blindly by drivers. */ void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *old_state, - bool active_only) + uint32_t flags) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct drm_plane *plane; struct drm_plane_state *old_plane_state; int i; + bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY; + bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET; for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; @@ -1760,10 +1770,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, /* * Special-case disabling the plane if drivers support it. */ - if (disabling && funcs->atomic_disable) + if (disabling && funcs->atomic_disable) { + struct drm_crtc_state *crtc_state; + + crtc_state = old_plane_state->crtc->state; + + if (drm_atomic_crtc_needs_modeset(crtc_state) && + no_disable) + continue; + funcs->atomic_disable(plane, old_plane_state); - else if (plane->state->crtc || disabling) + } else if (plane->state->crtc || disabling) { funcs->atomic_update(plane, old_plane_state); + } } for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 877d2efa28e2..486943e70f70 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -105,7 +105,7 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) atomic_inc(&exynos_crtc->pending_update); } - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); exynos_atomic_wait_for_commit(state); diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 438bac8fbc2b..56dfc4cd50c6 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -193,7 +193,8 @@ static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 0e769abd0c2c..72c1ae4e02d4 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -70,13 +70,15 @@ static void mtk_atomic_complete(struct mtk_drm_private *private, * * drm_atomic_helper_commit_modeset_disables(dev, state); * drm_atomic_helper_commit_modeset_enables(dev, state); - * drm_atomic_helper_commit_planes(dev, state, true); + * drm_atomic_helper_commit_planes(dev, state, + * DRM_PLANE_COMMIT_ACTIVE_ONLY); * * See the kerneldoc entries for these three functions for more details. */ drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, true); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_wait_for_vblanks(drm, state); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 4a8a6f1f1151..5df252cebf1c 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -118,7 +118,7 @@ static void complete_commit(struct msm_commit *c, bool async) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dd78f2045f9..e1cfba51cff6 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -96,7 +96,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) dispc_runtime_get(); drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_modeset_enables(dev, old_state); omap_atomic_wait_for_completion(dev, old_state); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f03eb55318c1..bd9c3bb9252c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -257,7 +257,8 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, true); + drm_atomic_helper_commit_planes(dev, old_state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_wait_for_vblanks(dev, old_state); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 55c52734c52d..7ca8f347d79f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -233,7 +233,8 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_commit_hw_done(state); diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index f8311b2bc84e..7cd3804c6dee 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -178,7 +178,7 @@ static void sti_atomic_complete(struct sti_private *private, */ drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_planes(drm, state, false); + drm_atomic_helper_commit_planes(drm, state, 0); drm_atomic_helper_commit_modeset_enables(drm, state); drm_atomic_helper_wait_for_vblanks(drm, state); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 755264d9db22..4b9f1c79cd7b 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -57,7 +57,8 @@ static void tegra_atomic_complete(struct tegra_drm *tegra, drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, true); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_wait_for_vblanks(drm, state); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 3404d245d28d..4405e4bc8056 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -118,7 +118,7 @@ static int tilcdc_commit(struct drm_device *dev, drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 4ac894d993cd..c1f65c6c8e60 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -44,7 +44,7 @@ vc4_atomic_complete_commit(struct vc4_commit *c) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 4e192aa2d021..7cf3678623c3 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -338,7 +338,8 @@ static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_commit_hw_done(state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 1abf2c0227a6..f86682825d68 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -65,9 +65,13 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, int drm_atomic_helper_prepare_planes(struct drm_device *dev, struct drm_atomic_state *state); + +#define DRM_PLANE_COMMIT_ACTIVE_ONLY BIT(0) +#define DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET BIT(1) + void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *state, - bool active_only); + uint32_t flags); void drm_atomic_helper_cleanup_planes(struct drm_device *dev, struct drm_atomic_state *old_state); void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state); From 749cc6f7ed509e1be04e9c26d9cdc3c1f0dd13b4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 29 Aug 2016 14:34:07 +0200 Subject: [PATCH 13/24] drm/fb-helper: don't call remove_conflicting_framebuffers for FB=m && DRM=y When CONFIG_DRM_KMS_FB_HELPER is disabled, we can have a configuration in which some DRM drivers are built-in, but the framebuffer core is a loadable module. This results in a link error, such as: drivers/gpu/drm/radeon/radeon.o: In function `radeon_pci_probe': radeon_kfd.c:(.text.radeon_pci_probe+0xbc): undefined reference to `remove_conflicting_framebuffers' drivers/gpu/drm/amd/amdgpu/amdgpu.o: In function `amdgpu_pci_probe': amdgpu_mn.c:(.text.amdgpu_pci_probe+0xa8): undefined reference to `remove_conflicting_framebuffers' drivers/gpu/drm/mgag200/mgag200.o: In function `mga_vram_init': mgag200_ttm.c:(.text.mga_vram_init+0xa8): undefined reference to `remove_conflicting_framebuffers' drivers/gpu/drm/mgag200/mgag200.o: In function `mga_pci_probe': mgag200_ttm.c:(.text.mga_pci_probe+0x88): undefined reference to `remove_conflicting_framebuffers' Makefile:969: recipe for target 'vmlinux' failed This changes the compile-time check to IS_REACHABLE, which means we end up not calling remove_conflicting_framebuffers() in the configuration, which seems good enough, as we know that no framebuffer driver is loaded by the time that the built-in DRM driver calls remove_conflicting_framebuffers. We could alternatively avoid the link error by forcing CONFIG_FB to not be a module in this case, but that wouldn't change anything at runtime, and just make the already convoluted set of dependencies worse here. I could not find out what happens if the fbdev driver gets loaded as a module after the DRM driver is already initialized, but that is a case that can happen with or without this patch. Signed-off-by: Arnd Bergmann Fixes: 0a3bfe29f816 ("drm/fb-helper: Fix the dummy remove_conflicting_framebuffers") Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829123428.3260105-1-arnd@arndb.de Link: http://patchwork.freedesktop.org/patch/msgid/1472461923-14364-1-git-send-email-gnuiyl@gmail.com --- include/drm/drm_fb_helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index edc6cfd3aa34..797fb5f80c45 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -491,7 +491,7 @@ static inline int drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { -#if IS_ENABLED(CONFIG_FB) +#if IS_REACHABLE(CONFIG_FB) return remove_conflicting_framebuffers(a, name, primary); #else return 0; From 321a95ae35f2ec4f58c4cda28d3606cc6f2b97d1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:49 +0200 Subject: [PATCH 14/24] drm: Extract drm_encoder.[hc] Same treatment as before. Only hiccup is drm_crtc_mask, which unfortunately can't be resolved until drm_crtc.h is less of a monster. Untangle the header loop with a forward declaration for that static inline. Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-1-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 9 ++ drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_crtc.c | 193 ------------------------ drivers/gpu/drm/drm_crtc_internal.h | 10 +- drivers/gpu/drm/drm_encoder.c | 220 ++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 134 +---------------- include/drm/drm_encoder.h | 167 +++++++++++++++++++++ 7 files changed, 407 insertions(+), 329 deletions(-) create mode 100644 drivers/gpu/drm/drm_encoder.c create mode 100644 include/drm/drm_encoder.h diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index fa948b4e029b..7f788caebea3 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -125,6 +125,15 @@ Connector Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_connector.c :export: +Encoder Abstraction +=================== + +.. kernel-doc:: include/drm/drm_encoder.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_encoder.c + :export: + KMS Initialization and Cleanup ============================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 193ff2d09479..efdb4176230f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -13,7 +13,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ drm_modeset_lock.o drm_atomic.o drm_bridge.o \ - drm_framebuffer.o drm_connector.o drm_blend.o + drm_framebuffer.o drm_connector.o drm_blend.o \ + drm_encoder.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7f2510524f09..219cd4ee23b7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -54,18 +54,6 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { { DRM_PLANE_TYPE_CURSOR, "Cursor" }, }; -static const struct drm_prop_enum_list drm_encoder_enum_list[] = { - { DRM_MODE_ENCODER_NONE, "None" }, - { DRM_MODE_ENCODER_DAC, "DAC" }, - { DRM_MODE_ENCODER_TMDS, "TMDS" }, - { DRM_MODE_ENCODER_LVDS, "LVDS" }, - { DRM_MODE_ENCODER_TVDAC, "TV" }, - { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, - { DRM_MODE_ENCODER_DSI, "DSI" }, - { DRM_MODE_ENCODER_DPMST, "DP MST" }, - { DRM_MODE_ENCODER_DPI, "DPI" }, -}; - /* * Optional properties */ @@ -419,117 +407,6 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_cleanup); -static int drm_encoder_register_all(struct drm_device *dev) -{ - struct drm_encoder *encoder; - int ret = 0; - - drm_for_each_encoder(encoder, dev) { - if (encoder->funcs->late_register) - ret = encoder->funcs->late_register(encoder); - if (ret) - return ret; - } - - return 0; -} - -static void drm_encoder_unregister_all(struct drm_device *dev) -{ - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, dev) { - if (encoder->funcs->early_unregister) - encoder->funcs->early_unregister(encoder); - } -} - -/** - * drm_encoder_init - Init a preallocated encoder - * @dev: drm device - * @encoder: the encoder to init - * @funcs: callbacks for this encoder - * @encoder_type: user visible type of the encoder - * @name: printf style format string for the encoder name, or NULL for default name - * - * Initialises a preallocated encoder. Encoder should be - * subclassed as part of driver encoder objects. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - const struct drm_encoder_funcs *funcs, - int encoder_type, const char *name, ...) -{ - int ret; - - drm_modeset_lock_all(dev); - - ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); - if (ret) - goto out_unlock; - - encoder->dev = dev; - encoder->encoder_type = encoder_type; - encoder->funcs = funcs; - if (name) { - va_list ap; - - va_start(ap, name); - encoder->name = kvasprintf(GFP_KERNEL, name, ap); - va_end(ap); - } else { - encoder->name = kasprintf(GFP_KERNEL, "%s-%d", - drm_encoder_enum_list[encoder_type].name, - encoder->base.id); - } - if (!encoder->name) { - ret = -ENOMEM; - goto out_put; - } - - list_add_tail(&encoder->head, &dev->mode_config.encoder_list); - encoder->index = dev->mode_config.num_encoder++; - -out_put: - if (ret) - drm_mode_object_unregister(dev, &encoder->base); - -out_unlock: - drm_modeset_unlock_all(dev); - - return ret; -} -EXPORT_SYMBOL(drm_encoder_init); - -/** - * drm_encoder_cleanup - cleans up an initialised encoder - * @encoder: encoder to cleanup - * - * Cleans up the encoder but doesn't free the object. - */ -void drm_encoder_cleanup(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - - /* Note that the encoder_list is considered to be static; should we - * remove the drm_encoder at runtime we would have to decrement all - * the indices on the drm_encoder after us in the encoder_list. - */ - - drm_modeset_lock_all(dev); - drm_mode_object_unregister(dev, &encoder->base); - kfree(encoder->name); - list_del(&encoder->head); - dev->mode_config.num_encoder--; - drm_modeset_unlock_all(dev); - - memset(encoder, 0, sizeof(*encoder)); -} -EXPORT_SYMBOL(drm_encoder_cleanup); - static unsigned int drm_num_planes(struct drm_device *dev) { unsigned int num = 0; @@ -1172,76 +1049,6 @@ int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, return 0; } -static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) -{ - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - bool uses_atomic = false; - - /* For atomic drivers only state objects are synchronously updated and - * protected by modeset locks, so check those first. */ - drm_for_each_connector(connector, dev) { - if (!connector->state) - continue; - - uses_atomic = true; - - if (connector->state->best_encoder != encoder) - continue; - - return connector->state->crtc; - } - - /* Don't return stale data (e.g. pending async disable). */ - if (uses_atomic) - return NULL; - - return encoder->crtc; -} - -/** - * drm_mode_getencoder - get encoder configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a encoder configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getencoder(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_encoder *enc_resp = data; - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - encoder = drm_encoder_find(dev, enc_resp->encoder_id); - if (!encoder) - return -ENOENT; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - crtc = drm_encoder_get_crtc(encoder); - if (crtc) - enc_resp->crtc_id = crtc->base.id; - else - enc_resp->crtc_id = 0; - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - enc_resp->encoder_type = encoder->encoder_type; - enc_resp->encoder_id = encoder->base.id; - enc_resp->possible_crtcs = encoder->possible_crtcs; - enc_resp->possible_clones = encoder->possible_clones; - - return 0; -} - /** * drm_mode_getplane_res - enumerate all plane resources * @dev: DRM device diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 62efb9d09a85..54f9a2c85965 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -105,8 +105,6 @@ int drm_mode_createblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_destroyblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_getencoder(struct drm_device *dev, - void *data, struct drm_file *file_priv); int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_gamma_set_ioctl(struct drm_device *dev, @@ -115,6 +113,14 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* drm_encoder.c */ +int drm_encoder_register_all(struct drm_device *dev); +void drm_encoder_unregister_all(struct drm_device *dev); + +/* IOCTL */ +int drm_mode_getencoder(struct drm_device *dev, + void *data, struct drm_file *file_priv); + /* drm_connector.c */ void drm_connector_ida_init(void); void drm_connector_ida_destroy(void); diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c new file mode 100644 index 000000000000..bce781b7bb5f --- /dev/null +++ b/drivers/gpu/drm/drm_encoder.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "drm_crtc_internal.h" + +static const struct drm_prop_enum_list drm_encoder_enum_list[] = { + { DRM_MODE_ENCODER_NONE, "None" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TV" }, + { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, + { DRM_MODE_ENCODER_DSI, "DSI" }, + { DRM_MODE_ENCODER_DPMST, "DP MST" }, + { DRM_MODE_ENCODER_DPI, "DPI" }, +}; + +int drm_encoder_register_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + int ret = 0; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->late_register) + ret = encoder->funcs->late_register(encoder); + if (ret) + return ret; + } + + return 0; +} + +void drm_encoder_unregister_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->early_unregister) + encoder->funcs->early_unregister(encoder); + } +} + +/** + * drm_encoder_init - Init a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * Initialises a preallocated encoder. Encoder should be + * subclassed as part of driver encoder objects. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...) +{ + int ret; + + drm_modeset_lock_all(dev); + + ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); + if (ret) + goto out_unlock; + + encoder->dev = dev; + encoder->encoder_type = encoder_type; + encoder->funcs = funcs; + if (name) { + va_list ap; + + va_start(ap, name); + encoder->name = kvasprintf(GFP_KERNEL, name, ap); + va_end(ap); + } else { + encoder->name = kasprintf(GFP_KERNEL, "%s-%d", + drm_encoder_enum_list[encoder_type].name, + encoder->base.id); + } + if (!encoder->name) { + ret = -ENOMEM; + goto out_put; + } + + list_add_tail(&encoder->head, &dev->mode_config.encoder_list); + encoder->index = dev->mode_config.num_encoder++; + +out_put: + if (ret) + drm_mode_object_unregister(dev, &encoder->base); + +out_unlock: + drm_modeset_unlock_all(dev); + + return ret; +} +EXPORT_SYMBOL(drm_encoder_init); + +/** + * drm_encoder_cleanup - cleans up an initialised encoder + * @encoder: encoder to cleanup + * + * Cleans up the encoder but doesn't free the object. + */ +void drm_encoder_cleanup(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + + /* Note that the encoder_list is considered to be static; should we + * remove the drm_encoder at runtime we would have to decrement all + * the indices on the drm_encoder after us in the encoder_list. + */ + + drm_modeset_lock_all(dev); + drm_mode_object_unregister(dev, &encoder->base); + kfree(encoder->name); + list_del(&encoder->head); + dev->mode_config.num_encoder--; + drm_modeset_unlock_all(dev); + + memset(encoder, 0, sizeof(*encoder)); +} +EXPORT_SYMBOL(drm_encoder_cleanup); + +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + bool uses_atomic = false; + + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + drm_for_each_connector(connector, dev) { + if (!connector->state) + continue; + + uses_atomic = true; + + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + /* Don't return stale data (e.g. pending async disable). */ + if (uses_atomic) + return NULL; + + return encoder->crtc; +} + +/** + * drm_mode_getencoder - get encoder configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Construct a encoder configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_getencoder(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_get_encoder *enc_resp = data; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + encoder = drm_encoder_find(dev, enc_resp->encoder_id); + if (!encoder) + return -ENOENT; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + crtc = drm_encoder_get_crtc(encoder); + if (crtc) + enc_resp->crtc_id = crtc->base.id; + else + enc_resp->crtc_id = 0; + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + enc_resp->encoder_type = encoder->encoder_type; + enc_resp->encoder_id = encoder->base.id; + enc_resp->possible_crtcs = encoder->possible_crtcs; + enc_resp->possible_clones = encoder->possible_clones; + + return 0; +} diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bb214a114329..c459867ecb9c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -40,6 +40,7 @@ #include #include #include +#include struct drm_device; struct drm_mode_set; @@ -661,97 +662,6 @@ struct drm_crtc { struct drm_modeset_acquire_ctx *acquire_ctx; }; -/** - * struct drm_encoder_funcs - encoder controls - * - * Encoders sit between CRTCs and connectors. - */ -struct drm_encoder_funcs { - /** - * @reset: - * - * Reset encoder hardware and software state to off. This function isn't - * called by the core directly, only through drm_mode_config_reset(). - * It's not a helper hook only for historical reasons. - */ - void (*reset)(struct drm_encoder *encoder); - - /** - * @destroy: - * - * Clean up encoder resources. This is only called at driver unload time - * through drm_mode_config_cleanup() since an encoder cannot be - * hotplugged in DRM. - */ - void (*destroy)(struct drm_encoder *encoder); - - /** - * @late_register: - * - * This optional hook can be used to register additional userspace - * interfaces attached to the encoder like debugfs interfaces. - * It is called late in the driver load sequence from drm_dev_register(). - * Everything added from this callback should be unregistered in - * the early_unregister callback. - * - * Returns: - * - * 0 on success, or a negative error code on failure. - */ - int (*late_register)(struct drm_encoder *encoder); - - /** - * @early_unregister: - * - * This optional hook should be used to unregister the additional - * userspace interfaces attached to the encoder from - * late_unregister(). It is called from drm_dev_unregister(), - * early in the driver unload sequence to disable userspace access - * before data structures are torndown. - */ - void (*early_unregister)(struct drm_encoder *encoder); -}; - -/** - * struct drm_encoder - central DRM encoder structure - * @dev: parent DRM device - * @head: list management - * @base: base KMS object - * @name: human readable name, can be overwritten by the driver - * @encoder_type: one of the DRM_MODE_ENCODER_ types in drm_mode.h - * @possible_crtcs: bitmask of potential CRTC bindings - * @possible_clones: bitmask of potential sibling encoders for cloning - * @crtc: currently bound CRTC - * @bridge: bridge associated to the encoder - * @funcs: control functions - * @helper_private: mid-layer private data - * - * CRTCs drive pixels to encoders, which convert them into signals - * appropriate for a given connector or set of connectors. - */ -struct drm_encoder { - struct drm_device *dev; - struct list_head head; - - struct drm_mode_object base; - char *name; - int encoder_type; - - /** - * @index: Position inside the mode_config.list, can be used as an array - * index. It is invariant over the lifetime of the encoder. - */ - unsigned index; - - uint32_t possible_crtcs; - uint32_t possible_clones; - - struct drm_crtc *crtc; - struct drm_bridge *bridge; - const struct drm_encoder_funcs *funcs; - const struct drm_encoder_helper_funcs *helper_private; -}; - /** * struct drm_plane_state - mutable plane state * @plane: backpointer to the plane @@ -2114,7 +2024,6 @@ struct drm_mode_config { for_each_if ((encoder_mask) & (1 << drm_encoder_index(encoder))) #define obj_to_crtc(x) container_of(x, struct drm_crtc, base) -#define obj_to_encoder(x) container_of(x, struct drm_encoder, base) #define obj_to_mode(x) container_of(x, struct drm_display_mode, base) #define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) #define obj_to_property(x) container_of(x, struct drm_property, base) @@ -2159,37 +2068,6 @@ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc) return 1 << drm_crtc_index(crtc); } -extern __printf(5, 6) -int drm_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - const struct drm_encoder_funcs *funcs, - int encoder_type, const char *name, ...); - -/** - * drm_encoder_index - find the index of a registered encoder - * @encoder: encoder to find index for - * - * Given a registered encoder, return the index of that encoder within a DRM - * device's list of encoders. - */ -static inline unsigned int drm_encoder_index(struct drm_encoder *encoder) -{ - return encoder->index; -} - -/** - * drm_encoder_crtc_ok - can a given crtc drive a given encoder? - * @encoder: encoder to test - * @crtc: crtc to test - * - * Return false if @encoder can't be driven by @crtc, true otherwise. - */ -static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder, - struct drm_crtc *crtc) -{ - return !!(encoder->possible_crtcs & drm_crtc_mask(crtc)); -} - extern __printf(8, 9) int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, @@ -2225,8 +2103,6 @@ extern void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, extern int drm_crtc_force_disable(struct drm_crtc *crtc); extern int drm_crtc_force_disable_all(struct drm_device *dev); -extern void drm_encoder_cleanup(struct drm_encoder *encoder); - extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); @@ -2338,14 +2214,6 @@ static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, return mo ? obj_to_crtc(mo) : NULL; } -static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); - return mo ? obj_to_encoder(mo) : NULL; -} - static inline struct drm_property *drm_property_find(struct drm_device *dev, uint32_t id) { diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h new file mode 100644 index 000000000000..2712fd1a686b --- /dev/null +++ b/include/drm/drm_encoder.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef __DRM_ENCODER_H__ +#define __DRM_ENCODER_H__ + +#include +#include +#include + +/** + * struct drm_encoder_funcs - encoder controls + * + * Encoders sit between CRTCs and connectors. + */ +struct drm_encoder_funcs { + /** + * @reset: + * + * Reset encoder hardware and software state to off. This function isn't + * called by the core directly, only through drm_mode_config_reset(). + * It's not a helper hook only for historical reasons. + */ + void (*reset)(struct drm_encoder *encoder); + + /** + * @destroy: + * + * Clean up encoder resources. This is only called at driver unload time + * through drm_mode_config_cleanup() since an encoder cannot be + * hotplugged in DRM. + */ + void (*destroy)(struct drm_encoder *encoder); + + /** + * @late_register: + * + * This optional hook can be used to register additional userspace + * interfaces attached to the encoder like debugfs interfaces. + * It is called late in the driver load sequence from drm_dev_register(). + * Everything added from this callback should be unregistered in + * the early_unregister callback. + * + * Returns: + * + * 0 on success, or a negative error code on failure. + */ + int (*late_register)(struct drm_encoder *encoder); + + /** + * @early_unregister: + * + * This optional hook should be used to unregister the additional + * userspace interfaces attached to the encoder from + * late_unregister(). It is called from drm_dev_unregister(), + * early in the driver unload sequence to disable userspace access + * before data structures are torndown. + */ + void (*early_unregister)(struct drm_encoder *encoder); +}; + +/** + * struct drm_encoder - central DRM encoder structure + * @dev: parent DRM device + * @head: list management + * @base: base KMS object + * @name: human readable name, can be overwritten by the driver + * @encoder_type: one of the DRM_MODE_ENCODER_ types in drm_mode.h + * @possible_crtcs: bitmask of potential CRTC bindings + * @possible_clones: bitmask of potential sibling encoders for cloning + * @crtc: currently bound CRTC + * @bridge: bridge associated to the encoder + * @funcs: control functions + * @helper_private: mid-layer private data + * + * CRTCs drive pixels to encoders, which convert them into signals + * appropriate for a given connector or set of connectors. + */ +struct drm_encoder { + struct drm_device *dev; + struct list_head head; + + struct drm_mode_object base; + char *name; + int encoder_type; + + /** + * @index: Position inside the mode_config.list, can be used as an array + * index. It is invariant over the lifetime of the encoder. + */ + unsigned index; + + uint32_t possible_crtcs; + uint32_t possible_clones; + + struct drm_crtc *crtc; + struct drm_bridge *bridge; + const struct drm_encoder_funcs *funcs; + const struct drm_encoder_helper_funcs *helper_private; +}; + +#define obj_to_encoder(x) container_of(x, struct drm_encoder, base) + +__printf(5, 6) +int drm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...); + +/** + * drm_encoder_index - find the index of a registered encoder + * @encoder: encoder to find index for + * + * Given a registered encoder, return the index of that encoder within a DRM + * device's list of encoders. + */ +static inline unsigned int drm_encoder_index(struct drm_encoder *encoder) +{ + return encoder->index; +} + +/* FIXME: We have an include file mess still, drm_crtc.h needs untangling. */ +static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc); + +/** + * drm_encoder_crtc_ok - can a given crtc drive a given encoder? + * @encoder: encoder to test + * @crtc: crtc to test + * + * Return false if @encoder can't be driven by @crtc, true otherwise. + */ +static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder, + struct drm_crtc *crtc) +{ + return !!(encoder->possible_crtcs & drm_crtc_mask(crtc)); +} + +static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); + return mo ? obj_to_encoder(mo) : NULL; +} + +void drm_encoder_cleanup(struct drm_encoder *encoder); + +#endif From e03e6de03e931bd3d93b1e2a9dc7d0bf0b505287 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:50 +0200 Subject: [PATCH 15/24] drm/doc: Polish kerneldoc for encoders - Move missing bits into struct drm_encoder docs. - Explain that encoders are 95% internal and only 5% uapi, and that in general the uapi part is broken. - Remove verbose comments for functions not exposed to drivers. v2: Review from Archit: - Appease checkpatch in the moved code. - Make it clearer that bridges are not exposed to userspace. Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-2-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 46 +++-------------------- drivers/gpu/drm/drm_encoder.c | 48 +++++++++++++++--------- include/drm/drm_encoder.h | 70 ++++++++++++++++++++++++++++++++--- 3 files changed, 101 insertions(+), 63 deletions(-) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 7f788caebea3..47c2835b7c2d 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -128,6 +128,12 @@ Connector Functions Reference Encoder Abstraction =================== +.. kernel-doc:: drivers/gpu/drm/drm_encoder.c + :doc: overview + +Encoder Functions Reference +--------------------------- + .. kernel-doc:: include/drm/drm_encoder.h :internal: @@ -207,46 +213,6 @@ future); drivers that do not wish to provide special handling for primary planes may make use of the helper functions described in ? to create and register a primary plane with standard capabilities. -Encoders (:c:type:`struct drm_encoder `) ------------------------------------------------------ - -An encoder takes pixel data from a CRTC and converts it to a format -suitable for any attached connectors. On some devices, it may be -possible to have a CRTC send data to more than one encoder. In that -case, both encoders would receive data from the same scanout buffer, -resulting in a "cloned" display configuration across the connectors -attached to each encoder. - -Encoder Initialization -~~~~~~~~~~~~~~~~~~~~~~ - -As for CRTCs, a KMS driver must create, initialize and register at least -one :c:type:`struct drm_encoder ` instance. The -instance is allocated and zeroed by the driver, possibly as part of a -larger structure. - -Drivers must initialize the :c:type:`struct drm_encoder -` possible_crtcs and possible_clones fields before -registering the encoder. Both fields are bitmasks of respectively the -CRTCs that the encoder can be connected to, and sibling encoders -candidate for cloning. - -After being initialized, the encoder must be registered with a call to -:c:func:`drm_encoder_init()`. The function takes a pointer to the -encoder functions and an encoder type. Supported types are - -- DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A -- DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort -- DRM_MODE_ENCODER_LVDS for display panels -- DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, - Component, SCART) -- DRM_MODE_ENCODER_VIRTUAL for virtual machine displays - -Encoders must be attached to a CRTC to be used. DRM drivers leave -encoders unattached at initialization time. Applications (or the fbdev -compatibility layer when implemented) are responsible for attaching the -encoders they want to use to a CRTC. - Cleanup ------- diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index bce781b7bb5f..998a6743a586 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -26,6 +26,30 @@ #include "drm_crtc_internal.h" +/** + * DOC: overview + * + * Encoders represent the connecting element between the CRTC (as the overall + * pixel pipeline, represented by struct &drm_crtc) and the connectors (as the + * generic sink entity, represented by struct &drm_connector). Encoders are + * objects exposed to userspace, originally to allow userspace to infer cloning + * and connector/CRTC restrictions. Unfortunately almost all drivers get this + * wrong, making the uabi pretty much useless. On top of that the exposed + * restrictions are too simple for todays hardware, and the recommend way to + * infer restrictions is by using the DRM_MODE_ATOMIC_TEST_ONLY flag for the + * atomic IOCTL. + * + * Otherwise encoders aren't used in the uapi at all (any modeset request from + * userspace directly connects a connector with a CRTC), drivers are therefore + * free to use them however they wish. Modeset helper libraries make strong use + * of encoders to facilitate code sharing. But for more complex settings it is + * usually better to move shared code into a separate &drm_bridge. Compared to + * encoders bridges also have the benefit of not being purely an internal + * abstraction since they are not exposed to userspace at all. + * + * Encoders are initialized with drm_encoder_init() and cleaned up using + * drm_encoder_cleanup(). + */ static const struct drm_prop_enum_list drm_encoder_enum_list[] = { { DRM_MODE_ENCODER_NONE, "None" }, { DRM_MODE_ENCODER_DAC, "DAC" }, @@ -71,16 +95,17 @@ void drm_encoder_unregister_all(struct drm_device *dev) * @encoder_type: user visible type of the encoder * @name: printf style format string for the encoder name, or NULL for default name * - * Initialises a preallocated encoder. Encoder should be - * subclassed as part of driver encoder objects. + * Initialises a preallocated encoder. Encoder should be subclassed as part of + * driver encoder objects. At driver unload time drm_encoder_cleanup() should be + * called from the driver's destroy hook in &drm_encoder_funcs. * * Returns: * Zero on success, error code on failure. */ int drm_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - const struct drm_encoder_funcs *funcs, - int encoder_type, const char *name, ...) + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...) { int ret; @@ -176,19 +201,6 @@ static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) return encoder->crtc; } -/** - * drm_mode_getencoder - get encoder configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a encoder configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 2712fd1a686b..3d7350f1fcc1 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -84,9 +84,6 @@ struct drm_encoder_funcs { * @head: list management * @base: base KMS object * @name: human readable name, can be overwritten by the driver - * @encoder_type: one of the DRM_MODE_ENCODER_ types in drm_mode.h - * @possible_crtcs: bitmask of potential CRTC bindings - * @possible_clones: bitmask of potential sibling encoders for cloning * @crtc: currently bound CRTC * @bridge: bridge associated to the encoder * @funcs: control functions @@ -101,6 +98,32 @@ struct drm_encoder { struct drm_mode_object base; char *name; + /** + * @encoder_type: + * + * One of the DRM_MODE_ENCODER_ types in drm_mode.h. The following + * encoder types are defined thus far: + * + * - DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A. + * + * - DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort. + * + * - DRM_MODE_ENCODER_LVDS for display panels, or in general any panel + * with a proprietary parallel connector. + * + * - DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, + * Component, SCART). + * + * - DRM_MODE_ENCODER_VIRTUAL for virtual machine displays + * + * - DRM_MODE_ENCODER_DSI for panels connected using the DSI serial bus. + * + * - DRM_MODE_ENCODER_DPI for panels connected using the DPI parallel + * bus. + * + * - DRM_MODE_ENCODER_DPMST for special fake encoders used to allow + * mutliple DP MST streams to share one physical encoder. + */ int encoder_type; /** @@ -109,7 +132,34 @@ struct drm_encoder { */ unsigned index; + /** + * @possible_crtcs: Bitmask of potential CRTC bindings, using + * drm_crtc_index() as the index into the bitfield. The driver must set + * the bits for all &drm_crtc objects this encoder can be connected to + * before calling drm_encoder_init(). + * + * In reality almost every driver gets this wrong. + * + * Note that since CRTC objects can't be hotplugged the assigned indices + * are stable and hence known before registering all objects. + */ uint32_t possible_crtcs; + + /** + * @possible_clones: Bitmask of potential sibling encoders for cloning, + * using drm_encoder_index() as the index into the bitfield. The driver + * must set the bits for all &drm_encoder objects which can clone a + * &drm_crtc together with this encoder before calling + * drm_encoder_init(). Drivers should set the bit representing the + * encoder itself, too. Cloning bits should be set such that when two + * encoders can be used in a cloned configuration, they both should have + * each another bits set. + * + * In reality almost every driver gets this wrong. + * + * Note that since encoder objects can't be hotplugged the assigned indices + * are stable and hence known before registering all objects. + */ uint32_t possible_clones; struct drm_crtc *crtc; @@ -146,7 +196,7 @@ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc); * @encoder: encoder to test * @crtc: crtc to test * - * Return false if @encoder can't be driven by @crtc, true otherwise. + * Returns false if @encoder can't be driven by @crtc, true otherwise. */ static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder, struct drm_crtc *crtc) @@ -154,11 +204,21 @@ static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder, return !!(encoder->possible_crtcs & drm_crtc_mask(crtc)); } +/** + * drm_encoder_find - find a &drm_encoder + * @dev: DRM device + * @id: encoder id + * + * Returns the encoder with @id, NULL if it doesn't exist. Simple wrapper around + * drm_mode_object_find(). + */ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, - uint32_t id) + uint32_t id) { struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); + return mo ? obj_to_encoder(mo) : NULL; } From 949619f32eee37a6385de1e976523501c8256768 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:51 +0200 Subject: [PATCH 16/24] drm: Extract drm_mode_object.[hc] Just for the struct drm_mode_object base class. The header file was already partially extracted to help untangle the include loops. v2: - Also move the generic get/set property ioctls. At first this seemed like a bad idea since it requires making drm_mode_crtc_set_obj_prop non-static. But eventually that will get split away too (like the connector version already is) for both crtc and planes. Hence I reconsidered. - drm_mode_object.[hc] instead of drm_modeset.[hc], which requires renaming the drm_modeset.h header I already started building up. This is more consistent (matches the name of the main structure), and I want to be able to use drm_modeset.[hc] for the basic modeset init/cleanup functionality like drm_mode_config_init. Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-3-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 9 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_crtc.c | 413 +---------------- drivers/gpu/drm/drm_crtc_internal.h | 52 ++- drivers/gpu/drm/drm_mode_object.c | 435 ++++++++++++++++++ include/drm/drm_connector.h | 2 +- include/drm/drm_crtc.h | 12 +- include/drm/drm_encoder.h | 2 +- include/drm/drm_framebuffer.h | 2 +- .../drm/{drm_modeset.h => drm_mode_object.h} | 10 + include/drm/drm_modes.h | 2 +- 11 files changed, 492 insertions(+), 449 deletions(-) create mode 100644 drivers/gpu/drm/drm_mode_object.c rename include/drm/{drm_modeset.h => drm_mode_object.h} (87%) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 47c2835b7c2d..b164472f2157 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -15,6 +15,15 @@ be setup by initializing the following fields. - struct drm_mode_config_funcs \*funcs; Mode setting functions. +Modeset Base Object Abstraction +=============================== + +.. kernel-doc:: include/drm/drm_mode_object.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_mode_object.c + :export: + KMS Data Structures =================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index efdb4176230f..59979f3f3648 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ drm_modeset_lock.o drm_atomic.o drm_bridge.o \ drm_framebuffer.o drm_connector.o drm_blend.o \ - drm_encoder.o + drm_encoder.o drm_mode_object.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 219cd4ee23b7..c9dc031d9b36 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -57,162 +57,6 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { /* * Optional properties */ -/* - * Internal function to assign a slot in the object idr and optionally - * register the object into the idr. - */ -int drm_mode_object_get_reg(struct drm_device *dev, - struct drm_mode_object *obj, - uint32_t obj_type, - bool register_obj, - void (*obj_free_cb)(struct kref *kref)) -{ - int ret; - - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); - if (ret >= 0) { - /* - * Set up the object linking under the protection of the idr - * lock so that other users can't see inconsistent state. - */ - obj->id = ret; - obj->type = obj_type; - if (obj_free_cb) { - obj->free_cb = obj_free_cb; - kref_init(&obj->refcount); - } - } - mutex_unlock(&dev->mode_config.idr_mutex); - - return ret < 0 ? ret : 0; -} - -/** - * drm_mode_object_get - allocate a new modeset identifier - * @dev: DRM device - * @obj: object pointer, used to generate unique ID - * @obj_type: object type - * - * Create a unique identifier based on @ptr in @dev's identifier space. Used - * for tracking modes, CRTCs and connectors. Note that despite the _get postfix - * modeset identifiers are _not_ reference counted. Hence don't use this for - * reference counted modeset objects like framebuffers. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type) -{ - return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL); -} - -void drm_mode_object_register(struct drm_device *dev, - struct drm_mode_object *obj) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); - mutex_unlock(&dev->mode_config.idr_mutex); -} - -/** - * drm_mode_object_unregister - free a modeset identifer - * @dev: DRM device - * @object: object to free - * - * Free @id from @dev's unique identifier pool. - * This function can be called multiple times, and guards against - * multiple removals. - * These modeset identifiers are _not_ reference counted. Hence don't use this - * for reference counted modeset objects like framebuffers. - */ -void drm_mode_object_unregister(struct drm_device *dev, - struct drm_mode_object *object) -{ - mutex_lock(&dev->mode_config.idr_mutex); - if (object->id) { - idr_remove(&dev->mode_config.crtc_idr, object->id); - object->id = 0; - } - mutex_unlock(&dev->mode_config.idr_mutex); -} - -struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) - obj = NULL; - if (obj && obj->id != id) - obj = NULL; - - if (obj && obj->free_cb) { - if (!kref_get_unless_zero(&obj->refcount)) - obj = NULL; - } - mutex_unlock(&dev->mode_config.idr_mutex); - - return obj; -} - -/** - * drm_mode_object_find - look up a drm object with static lifetime - * @dev: drm device - * @id: id of the mode object - * @type: type of the mode object - * - * This function is used to look up a modeset object. It will acquire a - * reference for reference counted objects. This reference must be dropped again - * by callind drm_mode_object_unreference(). - */ -struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - obj = __drm_mode_object_find(dev, id, type); - return obj; -} -EXPORT_SYMBOL(drm_mode_object_find); - -/** - * drm_mode_object_unreference - decr the object refcnt - * @obj: mode_object - * - * This functions decrements the object's refcount if it is a refcounted modeset - * object. It is a no-op on any other object. This is used to drop references - * acquired with drm_mode_object_reference(). - */ -void drm_mode_object_unreference(struct drm_mode_object *obj) -{ - if (obj->free_cb) { - DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); - kref_put(&obj->refcount, obj->free_cb); - } -} -EXPORT_SYMBOL(drm_mode_object_unreference); - -/** - * drm_mode_object_reference - incr the object refcnt - * @obj: mode_object - * - * This functions increments the object's refcount if it is a refcounted modeset - * object. It is a no-op on any other object. References should be dropped again - * by calling drm_mode_object_unreference(). - */ -void drm_mode_object_reference(struct drm_mode_object *obj) -{ - if (obj->free_cb) { - DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); - kref_get(&obj->refcount); - } -} -EXPORT_SYMBOL(drm_mode_object_reference); - /** * drm_crtc_force_disable - Forcibly turn off a CRTC * @crtc: CRTC to turn off @@ -1010,45 +854,6 @@ int drm_mode_getcrtc(struct drm_device *dev, return 0; } -/* helper for getconnector and getproperties ioctls */ -int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, - uint32_t __user *prop_ptr, - uint64_t __user *prop_values, - uint32_t *arg_count_props) -{ - int props_count; - int i, ret, copied; - - props_count = obj->properties->count; - if (!atomic) - props_count -= obj->properties->atomic_count; - - if ((*arg_count_props >= props_count) && props_count) { - for (i = 0, copied = 0; copied < props_count; i++) { - struct drm_property *prop = obj->properties->properties[i]; - uint64_t val; - - if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) - continue; - - ret = drm_object_property_get_value(obj, prop, &val); - if (ret) - return ret; - - if (put_user(prop->base.id, prop_ptr + copied)) - return -EFAULT; - - if (put_user(val, prop_values + copied)) - return -EFAULT; - - copied++; - } - } - *arg_count_props = props_count; - - return 0; -} - /** * drm_mode_getplane_res - enumerate all plane resources * @dev: DRM device @@ -2290,105 +2095,6 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); -/** - * drm_object_attach_property - attach a property to a modeset object - * @obj: drm modeset object - * @property: property to attach - * @init_val: initial value of the property - * - * This attaches the given property to the modeset object with the given initial - * value. Currently this function cannot fail since the properties are stored in - * a statically sized array. - */ -void drm_object_attach_property(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t init_val) -{ - int count = obj->properties->count; - - if (count == DRM_OBJECT_MAX_PROPERTY) { - WARN(1, "Failed to attach object property (type: 0x%x). Please " - "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " - "you see this message on the same object type.\n", - obj->type); - return; - } - - obj->properties->properties[count] = property; - obj->properties->values[count] = init_val; - obj->properties->count++; - if (property->flags & DRM_MODE_PROP_ATOMIC) - obj->properties->atomic_count++; -} -EXPORT_SYMBOL(drm_object_attach_property); - -/** - * drm_object_property_set_value - set the value of a property - * @obj: drm mode object to set property value for - * @property: property to set - * @val: value the property should be set to - * - * This functions sets a given property on a given object. This function only - * changes the software state of the property, it does not call into the - * driver's ->set_property callback. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_set_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t val) -{ - int i; - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - obj->properties->values[i] = val; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_set_value); - -/** - * drm_object_property_get_value - retrieve the value of a property - * @obj: drm mode object to get property value from - * @property: property to retrieve - * @val: storage for the property value - * - * This function retrieves the softare state of the given property for the given - * property. Since there is no driver callback to retrieve the current property - * value this might be out of sync with the hardware, depending upon the driver - * and property. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_get_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val) -{ - int i; - - /* read-only properties bypass atomic mechanism and still store - * their value in obj->properties->values[].. mostly to avoid - * having to deal w/ EDID and similar props in atomic paths: - */ - if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && - !(property->flags & DRM_MODE_PROP_IMMUTABLE)) - return drm_atomic_get_property(obj, property, val); - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - *val = obj->properties->values[i]; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_get_value); - /** * drm_mode_getproperty_ioctl - get the property metadata * @dev: DRM device @@ -2934,9 +2640,9 @@ void drm_property_change_valid_put(struct drm_property *property, drm_property_unreference_blob(obj_to_blob(ref)); } -static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) { int ret = -EINVAL; struct drm_crtc *crtc = obj_to_crtc(obj); @@ -2978,119 +2684,6 @@ int drm_mode_plane_set_obj_prop(struct drm_plane *plane, } EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); -/** - * drm_mode_obj_get_properties_ioctl - get the current value of a object's property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the current value for an object's property. Compared - * to the connector specific ioctl this one is extended to also work on crtc and - * plane objects. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_get_properties *arg = data; - struct drm_mode_object *obj; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!obj) { - ret = -ENOENT; - goto out; - } - if (!obj->properties) { - ret = -EINVAL; - goto out_unref; - } - - ret = drm_mode_object_get_properties(obj, file_priv->atomic, - (uint32_t __user *)(unsigned long)(arg->props_ptr), - (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), - &arg->count_props); - -out_unref: - drm_mode_object_unreference(obj); -out: - drm_modeset_unlock_all(dev); - return ret; -} - -int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_set_property *arg = data; - struct drm_mode_object *arg_obj; - struct drm_mode_object *prop_obj; - struct drm_property *property; - int i, ret = -EINVAL; - struct drm_mode_object *ref; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!arg_obj) { - ret = -ENOENT; - goto out; - } - if (!arg_obj->properties) - goto out_unref; - - for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->properties[i]->base.id == arg->prop_id) - break; - - if (i == arg_obj->properties->count) - goto out_unref; - - prop_obj = drm_mode_object_find(dev, arg->prop_id, - DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) { - ret = -ENOENT; - goto out_unref; - } - property = obj_to_property(prop_obj); - - if (!drm_property_change_valid_get(property, arg->value, &ref)) - goto out_unref; - - switch (arg_obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: - ret = drm_mode_connector_set_obj_prop(arg_obj, property, - arg->value); - break; - case DRM_MODE_OBJECT_CRTC: - ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); - break; - case DRM_MODE_OBJECT_PLANE: - ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), - property, arg->value); - break; - } - - drm_property_change_valid_put(property, ref); - -out_unref: - drm_mode_object_unreference(arg_obj); -out: - drm_modeset_unlock_all(dev); - return ret; -} - /** * drm_mode_crtc_set_gamma_size - set the gamma table size * @crtc: CRTC to set the gamma table size for diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 54f9a2c85965..2eb1d941ea70 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -33,29 +33,14 @@ /* drm_crtc.c */ -int drm_mode_object_get_reg(struct drm_device *dev, - struct drm_mode_object *obj, - uint32_t obj_type, - bool register_obj, - void (*obj_free_cb)(struct kref *kref)); -void drm_mode_object_register(struct drm_device *dev, - struct drm_mode_object *obj); -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type); -struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, - uint32_t id, uint32_t type); -void drm_mode_object_unregister(struct drm_device *dev, - struct drm_mode_object *object); -int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, - uint32_t __user *prop_ptr, - uint64_t __user *prop_values, - uint32_t *arg_count_props); +int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value); bool drm_property_change_valid_get(struct drm_property *property, uint64_t value, struct drm_mode_object **ref); void drm_property_change_valid_put(struct drm_property *property, struct drm_mode_object *ref); - int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format); int drm_crtc_check_viewport(const struct drm_crtc *crtc, @@ -76,11 +61,6 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* IOCTLs */ -int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_getplane_res(struct drm_device *dev, void *data, @@ -113,6 +93,32 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* drm_mode_object.c */ +int drm_mode_object_get_reg(struct drm_device *dev, + struct drm_mode_object *obj, + uint32_t obj_type, + bool register_obj, + void (*obj_free_cb)(struct kref *kref)); +void drm_mode_object_register(struct drm_device *dev, + struct drm_mode_object *obj); +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type); +struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type); +void drm_mode_object_unregister(struct drm_device *dev, + struct drm_mode_object *object); +int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, + uint64_t __user *prop_values, + uint32_t *arg_count_props); + +/* IOCTL */ + +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + /* drm_encoder.c */ int drm_encoder_register_all(struct drm_device *dev); void drm_encoder_unregister_all(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c new file mode 100644 index 000000000000..cef9104e8285 --- /dev/null +++ b/drivers/gpu/drm/drm_mode_object.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "drm_crtc_internal.h" + +/* + * Internal function to assign a slot in the object idr and optionally + * register the object into the idr. + */ +int drm_mode_object_get_reg(struct drm_device *dev, + struct drm_mode_object *obj, + uint32_t obj_type, + bool register_obj, + void (*obj_free_cb)(struct kref *kref)) +{ + int ret; + + mutex_lock(&dev->mode_config.idr_mutex); + ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); + if (ret >= 0) { + /* + * Set up the object linking under the protection of the idr + * lock so that other users can't see inconsistent state. + */ + obj->id = ret; + obj->type = obj_type; + if (obj_free_cb) { + obj->free_cb = obj_free_cb; + kref_init(&obj->refcount); + } + } + mutex_unlock(&dev->mode_config.idr_mutex); + + return ret < 0 ? ret : 0; +} + +/** + * drm_mode_object_get - allocate a new modeset identifier + * @dev: DRM device + * @obj: object pointer, used to generate unique ID + * @obj_type: object type + * + * Create a unique identifier based on @ptr in @dev's identifier space. Used + * for tracking modes, CRTCs and connectors. Note that despite the _get postfix + * modeset identifiers are _not_ reference counted. Hence don't use this for + * reference counted modeset objects like framebuffers. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type) +{ + return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL); +} + +void drm_mode_object_register(struct drm_device *dev, + struct drm_mode_object *obj) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); + mutex_unlock(&dev->mode_config.idr_mutex); +} + +/** + * drm_mode_object_unregister - free a modeset identifer + * @dev: DRM device + * @object: object to free + * + * Free @id from @dev's unique identifier pool. + * This function can be called multiple times, and guards against + * multiple removals. + * These modeset identifiers are _not_ reference counted. Hence don't use this + * for reference counted modeset objects like framebuffers. + */ +void drm_mode_object_unregister(struct drm_device *dev, + struct drm_mode_object *object) +{ + mutex_lock(&dev->mode_config.idr_mutex); + if (object->id) { + idr_remove(&dev->mode_config.crtc_idr, object->id); + object->id = 0; + } + mutex_unlock(&dev->mode_config.idr_mutex); +} + +struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) + obj = NULL; + if (obj && obj->id != id) + obj = NULL; + + if (obj && obj->free_cb) { + if (!kref_get_unless_zero(&obj->refcount)) + obj = NULL; + } + mutex_unlock(&dev->mode_config.idr_mutex); + + return obj; +} + +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * This function is used to look up a modeset object. It will acquire a + * reference for reference counted objects. This reference must be dropped again + * by callind drm_mode_object_unreference(). + */ +struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + obj = __drm_mode_object_find(dev, id, type); + return obj; +} +EXPORT_SYMBOL(drm_mode_object_find); + +/** + * drm_mode_object_unreference - decr the object refcnt + * @obj: mode_object + * + * This functions decrements the object's refcount if it is a refcounted modeset + * object. It is a no-op on any other object. This is used to drop references + * acquired with drm_mode_object_reference(). + */ +void drm_mode_object_unreference(struct drm_mode_object *obj) +{ + if (obj->free_cb) { + DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); + kref_put(&obj->refcount, obj->free_cb); + } +} +EXPORT_SYMBOL(drm_mode_object_unreference); + +/** + * drm_mode_object_reference - incr the object refcnt + * @obj: mode_object + * + * This functions increments the object's refcount if it is a refcounted modeset + * object. It is a no-op on any other object. References should be dropped again + * by calling drm_mode_object_unreference(). + */ +void drm_mode_object_reference(struct drm_mode_object *obj) +{ + if (obj->free_cb) { + DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); + kref_get(&obj->refcount); + } +} +EXPORT_SYMBOL(drm_mode_object_reference); + +/** + * drm_object_attach_property - attach a property to a modeset object + * @obj: drm modeset object + * @property: property to attach + * @init_val: initial value of the property + * + * This attaches the given property to the modeset object with the given initial + * value. Currently this function cannot fail since the properties are stored in + * a statically sized array. + */ +void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val) +{ + int count = obj->properties->count; + + if (count == DRM_OBJECT_MAX_PROPERTY) { + WARN(1, "Failed to attach object property (type: 0x%x). Please " + "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " + "you see this message on the same object type.\n", + obj->type); + return; + } + + obj->properties->properties[count] = property; + obj->properties->values[count] = init_val; + obj->properties->count++; + if (property->flags & DRM_MODE_PROP_ATOMIC) + obj->properties->atomic_count++; +} +EXPORT_SYMBOL(drm_object_attach_property); + +/** + * drm_object_property_set_value - set the value of a property + * @obj: drm mode object to set property value for + * @property: property to set + * @val: value the property should be set to + * + * This functions sets a given property on a given object. This function only + * changes the software state of the property, it does not call into the + * driver's ->set_property callback. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t val) +{ + int i; + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + obj->properties->values[i] = val; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_object_property_set_value); + +/** + * drm_object_property_get_value - retrieve the value of a property + * @obj: drm mode object to get property value from + * @property: property to retrieve + * @val: storage for the property value + * + * This function retrieves the softare state of the given property for the given + * property. Since there is no driver callback to retrieve the current property + * value this might be out of sync with the hardware, depending upon the driver + * and property. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_object_property_get_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val) +{ + int i; + + /* read-only properties bypass atomic mechanism and still store + * their value in obj->properties->values[].. mostly to avoid + * having to deal w/ EDID and similar props in atomic paths: + */ + if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + !(property->flags & DRM_MODE_PROP_IMMUTABLE)) + return drm_atomic_get_property(obj, property, val); + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + *val = obj->properties->values[i]; + return 0; + } + + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_object_property_get_value); + +/* helper for getconnector and getproperties ioctls */ +int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, + uint64_t __user *prop_values, + uint32_t *arg_count_props) +{ + int props_count; + int i, ret, copied; + + props_count = obj->properties->count; + if (!atomic) + props_count -= obj->properties->atomic_count; + + if ((*arg_count_props >= props_count) && props_count) { + for (i = 0, copied = 0; copied < props_count; i++) { + struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; + + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + + ret = drm_object_property_get_value(obj, prop, &val); + if (ret) + return ret; + + if (put_user(prop->base.id, prop_ptr + copied)) + return -EFAULT; + + if (put_user(val, prop_values + copied)) + return -EFAULT; + + copied++; + } + } + *arg_count_props = props_count; + + return 0; +} + +/** + * drm_mode_obj_get_properties_ioctl - get the current value of a object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an object's property. Compared + * to the connector specific ioctl this one is extended to also work on crtc and + * plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_get_properties *arg = data; + struct drm_mode_object *obj; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + + obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!obj) { + ret = -ENOENT; + goto out; + } + if (!obj->properties) { + ret = -EINVAL; + goto out_unref; + } + + ret = drm_mode_object_get_properties(obj, file_priv->atomic, + (uint32_t __user *)(unsigned long)(arg->props_ptr), + (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), + &arg->count_props); + +out_unref: + drm_mode_object_unreference(obj); +out: + drm_modeset_unlock_all(dev); + return ret; +} + +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_set_property *arg = data; + struct drm_mode_object *arg_obj; + struct drm_mode_object *prop_obj; + struct drm_property *property; + int i, ret = -EINVAL; + struct drm_mode_object *ref; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + + arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!arg_obj) { + ret = -ENOENT; + goto out; + } + if (!arg_obj->properties) + goto out_unref; + + for (i = 0; i < arg_obj->properties->count; i++) + if (arg_obj->properties->properties[i]->base.id == arg->prop_id) + break; + + if (i == arg_obj->properties->count) + goto out_unref; + + prop_obj = drm_mode_object_find(dev, arg->prop_id, + DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) { + ret = -ENOENT; + goto out_unref; + } + property = obj_to_property(prop_obj); + + if (!drm_property_change_valid_get(property, arg->value, &ref)) + goto out_unref; + + switch (arg_obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: + ret = drm_mode_connector_set_obj_prop(arg_obj, property, + arg->value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); + break; + case DRM_MODE_OBJECT_PLANE: + ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), + property, arg->value); + break; + } + + drm_property_change_valid_put(property, ref); + +out_unref: + drm_mode_object_unreference(arg_obj); +out: + drm_modeset_unlock_all(dev); + return ret; +} diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f1576db6c044..66b7d6744dd2 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -25,7 +25,7 @@ #include #include -#include +#include struct drm_connector_helper_funcs; struct drm_device; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c459867ecb9c..8ea3e65fe0c2 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -2116,16 +2116,6 @@ static inline bool drm_property_type_is(struct drm_property *property, return property->flags & type; } -extern int drm_object_property_set_value(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t val); -extern int drm_object_property_get_value(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t *value); - -extern void drm_object_attach_property(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t init_val); extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, const char *name, int num_values); extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 3d7350f1fcc1..fce0203094f7 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -25,7 +25,7 @@ #include #include -#include +#include /** * struct drm_encoder_funcs - encoder controls diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index 50deb40d3bfd..b2554c50a903 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -25,7 +25,7 @@ #include #include -#include +#include struct drm_framebuffer; struct drm_file; diff --git a/include/drm/drm_modeset.h b/include/drm/drm_mode_object.h similarity index 87% rename from include/drm/drm_modeset.h rename to include/drm/drm_mode_object.h index fe910d5efe12..c0e4414299f7 100644 --- a/include/drm/drm_modeset.h +++ b/include/drm/drm_mode_object.h @@ -67,4 +67,14 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, void drm_mode_object_reference(struct drm_mode_object *obj); void drm_mode_object_unreference(struct drm_mode_object *obj); +int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t val); +int drm_object_property_get_value(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t *value); + +void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val); #endif diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 1621e9b32330..011f199d3bcf 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -27,7 +27,7 @@ #ifndef __DRM_MODES_H__ #define __DRM_MODES_H__ -#include +#include #include /* From f094d881954982b559e98e90aca1bf6f45141420 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:52 +0200 Subject: [PATCH 17/24] drm: Remove drm_mode_object->atomic_count It's only used in drm_mode_object_get_properties, and we can compute it there directly with a bit of code shuffling. Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-4-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_mode_object.c | 31 ++++++++++++------------------- include/drm/drm_mode_object.h | 2 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index cef9104e8285..a92aeed51156 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -209,8 +209,6 @@ void drm_object_attach_property(struct drm_mode_object *obj, obj->properties->properties[count] = property; obj->properties->values[count] = init_val; obj->properties->count++; - if (property->flags & DRM_MODE_PROP_ATOMIC) - obj->properties->atomic_count++; } EXPORT_SYMBOL(drm_object_attach_property); @@ -288,35 +286,30 @@ int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, uint64_t __user *prop_values, uint32_t *arg_count_props) { - int props_count; - int i, ret, copied; + int i, ret, count; - props_count = obj->properties->count; - if (!atomic) - props_count -= obj->properties->atomic_count; + for (i = 0, count = 0; i < obj->properties->count; i++) { + struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; - if ((*arg_count_props >= props_count) && props_count) { - for (i = 0, copied = 0; copied < props_count; i++) { - struct drm_property *prop = obj->properties->properties[i]; - uint64_t val; - - if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) - continue; + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + if (*arg_count_props > count) { ret = drm_object_property_get_value(obj, prop, &val); if (ret) return ret; - if (put_user(prop->base.id, prop_ptr + copied)) + if (put_user(prop->base.id, prop_ptr + count)) return -EFAULT; - if (put_user(val, prop_values + copied)) + if (put_user(val, prop_values + count)) return -EFAULT; - - copied++; } + + count++; } - *arg_count_props = props_count; + *arg_count_props = count; return 0; } diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index c0e4414299f7..b8adb6425f2a 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -37,7 +37,7 @@ struct drm_mode_object { #define DRM_OBJECT_MAX_PROPERTY 24 struct drm_object_properties { - int count, atomic_count; + int count; /* NOTE: if we ever start dynamically destroying properties (ie. * not at drm_mode_config_cleanup() time), then we'd have to do * a better job of detaching property from mode objects to avoid From a2511a557eb868dc5fb28808ef58bd8af2e51f3b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:53 +0200 Subject: [PATCH 18/24] drm/doc: Polish docs for drm_mode_object I figured an overview section here is overkill, and better to just document the 2 structures themselves well enough. v2: Review from Archit: - Appease checkpatch in moved code. - Spelling fixes in the kerneldoc. Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-5-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_mode_object.c | 17 ++++++++--- include/drm/drm_mode_object.h | 50 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index a92aeed51156..6edda8382a4c 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -97,7 +97,7 @@ void drm_mode_object_register(struct drm_device *dev, * for reference counted modeset objects like framebuffers. */ void drm_mode_object_unregister(struct drm_device *dev, - struct drm_mode_object *object) + struct drm_mode_object *object) { mutex_lock(&dev->mode_config.idr_mutex); if (object->id) { @@ -152,7 +152,7 @@ EXPORT_SYMBOL(drm_mode_object_find); * drm_mode_object_unreference - decr the object refcnt * @obj: mode_object * - * This functions decrements the object's refcount if it is a refcounted modeset + * This function decrements the object's refcount if it is a refcounted modeset * object. It is a no-op on any other object. This is used to drop references * acquired with drm_mode_object_reference(). */ @@ -169,7 +169,7 @@ EXPORT_SYMBOL(drm_mode_object_unreference); * drm_mode_object_reference - incr the object refcnt * @obj: mode_object * - * This functions increments the object's refcount if it is a refcounted modeset + * This function increments the object's refcount if it is a refcounted modeset * object. It is a no-op on any other object. References should be dropped again * by calling drm_mode_object_unreference(). */ @@ -218,10 +218,16 @@ EXPORT_SYMBOL(drm_object_attach_property); * @property: property to set * @val: value the property should be set to * - * This functions sets a given property on a given object. This function only + * This function sets a given property on a given object. This function only * changes the software state of the property, it does not call into the * driver's ->set_property callback. * + * Note that atomic drivers should not have any need to call this, the core will + * ensure consistency of values reported back to userspace through the + * appropriate ->atomic_get_property callback. Only legacy drivers should call + * this function to update the tracked value (after clamping and other + * restrictions have been applied). + * * Returns: * Zero on success, error code on failure. */ @@ -252,6 +258,9 @@ EXPORT_SYMBOL(drm_object_property_set_value); * value this might be out of sync with the hardware, depending upon the driver * and property. * + * Atomic drivers should never call this function directly, the core will read + * out property values through the various ->atomic_get_property callbacks. + * * Returns: * Zero on success, error code on failure. */ diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index b8adb6425f2a..be3d93839ae2 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -27,6 +27,28 @@ struct drm_object_properties; struct drm_property; +/** + * struct drm_mode_object - base structure for modeset objects + * @id: userspace visible identifier + * @type: type of the object, one of DRM_MODE_OBJECT\_\* + * @properties: properties attached to this object, including values + * @refcount: reference count for objects which with dynamic lifetime + * @free_cb: free function callback, only set for objects with dynamic lifetime + * + * Base structure for modeset objects visible to userspace. Objects can be + * looked up using drm_mode_object_find(). Besides basic uapi interface + * properties like @id and @type it provides two services: + * + * - It tracks attached properties and their values. This is used by &drm_crtc, + * &drm_plane and &drm_connector. Properties are attached by calling + * drm_object_attach_property() before the object is visible to userspace. + * + * - For objects with dynamic lifetimes (as indicated by a non-NULL @free_cb) it + * provides reference counting through drm_mode_object_reference() and + * drm_mode_object_unreference(). This is used by &drm_framebuffer, + * &drm_connector and &drm_property_blob. These objects provide specialized + * reference counting wrappers. + */ struct drm_mode_object { uint32_t id; uint32_t type; @@ -36,16 +58,38 @@ struct drm_mode_object { }; #define DRM_OBJECT_MAX_PROPERTY 24 +/** + * struct drm_object_properties - property tracking for &drm_mode_object + */ struct drm_object_properties { + /** + * @count: number of valid properties, must be less than or equal to + * DRM_OBJECT_MAX_PROPERTY. + */ + int count; - /* NOTE: if we ever start dynamically destroying properties (ie. + /** + * @properties: Array of pointers to &drm_property. + * + * NOTE: if we ever start dynamically destroying properties (ie. * not at drm_mode_config_cleanup() time), then we'd have to do * a better job of detaching property from mode objects to avoid * dangling property pointers: */ struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY]; - /* do not read/write values directly, but use drm_object_property_get_value() - * and drm_object_property_set_value(): + + /** + * @values: Array to store the property values, matching @properties. Do + * not read/write values directly, but use + * drm_object_property_get_value() and drm_object_property_set_value(). + * + * Note that atomic drivers do not store mutable properties in this + * array, but only the decoded values in the corresponding state + * structure. The decoding is done using the ->atomic_get_property and + * ->atomic_set_property hooks of the corresponding object. Hence atomic + * drivers should not use drm_object_property_set_value() and + * drm_object_property_get_value() on mutable objects, i.e. those + * without the DRM_MODE_PROP_IMMUTABLE flag set. */ uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; From ec5e304747241823435b160d3eabf1295c06d2e3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:54 +0200 Subject: [PATCH 19/24] drm: move drm_mode_legacy_fb_format to drm_fourcc.c It's part of the drm fourcc handling code, mapping the old depth/bpp values to new fourcc codes. Cc: Laurent Pinchart Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-6-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc.c | 43 ------------------------------------ drivers/gpu/drm/drm_fourcc.c | 43 ++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 -- include/drm/drm_fourcc.h | 1 + 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c9dc031d9b36..9ff58e6b51c4 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1671,49 +1671,6 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, req, file_priv); } -/** - * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description - * @bpp: bits per pixels - * @depth: bit depth per pixel - * - * Computes a drm fourcc pixel format code for the given @bpp/@depth values. - * Useful in fbdev emulation code, since that deals in those values. - */ -uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) -{ - uint32_t fmt; - - switch (bpp) { - case 8: - fmt = DRM_FORMAT_C8; - break; - case 16: - if (depth == 15) - fmt = DRM_FORMAT_XRGB1555; - else - fmt = DRM_FORMAT_RGB565; - break; - case 24: - fmt = DRM_FORMAT_RGB888; - break; - case 32: - if (depth == 24) - fmt = DRM_FORMAT_XRGB8888; - else if (depth == 30) - fmt = DRM_FORMAT_XRGB2101010; - else - fmt = DRM_FORMAT_ARGB8888; - break; - default: - DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); - fmt = DRM_FORMAT_XRGB8888; - break; - } - - return fmt; -} -EXPORT_SYMBOL(drm_mode_legacy_fb_format); - static bool drm_property_type_valid(struct drm_property *property) { if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index c81546c15c93..29c56b4331e0 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -35,6 +35,49 @@ static char printable_char(int c) return isascii(c) && isprint(c) ? c : '?'; } +/** + * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description + * @bpp: bits per pixels + * @depth: bit depth per pixel + * + * Computes a drm fourcc pixel format code for the given @bpp/@depth values. + * Useful in fbdev emulation code, since that deals in those values. + */ +uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) +{ + uint32_t fmt; + + switch (bpp) { + case 8: + fmt = DRM_FORMAT_C8; + break; + case 16: + if (depth == 15) + fmt = DRM_FORMAT_XRGB1555; + else + fmt = DRM_FORMAT_RGB565; + break; + case 24: + fmt = DRM_FORMAT_RGB888; + break; + case 32: + if (depth == 24) + fmt = DRM_FORMAT_XRGB8888; + else if (depth == 30) + fmt = DRM_FORMAT_XRGB2101010; + else + fmt = DRM_FORMAT_ARGB8888; + break; + default: + DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); + fmt = DRM_FORMAT_XRGB8888; + break; + } + + return fmt; +} +EXPORT_SYMBOL(drm_mode_legacy_fb_format); + /** * drm_get_format_name - return a string for drm fourcc format * @format: format to compute name of diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8ea3e65fe0c2..63f03490e987 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -2158,8 +2158,6 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, extern int drm_mode_set_config_internal(struct drm_mode_set *set); -extern uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); - extern struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, char topology[8]); extern struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index b106337de1bf..30c30fa87ee8 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -25,6 +25,7 @@ #include #include +uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); int drm_format_num_planes(uint32_t format); int drm_format_plane_cpp(uint32_t format, int plane); From 59e71ee746a37fe077b73cecf189de1d27efd6eb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:55 +0200 Subject: [PATCH 20/24] drm: Extract drm_property.[hc] This just contains the base property classes and all the code to handle blobs. I think for any kind of standardized/shared properties it's better to have separate files - this is fairly big already as-is. v2: resurrect misplaced hunk (Daniel Stone) Cc: Daniel Stone Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-7-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 9 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_crtc.c | 926 --------------------------- drivers/gpu/drm/drm_crtc_internal.h | 32 +- drivers/gpu/drm/drm_property.c | 953 ++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 88 +-- include/drm/drm_property.h | 120 ++++ 7 files changed, 1102 insertions(+), 1028 deletions(-) create mode 100644 drivers/gpu/drm/drm_property.c create mode 100644 include/drm/drm_property.h diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index b164472f2157..e07a2667ab61 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -389,6 +389,15 @@ connector and plane objects by calling the pointer to the target object, a pointer to the previously created property and an initial instance value. +Property Types and Blob Property Support +---------------------------------------- + +.. kernel-doc:: include/drm/drm_property.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_property.c + :export: + Blending and Z-Position properties ---------------------------------- diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 59979f3f3648..12a966ec7298 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ drm_modeset_lock.o drm_atomic.o drm_bridge.o \ drm_framebuffer.o drm_connector.o drm_blend.o \ - drm_encoder.o drm_mode_object.o + drm_encoder.o drm_mode_object.o drm_property.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9ff58e6b51c4..b95c48acfa5b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1671,932 +1671,6 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, req, file_priv); } -static bool drm_property_type_valid(struct drm_property *property) -{ - if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) - return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); - return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); -} - -/** - * drm_property_create - create a new property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Note that the DRM core keeps a per-device list of properties and that, if - * drm_mode_config_cleanup() is called, it will destroy all properties created - * by the driver. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create(struct drm_device *dev, int flags, - const char *name, int num_values) -{ - struct drm_property *property = NULL; - int ret; - - property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); - if (!property) - return NULL; - - property->dev = dev; - - if (num_values) { - property->values = kcalloc(num_values, sizeof(uint64_t), - GFP_KERNEL); - if (!property->values) - goto fail; - } - - ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); - if (ret) - goto fail; - - property->flags = flags; - property->num_values = num_values; - INIT_LIST_HEAD(&property->enum_list); - - if (name) { - strncpy(property->name, name, DRM_PROP_NAME_LEN); - property->name[DRM_PROP_NAME_LEN-1] = '\0'; - } - - list_add_tail(&property->head, &dev->mode_config.property_list); - - WARN_ON(!drm_property_type_valid(property)); - - return property; -fail: - kfree(property->values); - kfree(property); - return NULL; -} -EXPORT_SYMBOL(drm_property_create); - -/** - * drm_property_create_enum - create a new enumeration property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property values - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set one of the predefined values for enumeration - * properties. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, - const char *name, - const struct drm_prop_enum_list *props, - int num_values) -{ - struct drm_property *property; - int i, ret; - - flags |= DRM_MODE_PROP_ENUM; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - - for (i = 0; i < num_values; i++) { - ret = drm_property_add_enum(property, i, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_enum); - -/** - * drm_property_create_bitmask - create a new bitmask property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property bitflags - * @num_props: size of the @props array - * @supported_bits: bitmask of all supported enumeration values - * - * This creates a new bitmask drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Compared to plain enumeration properties userspace is allowed to set any - * or'ed together combination of the predefined property bitflag values - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bitmask(struct drm_device *dev, - int flags, const char *name, - const struct drm_prop_enum_list *props, - int num_props, - uint64_t supported_bits) -{ - struct drm_property *property; - int i, ret, index = 0; - int num_values = hweight64(supported_bits); - - flags |= DRM_MODE_PROP_BITMASK; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - for (i = 0; i < num_props; i++) { - if (!(supported_bits & (1ULL << props[i].type))) - continue; - - if (WARN_ON(index >= num_values)) { - drm_property_destroy(dev, property); - return NULL; - } - - ret = drm_property_add_enum(property, index++, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_bitmask); - -static struct drm_property *property_create_range(struct drm_device *dev, - int flags, const char *name, - uint64_t min, uint64_t max) -{ - struct drm_property *property; - - property = drm_property_create(dev, flags, name, 2); - if (!property) - return NULL; - - property->values[0] = min; - property->values[1] = max; - - return property; -} - -/** - * drm_property_create_range - create a new unsigned ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any unsigned integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, - const char *name, - uint64_t min, uint64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, - name, min, max); -} -EXPORT_SYMBOL(drm_property_create_range); - -/** - * drm_property_create_signed_range - create a new signed ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any signed integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_signed_range(struct drm_device *dev, - int flags, const char *name, - int64_t min, int64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, - name, I642U64(min), I642U64(max)); -} -EXPORT_SYMBOL(drm_property_create_signed_range); - -/** - * drm_property_create_object - create a new object property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @type: object type from DRM_MODE_OBJECT_* defines - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set this to any property value of the given - * @type. Only useful for atomic properties, which is enforced. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_object(struct drm_device *dev, - int flags, const char *name, uint32_t type) -{ - struct drm_property *property; - - flags |= DRM_MODE_PROP_OBJECT; - - if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) - return NULL; - - property = drm_property_create(dev, flags, name, 1); - if (!property) - return NULL; - - property->values[0] = type; - - return property; -} -EXPORT_SYMBOL(drm_property_create_object); - -/** - * drm_property_create_bool - create a new boolean property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * This is implemented as a ranged property with only {0, 1} as valid values. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, - const char *name) -{ - return drm_property_create_range(dev, flags, name, 0, 1); -} -EXPORT_SYMBOL(drm_property_create_bool); - -/** - * drm_property_add_enum - add a possible value to an enumeration property - * @property: enumeration property to change - * @index: index of the new enumeration - * @value: value of the new enumeration - * @name: symbolic name of the new enumeration - * - * This functions adds enumerations to a property. - * - * It's use is deprecated, drivers should use one of the more specific helpers - * to directly create the property with all enumerations already attached. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_property_add_enum(struct drm_property *property, int index, - uint64_t value, const char *name) -{ - struct drm_property_enum *prop_enum; - - if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) - return -EINVAL; - - /* - * Bitmask enum properties have the additional constraint of values - * from 0 to 63 - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && - (value > 63)) - return -EINVAL; - - if (!list_empty(&property->enum_list)) { - list_for_each_entry(prop_enum, &property->enum_list, head) { - if (prop_enum->value == value) { - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - return 0; - } - } - } - - prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); - if (!prop_enum) - return -ENOMEM; - - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - prop_enum->value = value; - - property->values[index] = value; - list_add_tail(&prop_enum->head, &property->enum_list); - return 0; -} -EXPORT_SYMBOL(drm_property_add_enum); - -/** - * drm_property_destroy - destroy a drm property - * @dev: drm device - * @property: property to destry - * - * This function frees a property including any attached resources like - * enumeration values. - */ -void drm_property_destroy(struct drm_device *dev, struct drm_property *property) -{ - struct drm_property_enum *prop_enum, *pt; - - list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { - list_del(&prop_enum->head); - kfree(prop_enum); - } - - if (property->num_values) - kfree(property->values); - drm_mode_object_unregister(dev, &property->base); - list_del(&property->head); - kfree(property); -} -EXPORT_SYMBOL(drm_property_destroy); - -/** - * drm_mode_getproperty_ioctl - get the property metadata - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the metadata for a given property, like the different - * possible values for an enum property or the limits for a range property. - * - * Blob properties are special - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getproperty_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_property *out_resp = data; - struct drm_property *property; - int enum_count = 0; - int value_count = 0; - int ret = 0, i; - int copied; - struct drm_property_enum *prop_enum; - struct drm_mode_property_enum __user *enum_ptr; - uint64_t __user *values_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - property = drm_property_find(dev, out_resp->prop_id); - if (!property) { - ret = -ENOENT; - goto done; - } - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - list_for_each_entry(prop_enum, &property->enum_list, head) - enum_count++; - } - - value_count = property->num_values; - - strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); - out_resp->name[DRM_PROP_NAME_LEN-1] = 0; - out_resp->flags = property->flags; - - if ((out_resp->count_values >= value_count) && value_count) { - values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; - for (i = 0; i < value_count; i++) { - if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - } - } - out_resp->count_values = value_count; - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { - copied = 0; - enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; - list_for_each_entry(prop_enum, &property->enum_list, head) { - - if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - - if (copy_to_user(&enum_ptr[copied].name, - &prop_enum->name, DRM_PROP_NAME_LEN)) { - ret = -EFAULT; - goto done; - } - copied++; - } - } - out_resp->count_enum_blobs = enum_count; - } - - /* - * NOTE: The idea seems to have been to use this to read all the blob - * property values. But nothing ever added them to the corresponding - * list, userspace always used the special-purpose get_blob ioctl to - * read the value for a blob property. It also doesn't make a lot of - * sense to return values here when everything else is just metadata for - * the property itself. - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - out_resp->count_enum_blobs = 0; -done: - drm_modeset_unlock_all(dev); - return ret; -} - -static void drm_property_free_blob(struct kref *kref) -{ - struct drm_property_blob *blob = - container_of(kref, struct drm_property_blob, base.refcount); - - mutex_lock(&blob->dev->mode_config.blob_lock); - list_del(&blob->head_global); - mutex_unlock(&blob->dev->mode_config.blob_lock); - - drm_mode_object_unregister(blob->dev, &blob->base); - - kfree(blob); -} - -/** - * drm_property_create_blob - Create new blob property - * - * Creates a new blob property for a specified DRM device, optionally - * copying data. - * - * @dev: DRM device to create property for - * @length: Length to allocate for blob data - * @data: If specified, copies data into blob - * - * Returns: - * New blob property with a single reference on success, or an ERR_PTR - * value on failure. - */ -struct drm_property_blob * -drm_property_create_blob(struct drm_device *dev, size_t length, - const void *data) -{ - struct drm_property_blob *blob; - int ret; - - if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) - return ERR_PTR(-EINVAL); - - blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); - if (!blob) - return ERR_PTR(-ENOMEM); - - /* This must be explicitly initialised, so we can safely call list_del - * on it in the removal handler, even if it isn't in a file list. */ - INIT_LIST_HEAD(&blob->head_file); - blob->length = length; - blob->dev = dev; - - if (data) - memcpy(blob->data, data, length); - - ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB, - true, drm_property_free_blob); - if (ret) { - kfree(blob); - return ERR_PTR(-EINVAL); - } - - mutex_lock(&dev->mode_config.blob_lock); - list_add_tail(&blob->head_global, - &dev->mode_config.property_blob_list); - mutex_unlock(&dev->mode_config.blob_lock); - - return blob; -} -EXPORT_SYMBOL(drm_property_create_blob); - -/** - * drm_property_unreference_blob - Unreference a blob property - * - * Drop a reference on a blob property. May free the object. - * - * @blob: Pointer to blob property - */ -void drm_property_unreference_blob(struct drm_property_blob *blob) -{ - if (!blob) - return; - - drm_mode_object_unreference(&blob->base); -} -EXPORT_SYMBOL(drm_property_unreference_blob); - -/** - * drm_property_destroy_user_blobs - destroy all blobs created by this client - * @dev: DRM device - * @file_priv: destroy all blobs owned by this file handle - */ -void drm_property_destroy_user_blobs(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_property_blob *blob, *bt; - - /* - * When the file gets released that means no one else can access the - * blob list any more, so no need to grab dev->blob_lock. - */ - list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { - list_del_init(&blob->head_file); - drm_property_unreference_blob(blob); - } -} - -/** - * drm_property_reference_blob - Take a reference on an existing property - * - * Take a new reference on an existing blob property. - * - * @blob: Pointer to blob property - */ -struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) -{ - drm_mode_object_reference(&blob->base); - return blob; -} -EXPORT_SYMBOL(drm_property_reference_blob); - -/** - * drm_property_lookup_blob - look up a blob property and take a reference - * @dev: drm device - * @id: id of the blob property - * - * If successful, this takes an additional reference to the blob property. - * callers need to make sure to eventually unreference the returned property - * again, using @drm_property_unreference_blob. - */ -struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *obj; - struct drm_property_blob *blob = NULL; - - obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); - if (obj) - blob = obj_to_blob(obj); - return blob; -} -EXPORT_SYMBOL(drm_property_lookup_blob); - -/** - * drm_property_replace_global_blob - atomically replace existing blob property - * @dev: drm device - * @replace: location of blob property pointer to be replaced - * @length: length of data for new blob, or 0 for no data - * @data: content for new blob, or NULL for no data - * @obj_holds_id: optional object for property holding blob ID - * @prop_holds_id: optional property holding blob ID - * @return 0 on success or error on failure - * - * This function will atomically replace a global property in the blob list, - * optionally updating a property which holds the ID of that property. It is - * guaranteed to be atomic: no caller will be allowed to see intermediate - * results, and either the entire operation will succeed and clean up the - * previous property, or it will fail and the state will be unchanged. - * - * If length is 0 or data is NULL, no new blob will be created, and the holding - * property, if specified, will be set to 0. - * - * Access to the replace pointer is assumed to be protected by the caller, e.g. - * by holding the relevant modesetting object lock for its parent. - * - * For example, a drm_connector has a 'PATH' property, which contains the ID - * of a blob property with the value of the MST path information. Calling this - * function with replace pointing to the connector's path_blob_ptr, length and - * data set for the new path information, obj_holds_id set to the connector's - * base object, and prop_holds_id set to the path property name, will perform - * a completely atomic update. The access to path_blob_ptr is protected by the - * caller holding a lock on the connector. - */ -int drm_property_replace_global_blob(struct drm_device *dev, - struct drm_property_blob **replace, - size_t length, - const void *data, - struct drm_mode_object *obj_holds_id, - struct drm_property *prop_holds_id) -{ - struct drm_property_blob *new_blob = NULL; - struct drm_property_blob *old_blob = NULL; - int ret; - - WARN_ON(replace == NULL); - - old_blob = *replace; - - if (length && data) { - new_blob = drm_property_create_blob(dev, length, data); - if (IS_ERR(new_blob)) - return PTR_ERR(new_blob); - } - - /* This does not need to be synchronised with blob_lock, as the - * get_properties ioctl locks all modesetting objects, and - * obj_holds_id must be locked before calling here, so we cannot - * have its value out of sync with the list membership modified - * below under blob_lock. */ - if (obj_holds_id) { - ret = drm_object_property_set_value(obj_holds_id, - prop_holds_id, - new_blob ? - new_blob->base.id : 0); - if (ret != 0) - goto err_created; - } - - drm_property_unreference_blob(old_blob); - *replace = new_blob; - - return 0; - -err_created: - drm_property_unreference_blob(new_blob); - return ret; -} -EXPORT_SYMBOL(drm_property_replace_global_blob); - -/** - * drm_mode_getblob_ioctl - get the contents of a blob property value - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the contents of a blob property. The value stored in - * an object's blob property is just a normal modeset object id. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_blob *out_resp = data; - struct drm_property_blob *blob; - int ret = 0; - void __user *blob_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) - return -ENOENT; - - if (out_resp->length == blob->length) { - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_to_user(blob_ptr, blob->data, blob->length)) { - ret = -EFAULT; - goto unref; - } - } - out_resp->length = blob->length; -unref: - drm_property_unreference_blob(blob); - - return ret; -} - -/** - * drm_mode_createblob_ioctl - create a new blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function creates a new blob property with user-defined values. In order - * to give us sensible validation and checking when creating, rather than at - * every potential use, we also require a type to be provided upfront. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_createblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_create_blob *out_resp = data; - struct drm_property_blob *blob; - void __user *blob_ptr; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_create_blob(dev, out_resp->length, NULL); - if (IS_ERR(blob)) - return PTR_ERR(blob); - - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { - ret = -EFAULT; - goto out_blob; - } - - /* Dropping the lock between create_blob and our access here is safe - * as only the same file_priv can remove the blob; at this point, it is - * not associated with any file_priv. */ - mutex_lock(&dev->mode_config.blob_lock); - out_resp->blob_id = blob->base.id; - list_add_tail(&blob->head_file, &file_priv->blobs); - mutex_unlock(&dev->mode_config.blob_lock); - - return 0; - -out_blob: - drm_property_unreference_blob(blob); - return ret; -} - -/** - * drm_mode_destroyblob_ioctl - destroy a user blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Destroy an existing user-defined blob property. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_destroyblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_destroy_blob *out_resp = data; - struct drm_property_blob *blob = NULL, *bt; - bool found = false; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) - return -ENOENT; - - mutex_lock(&dev->mode_config.blob_lock); - /* Ensure the property was actually created by this user. */ - list_for_each_entry(bt, &file_priv->blobs, head_file) { - if (bt == blob) { - found = true; - break; - } - } - - if (!found) { - ret = -EPERM; - goto err; - } - - /* We must drop head_file here, because we may not be the last - * reference on the blob. */ - list_del_init(&blob->head_file); - mutex_unlock(&dev->mode_config.blob_lock); - - /* One reference from lookup, and one from the filp. */ - drm_property_unreference_blob(blob); - drm_property_unreference_blob(blob); - - return 0; - -err: - mutex_unlock(&dev->mode_config.blob_lock); - drm_property_unreference_blob(blob); - - return ret; -} - -/* Some properties could refer to dynamic refcnt'd objects, or things that - * need special locking to handle lifetime issues (ie. to ensure the prop - * value doesn't become invalid part way through the property update due to - * race). The value returned by reference via 'obj' should be passed back - * to drm_property_change_valid_put() after the property is set (and the - * object to which the property is attached has a chance to take it's own - * reference). - */ -bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, struct drm_mode_object **ref) -{ - int i; - - if (property->flags & DRM_MODE_PROP_IMMUTABLE) - return false; - - *ref = NULL; - - if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { - if (value < property->values[0] || value > property->values[1]) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { - int64_t svalue = U642I64(value); - - if (svalue < U642I64(property->values[0]) || - svalue > U642I64(property->values[1])) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - uint64_t valid_mask = 0; - - for (i = 0; i < property->num_values; i++) - valid_mask |= (1ULL << property->values[i]); - return !(value & ~valid_mask); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - struct drm_property_blob *blob; - - if (value == 0) - return true; - - blob = drm_property_lookup_blob(property->dev, value); - if (blob) { - *ref = &blob->base; - return true; - } else { - return false; - } - } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - /* a zero value for an object property translates to null: */ - if (value == 0) - return true; - - *ref = __drm_mode_object_find(property->dev, value, - property->values[0]); - return *ref != NULL; - } - - for (i = 0; i < property->num_values; i++) - if (property->values[i] == value) - return true; - return false; -} - -void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref) -{ - if (!ref) - return; - - if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - drm_mode_object_unreference(ref); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - drm_property_unreference_blob(obj_to_blob(ref)); -} - int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 2eb1d941ea70..a3622644bccf 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -36,11 +36,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value); -bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, - struct drm_mode_object **ref); -void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref); int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format); int drm_crtc_check_viewport(const struct drm_crtc *crtc, @@ -49,8 +44,6 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, const struct drm_framebuffer *fb); void drm_fb_release(struct drm_file *file_priv); -void drm_property_destroy_user_blobs(struct drm_device *dev, - struct drm_file *file_priv); /* dumb buffer support IOCTLs */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, @@ -77,6 +70,24 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +/* drm_property.c */ +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv); +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, + struct drm_mode_object **ref); +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref); + +/* IOCTL */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_getblob_ioctl(struct drm_device *dev, @@ -85,13 +96,6 @@ int drm_mode_createblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_destroyblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_gamma_get_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_gamma_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); - -int drm_mode_page_flip_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); /* drm_mode_object.c */ int drm_mode_object_get_reg(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c new file mode 100644 index 000000000000..162cc9032ae5 --- /dev/null +++ b/drivers/gpu/drm/drm_property.c @@ -0,0 +1,953 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "drm_crtc_internal.h" + +static bool drm_property_type_valid(struct drm_property *property) +{ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); + return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); +} + +/** + * drm_property_create - create a new property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Note that the DRM core keeps a per-device list of properties and that, if + * drm_mode_config_cleanup() is called, it will destroy all properties created + * by the driver. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values) +{ + struct drm_property *property = NULL; + int ret; + + property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); + if (!property) + return NULL; + + property->dev = dev; + + if (num_values) { + property->values = kcalloc(num_values, sizeof(uint64_t), + GFP_KERNEL); + if (!property->values) + goto fail; + } + + ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); + if (ret) + goto fail; + + property->flags = flags; + property->num_values = num_values; + INIT_LIST_HEAD(&property->enum_list); + + if (name) { + strncpy(property->name, name, DRM_PROP_NAME_LEN); + property->name[DRM_PROP_NAME_LEN-1] = '\0'; + } + + list_add_tail(&property->head, &dev->mode_config.property_list); + + WARN_ON(!drm_property_type_valid(property)); + + return property; +fail: + kfree(property->values); + kfree(property); + return NULL; +} +EXPORT_SYMBOL(drm_property_create); + +/** + * drm_property_create_enum - create a new enumeration property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property values + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is only allowed to set one of the predefined values for enumeration + * properties. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, + const char *name, + const struct drm_prop_enum_list *props, + int num_values) +{ + struct drm_property *property; + int i, ret; + + flags |= DRM_MODE_PROP_ENUM; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + + for (i = 0; i < num_values; i++) { + ret = drm_property_add_enum(property, i, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_enum); + +/** + * drm_property_create_bitmask - create a new bitmask property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property bitflags + * @num_props: size of the @props array + * @supported_bits: bitmask of all supported enumeration values + * + * This creates a new bitmask drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Compared to plain enumeration properties userspace is allowed to set any + * or'ed together combination of the predefined property bitflag values + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_props, + uint64_t supported_bits) +{ + struct drm_property *property; + int i, ret, index = 0; + int num_values = hweight64(supported_bits); + + flags |= DRM_MODE_PROP_BITMASK; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + for (i = 0; i < num_props; i++) { + if (!(supported_bits & (1ULL << props[i].type))) + continue; + + if (WARN_ON(index >= num_values)) { + drm_property_destroy(dev, property); + return NULL; + } + + ret = drm_property_add_enum(property, index++, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_bitmask); + +static struct drm_property *property_create_range(struct drm_device *dev, + int flags, const char *name, + uint64_t min, uint64_t max) +{ + struct drm_property *property; + + property = drm_property_create(dev, flags, name, 2); + if (!property) + return NULL; + + property->values[0] = min; + property->values[1] = max; + + return property; +} + +/** + * drm_property_create_range - create a new unsigned ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is allowed to set any unsigned integer value in the (min, max) + * range inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, + const char *name, + uint64_t min, uint64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, + name, min, max); +} +EXPORT_SYMBOL(drm_property_create_range); + +/** + * drm_property_create_signed_range - create a new signed ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is allowed to set any signed integer value in the (min, max) + * range inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, + name, I642U64(min), I642U64(max)); +} +EXPORT_SYMBOL(drm_property_create_signed_range); + +/** + * drm_property_create_object - create a new object property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @type: object type from DRM_MODE_OBJECT_* defines + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is only allowed to set this to any property value of the given + * @type. Only useful for atomic properties, which is enforced. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, uint32_t type) +{ + struct drm_property *property; + + flags |= DRM_MODE_PROP_OBJECT; + + if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) + return NULL; + + property = drm_property_create(dev, flags, name, 1); + if (!property) + return NULL; + + property->values[0] = type; + + return property; +} +EXPORT_SYMBOL(drm_property_create_object); + +/** + * drm_property_create_bool - create a new boolean property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * This is implemented as a ranged property with only {0, 1} as valid values. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, + const char *name) +{ + return drm_property_create_range(dev, flags, name, 0, 1); +} +EXPORT_SYMBOL(drm_property_create_bool); + +/** + * drm_property_add_enum - add a possible value to an enumeration property + * @property: enumeration property to change + * @index: index of the new enumeration + * @value: value of the new enumeration + * @name: symbolic name of the new enumeration + * + * This functions adds enumerations to a property. + * + * It's use is deprecated, drivers should use one of the more specific helpers + * to directly create the property with all enumerations already attached. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name) +{ + struct drm_property_enum *prop_enum; + + if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) + return -EINVAL; + + /* + * Bitmask enum properties have the additional constraint of values + * from 0 to 63 + */ + if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && + (value > 63)) + return -EINVAL; + + if (!list_empty(&property->enum_list)) { + list_for_each_entry(prop_enum, &property->enum_list, head) { + if (prop_enum->value == value) { + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + return 0; + } + } + } + + prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); + if (!prop_enum) + return -ENOMEM; + + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + prop_enum->value = value; + + property->values[index] = value; + list_add_tail(&prop_enum->head, &property->enum_list); + return 0; +} +EXPORT_SYMBOL(drm_property_add_enum); + +/** + * drm_property_destroy - destroy a drm property + * @dev: drm device + * @property: property to destry + * + * This function frees a property including any attached resources like + * enumeration values. + */ +void drm_property_destroy(struct drm_device *dev, struct drm_property *property) +{ + struct drm_property_enum *prop_enum, *pt; + + list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { + list_del(&prop_enum->head); + kfree(prop_enum); + } + + if (property->num_values) + kfree(property->values); + drm_mode_object_unregister(dev, &property->base); + list_del(&property->head); + kfree(property); +} +EXPORT_SYMBOL(drm_property_destroy); + +/** + * drm_mode_getproperty_ioctl - get the property metadata + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the metadata for a given property, like the different + * possible values for an enum property or the limits for a range property. + * + * Blob properties are special + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_property *out_resp = data; + struct drm_property *property; + int enum_count = 0; + int value_count = 0; + int ret = 0, i; + int copied; + struct drm_property_enum *prop_enum; + struct drm_mode_property_enum __user *enum_ptr; + uint64_t __user *values_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + property = drm_property_find(dev, out_resp->prop_id); + if (!property) { + ret = -ENOENT; + goto done; + } + + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + list_for_each_entry(prop_enum, &property->enum_list, head) + enum_count++; + } + + value_count = property->num_values; + + strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); + out_resp->name[DRM_PROP_NAME_LEN-1] = 0; + out_resp->flags = property->flags; + + if ((out_resp->count_values >= value_count) && value_count) { + values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; + for (i = 0; i < value_count; i++) { + if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + } + } + out_resp->count_values = value_count; + + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { + copied = 0; + enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; + list_for_each_entry(prop_enum, &property->enum_list, head) { + + if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(&enum_ptr[copied].name, + &prop_enum->name, DRM_PROP_NAME_LEN)) { + ret = -EFAULT; + goto done; + } + copied++; + } + } + out_resp->count_enum_blobs = enum_count; + } + + /* + * NOTE: The idea seems to have been to use this to read all the blob + * property values. But nothing ever added them to the corresponding + * list, userspace always used the special-purpose get_blob ioctl to + * read the value for a blob property. It also doesn't make a lot of + * sense to return values here when everything else is just metadata for + * the property itself. + */ + if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + out_resp->count_enum_blobs = 0; +done: + drm_modeset_unlock_all(dev); + return ret; +} + +static void drm_property_free_blob(struct kref *kref) +{ + struct drm_property_blob *blob = + container_of(kref, struct drm_property_blob, base.refcount); + + mutex_lock(&blob->dev->mode_config.blob_lock); + list_del(&blob->head_global); + mutex_unlock(&blob->dev->mode_config.blob_lock); + + drm_mode_object_unregister(blob->dev, &blob->base); + + kfree(blob); +} + +/** + * drm_property_create_blob - Create new blob property + * + * Creates a new blob property for a specified DRM device, optionally + * copying data. + * + * @dev: DRM device to create property for + * @length: Length to allocate for blob data + * @data: If specified, copies data into blob + * + * Returns: + * New blob property with a single reference on success, or an ERR_PTR + * value on failure. + */ +struct drm_property_blob * +drm_property_create_blob(struct drm_device *dev, size_t length, + const void *data) +{ + struct drm_property_blob *blob; + int ret; + + if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) + return ERR_PTR(-EINVAL); + + blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); + if (!blob) + return ERR_PTR(-ENOMEM); + + /* This must be explicitly initialised, so we can safely call list_del + * on it in the removal handler, even if it isn't in a file list. */ + INIT_LIST_HEAD(&blob->head_file); + blob->length = length; + blob->dev = dev; + + if (data) + memcpy(blob->data, data, length); + + ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB, + true, drm_property_free_blob); + if (ret) { + kfree(blob); + return ERR_PTR(-EINVAL); + } + + mutex_lock(&dev->mode_config.blob_lock); + list_add_tail(&blob->head_global, + &dev->mode_config.property_blob_list); + mutex_unlock(&dev->mode_config.blob_lock); + + return blob; +} +EXPORT_SYMBOL(drm_property_create_blob); + +/** + * drm_property_unreference_blob - Unreference a blob property + * + * Drop a reference on a blob property. May free the object. + * + * @blob: Pointer to blob property + */ +void drm_property_unreference_blob(struct drm_property_blob *blob) +{ + if (!blob) + return; + + drm_mode_object_unreference(&blob->base); +} +EXPORT_SYMBOL(drm_property_unreference_blob); + +/** + * drm_property_destroy_user_blobs - destroy all blobs created by this client + * @dev: DRM device + * @file_priv: destroy all blobs owned by this file handle + */ +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct drm_property_blob *blob, *bt; + + /* + * When the file gets released that means no one else can access the + * blob list any more, so no need to grab dev->blob_lock. + */ + list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { + list_del_init(&blob->head_file); + drm_property_unreference_blob(blob); + } +} + +/** + * drm_property_reference_blob - Take a reference on an existing property + * + * Take a new reference on an existing blob property. + * + * @blob: Pointer to blob property + */ +struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) +{ + drm_mode_object_reference(&blob->base); + return blob; +} +EXPORT_SYMBOL(drm_property_reference_blob); + +/** + * drm_property_lookup_blob - look up a blob property and take a reference + * @dev: drm device + * @id: id of the blob property + * + * If successful, this takes an additional reference to the blob property. + * callers need to make sure to eventually unreference the returned property + * again, using @drm_property_unreference_blob. + */ +struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj; + struct drm_property_blob *blob = NULL; + + obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); + if (obj) + blob = obj_to_blob(obj); + return blob; +} +EXPORT_SYMBOL(drm_property_lookup_blob); + +/** + * drm_property_replace_global_blob - atomically replace existing blob property + * @dev: drm device + * @replace: location of blob property pointer to be replaced + * @length: length of data for new blob, or 0 for no data + * @data: content for new blob, or NULL for no data + * @obj_holds_id: optional object for property holding blob ID + * @prop_holds_id: optional property holding blob ID + * @return 0 on success or error on failure + * + * This function will atomically replace a global property in the blob list, + * optionally updating a property which holds the ID of that property. It is + * guaranteed to be atomic: no caller will be allowed to see intermediate + * results, and either the entire operation will succeed and clean up the + * previous property, or it will fail and the state will be unchanged. + * + * If length is 0 or data is NULL, no new blob will be created, and the holding + * property, if specified, will be set to 0. + * + * Access to the replace pointer is assumed to be protected by the caller, e.g. + * by holding the relevant modesetting object lock for its parent. + * + * For example, a drm_connector has a 'PATH' property, which contains the ID + * of a blob property with the value of the MST path information. Calling this + * function with replace pointing to the connector's path_blob_ptr, length and + * data set for the new path information, obj_holds_id set to the connector's + * base object, and prop_holds_id set to the path property name, will perform + * a completely atomic update. The access to path_blob_ptr is protected by the + * caller holding a lock on the connector. + */ +int drm_property_replace_global_blob(struct drm_device *dev, + struct drm_property_blob **replace, + size_t length, + const void *data, + struct drm_mode_object *obj_holds_id, + struct drm_property *prop_holds_id) +{ + struct drm_property_blob *new_blob = NULL; + struct drm_property_blob *old_blob = NULL; + int ret; + + WARN_ON(replace == NULL); + + old_blob = *replace; + + if (length && data) { + new_blob = drm_property_create_blob(dev, length, data); + if (IS_ERR(new_blob)) + return PTR_ERR(new_blob); + } + + /* This does not need to be synchronised with blob_lock, as the + * get_properties ioctl locks all modesetting objects, and + * obj_holds_id must be locked before calling here, so we cannot + * have its value out of sync with the list membership modified + * below under blob_lock. */ + if (obj_holds_id) { + ret = drm_object_property_set_value(obj_holds_id, + prop_holds_id, + new_blob ? + new_blob->base.id : 0); + if (ret != 0) + goto err_created; + } + + drm_property_unreference_blob(old_blob); + *replace = new_blob; + + return 0; + +err_created: + drm_property_unreference_blob(new_blob); + return ret; +} +EXPORT_SYMBOL(drm_property_replace_global_blob); + +/** + * drm_mode_getblob_ioctl - get the contents of a blob property value + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the contents of a blob property. The value stored in + * an object's blob property is just a normal modeset object id. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_blob *out_resp = data; + struct drm_property_blob *blob; + int ret = 0; + void __user *blob_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_lookup_blob(dev, out_resp->blob_id); + if (!blob) + return -ENOENT; + + if (out_resp->length == blob->length) { + blob_ptr = (void __user *)(unsigned long)out_resp->data; + if (copy_to_user(blob_ptr, blob->data, blob->length)) { + ret = -EFAULT; + goto unref; + } + } + out_resp->length = blob->length; +unref: + drm_property_unreference_blob(blob); + + return ret; +} + +/** + * drm_mode_createblob_ioctl - create a new blob property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function creates a new blob property with user-defined values. In order + * to give us sensible validation and checking when creating, rather than at + * every potential use, we also require a type to be provided upfront. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_createblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_create_blob *out_resp = data; + struct drm_property_blob *blob; + void __user *blob_ptr; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_create_blob(dev, out_resp->length, NULL); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + blob_ptr = (void __user *)(unsigned long)out_resp->data; + if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { + ret = -EFAULT; + goto out_blob; + } + + /* Dropping the lock between create_blob and our access here is safe + * as only the same file_priv can remove the blob; at this point, it is + * not associated with any file_priv. */ + mutex_lock(&dev->mode_config.blob_lock); + out_resp->blob_id = blob->base.id; + list_add_tail(&blob->head_file, &file_priv->blobs); + mutex_unlock(&dev->mode_config.blob_lock); + + return 0; + +out_blob: + drm_property_unreference_blob(blob); + return ret; +} + +/** + * drm_mode_destroyblob_ioctl - destroy a user blob property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Destroy an existing user-defined blob property. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_destroyblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_destroy_blob *out_resp = data; + struct drm_property_blob *blob = NULL, *bt; + bool found = false; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_lookup_blob(dev, out_resp->blob_id); + if (!blob) + return -ENOENT; + + mutex_lock(&dev->mode_config.blob_lock); + /* Ensure the property was actually created by this user. */ + list_for_each_entry(bt, &file_priv->blobs, head_file) { + if (bt == blob) { + found = true; + break; + } + } + + if (!found) { + ret = -EPERM; + goto err; + } + + /* We must drop head_file here, because we may not be the last + * reference on the blob. */ + list_del_init(&blob->head_file); + mutex_unlock(&dev->mode_config.blob_lock); + + /* One reference from lookup, and one from the filp. */ + drm_property_unreference_blob(blob); + drm_property_unreference_blob(blob); + + return 0; + +err: + mutex_unlock(&dev->mode_config.blob_lock); + drm_property_unreference_blob(blob); + + return ret; +} + +/* Some properties could refer to dynamic refcnt'd objects, or things that + * need special locking to handle lifetime issues (ie. to ensure the prop + * value doesn't become invalid part way through the property update due to + * race). The value returned by reference via 'obj' should be passed back + * to drm_property_change_valid_put() after the property is set (and the + * object to which the property is attached has a chance to take it's own + * reference). + */ +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, struct drm_mode_object **ref) +{ + int i; + + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + return false; + + *ref = NULL; + + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { + if (value < property->values[0] || value > property->values[1]) + return false; + return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { + int64_t svalue = U642I64(value); + + if (svalue < U642I64(property->values[0]) || + svalue > U642I64(property->values[1])) + return false; + return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + uint64_t valid_mask = 0; + + for (i = 0; i < property->num_values; i++) + valid_mask |= (1ULL << property->values[i]); + return !(value & ~valid_mask); + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { + struct drm_property_blob *blob; + + if (value == 0) + return true; + + blob = drm_property_lookup_blob(property->dev, value); + if (blob) { + *ref = &blob->base; + return true; + } else { + return false; + } + } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + /* a zero value for an object property translates to null: */ + if (value == 0) + return true; + + *ref = __drm_mode_object_find(property->dev, value, + property->values[0]); + return *ref != NULL; + } + + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; +} + +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref) +{ + if (!ref) + return; + + if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + drm_mode_object_unreference(ref); + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + drm_property_unreference_blob(obj_to_blob(ref)); +} diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 63f03490e987..3d33c90da9e5 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,6 +41,7 @@ #include #include #include +#include struct drm_device; struct drm_mode_set; @@ -82,33 +83,6 @@ struct drm_tile_group { u8 group_data[8]; }; -struct drm_property_blob { - struct drm_mode_object base; - struct drm_device *dev; - struct list_head head_global; - struct list_head head_file; - size_t length; - unsigned char data[]; -}; - -struct drm_property_enum { - uint64_t value; - struct list_head head; - char name[DRM_PROP_NAME_LEN]; -}; - -struct drm_property { - struct list_head head; - struct drm_mode_object base; - uint32_t flags; - char name[DRM_PROP_NAME_LEN]; - uint32_t num_values; - uint64_t *values; - struct drm_device *dev; - - struct list_head enum_list; -}; - struct drm_crtc; struct drm_encoder; struct drm_pending_vblank_event; @@ -2026,15 +2000,9 @@ struct drm_mode_config { #define obj_to_crtc(x) container_of(x, struct drm_crtc, base) #define obj_to_mode(x) container_of(x, struct drm_display_mode, base) #define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) -#define obj_to_property(x) container_of(x, struct drm_property, base) #define obj_to_blob(x) container_of(x, struct drm_property_blob, base) #define obj_to_plane(x) container_of(x, struct drm_plane, base) -struct drm_prop_enum_list { - int type; - char *name; -}; - extern __printf(6, 7) int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, @@ -2107,52 +2075,6 @@ extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); -static inline bool drm_property_type_is(struct drm_property *property, - uint32_t type) -{ - /* instanceof for props.. handles extended type vs original types: */ - if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) - return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; - return property->flags & type; -} - -extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, - const char *name, int num_values); -extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, - const char *name, - const struct drm_prop_enum_list *props, - int num_values); -struct drm_property *drm_property_create_bitmask(struct drm_device *dev, - int flags, const char *name, - const struct drm_prop_enum_list *props, - int num_props, - uint64_t supported_bits); -struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, - const char *name, - uint64_t min, uint64_t max); -struct drm_property *drm_property_create_signed_range(struct drm_device *dev, - int flags, const char *name, - int64_t min, int64_t max); -struct drm_property *drm_property_create_object(struct drm_device *dev, - int flags, const char *name, uint32_t type); -struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, - const char *name); -struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, - size_t length, - const void *data); -struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, - uint32_t id); -int drm_property_replace_global_blob(struct drm_device *dev, - struct drm_property_blob **replace, - size_t length, - const void *data, - struct drm_mode_object *obj_holds_id, - struct drm_property *prop_holds_id); -struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob); -void drm_property_unreference_blob(struct drm_property_blob *blob); -extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); -extern int drm_property_add_enum(struct drm_property *property, int index, - uint64_t value, const char *name); extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size); @@ -2202,14 +2124,6 @@ static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, return mo ? obj_to_crtc(mo) : NULL; } -static inline struct drm_property *drm_property_find(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY); - return mo ? obj_to_property(mo) : NULL; -} - /* * Extract a degamma/gamma LUT value provided by user and round it to the * precision supported by the hardware. diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h new file mode 100644 index 000000000000..ac40069358c7 --- /dev/null +++ b/include/drm/drm_property.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef __DRM_PROPERTY_H__ +#define __DRM_PROPERTY_H__ + +#include +#include +#include + +struct drm_property_blob { + struct drm_mode_object base; + struct drm_device *dev; + struct list_head head_global; + struct list_head head_file; + size_t length; + unsigned char data[]; +}; + +struct drm_property_enum { + uint64_t value; + struct list_head head; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_property { + struct list_head head; + struct drm_mode_object base; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + uint32_t num_values; + uint64_t *values; + struct drm_device *dev; + + struct list_head enum_list; +}; + +struct drm_prop_enum_list { + int type; + char *name; +}; + +#define obj_to_property(x) container_of(x, struct drm_property, base) + +static inline bool drm_property_type_is(struct drm_property *property, + uint32_t type) +{ + /* instanceof for props.. handles extended type vs original types: */ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return property->flags & type; +} + +struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values); +struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, + const char *name, + const struct drm_prop_enum_list *props, + int num_values); +struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_props, + uint64_t supported_bits); +struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, + const char *name, + uint64_t min, uint64_t max); +struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max); +struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, uint32_t type); +struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, + const char *name); +int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name); +void drm_property_destroy(struct drm_device *dev, struct drm_property *property); + +struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, + size_t length, + const void *data); +struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, + uint32_t id); +int drm_property_replace_global_blob(struct drm_device *dev, + struct drm_property_blob **replace, + size_t length, + const void *data, + struct drm_mode_object *obj_holds_id, + struct drm_property *prop_holds_id); +struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob); +void drm_property_unreference_blob(struct drm_property_blob *blob); + +static inline struct drm_property *drm_property_find(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY); + return mo ? obj_to_property(mo) : NULL; +} + +#endif From 77953bd136d2a70bca2dc93b3ccda07a2b37076f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:56 +0200 Subject: [PATCH 21/24] drm: Unify handling of blob and object properties They work exactly the same now, after the refcounting unification a bit ago. The only reason they're distinct is backwards compat with existing userspace. Cc: Daniel Stone Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-8-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_property.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index 162cc9032ae5..b5521f705b1c 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -911,20 +911,8 @@ bool drm_property_change_valid_get(struct drm_property *property, for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - struct drm_property_blob *blob; - - if (value == 0) - return true; - - blob = drm_property_lookup_blob(property->dev, value); - if (blob) { - *ref = &blob->base; - return true; - } else { - return false; - } - } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB) || + drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { /* a zero value for an object property translates to null: */ if (value == 0) return true; @@ -941,13 +929,12 @@ bool drm_property_change_valid_get(struct drm_property *property, } void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref) + struct drm_mode_object *ref) { if (!ref) return; - if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT) || + drm_property_type_is(property, DRM_MODE_PROP_BLOB)) drm_mode_object_unreference(ref); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - drm_property_unreference_blob(obj_to_blob(ref)); } From c8458c7efd01f5748e9ca74720e366d3fcc5fa97 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Aug 2016 10:27:57 +0200 Subject: [PATCH 22/24] drm/doc: Polish docs for drm_property&drm_property_blob - remove kerneldoc for drm-internal functions - drm_property_replace_global_blob isn't actually atomic, and doesn't need to be. Update docs&comments to match - document all the types and try to link things a bit better - nits all over v2: Appease checkpatch in the moved code (Archit) Reviewed-by: Archit Taneja Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160829082757.17913-9-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 88 +------------- drivers/gpu/drm/drm_property.c | 153 +++++++++-------------- include/drm/drm_property.h | 214 ++++++++++++++++++++++++++++++--- 3 files changed, 253 insertions(+), 202 deletions(-) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index e07a2667ab61..f9a991bb87d4 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -304,94 +304,12 @@ KMS Locking KMS Properties ============== -Drivers may need to expose additional parameters to applications than -those described in the previous sections. KMS supports attaching -properties to CRTCs, connectors and planes and offers a userspace API to -list, get and set the property values. - -Properties are identified by a name that uniquely defines the property -purpose, and store an associated value. For all property types except -blob properties the value is a 64-bit unsigned integer. - -KMS differentiates between properties and property instances. Drivers -first create properties and then create and associate individual -instances of those properties to objects. A property can be instantiated -multiple times and associated with different objects. Values are stored -in property instances, and all other property information are stored in -the property and shared between all instances of the property. - -Every property is created with a type that influences how the KMS core -handles the property. Supported property types are - -DRM_MODE_PROP_RANGE - Range properties report their minimum and maximum admissible values. - The KMS core verifies that values set by application fit in that - range. - -DRM_MODE_PROP_ENUM - Enumerated properties take a numerical value that ranges from 0 to - the number of enumerated values defined by the property minus one, - and associate a free-formed string name to each value. Applications - can retrieve the list of defined value-name pairs and use the - numerical value to get and set property instance values. - -DRM_MODE_PROP_BITMASK - Bitmask properties are enumeration properties that additionally - restrict all enumerated values to the 0..63 range. Bitmask property - instance values combine one or more of the enumerated bits defined - by the property. - -DRM_MODE_PROP_BLOB - Blob properties store a binary blob without any format restriction. - The binary blobs are created as KMS standalone objects, and blob - property instance values store the ID of their associated blob - object. - - Blob properties are only used for the connector EDID property and - cannot be created by drivers. - -To create a property drivers call one of the following functions -depending on the property type. All property creation functions take -property flags and name, as well as type-specific arguments. - -- struct drm_property \*drm_property_create_range(struct - drm_device \*dev, int flags, const char \*name, uint64_t min, - uint64_t max); - Create a range property with the given minimum and maximum values. - -- struct drm_property \*drm_property_create_enum(struct drm_device - \*dev, int flags, const char \*name, const struct - drm_prop_enum_list \*props, int num_values); - Create an enumerated property. The ``props`` argument points to an - array of ``num_values`` value-name pairs. - -- struct drm_property \*drm_property_create_bitmask(struct - drm_device \*dev, int flags, const char \*name, const struct - drm_prop_enum_list \*props, int num_values); - Create a bitmask property. The ``props`` argument points to an array - of ``num_values`` value-name pairs. - -Properties can additionally be created as immutable, in which case they -will be read-only for applications but can be modified by the driver. To -create an immutable property drivers must set the -DRM_MODE_PROP_IMMUTABLE flag at property creation time. - -When no array of value-name pairs is readily available at property -creation time for enumerated or range properties, drivers can create the -property using the :c:func:`drm_property_create()` function and -manually add enumeration value-name pairs by calling the -:c:func:`drm_property_add_enum()` function. Care must be taken to -properly specify the property type through the ``flags`` argument. - -After creating properties drivers can attach property instances to CRTC, -connector and plane objects by calling the -:c:func:`drm_object_attach_property()`. The function takes a -pointer to the target object, a pointer to the previously created -property and an initial instance value. - Property Types and Blob Property Support ---------------------------------------- +.. kernel-doc:: drivers/gpu/drm/drm_property.c + :doc: overview + .. kernel-doc:: include/drm/drm_property.h :internal: diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index b5521f705b1c..4139afbcc267 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -26,6 +26,30 @@ #include "drm_crtc_internal.h" +/** + * DOC: overview + * + * Properties as represented by &drm_property are used to extend the modeset + * interface exposed to userspace. For the atomic modeset IOCTL properties are + * even the only way to transport metadata about the desired new modeset + * configuration from userspace to the kernel. Properties have a well-defined + * value range, which is enforced by the drm core. See the documentation of the + * flags member of struct &drm_property for an overview of the different + * property types and ranges. + * + * Properties don't store the current value directly, but need to be + * instatiated by attaching them to a &drm_mode_object with + * drm_object_attach_property(). + * + * Property values are only 64bit. To support bigger piles of data (like gamma + * tables, color correction matrizes or large structures) a property can instead + * point at a &drm_property_blob with that additional data + * + * Properties are defined by their symbolic name, userspace must keep a + * per-object mapping from those names to the property ID used in the atomic + * IOCTL and in the get/set property IOCTL. + */ + static bool drm_property_type_valid(struct drm_property *property) { if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) @@ -42,11 +66,8 @@ static bool drm_property_type_valid(struct drm_property *property) * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Note that the DRM core keeps a per-device list of properties and that, if - * drm_mode_config_cleanup() is called, it will destroy all properties created - * by the driver. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Returns: * A pointer to the newly created property on success, NULL on failure. @@ -105,7 +126,8 @@ EXPORT_SYMBOL(drm_property_create); * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Userspace is only allowed to set one of the predefined values for enumeration * properties. @@ -152,7 +174,8 @@ EXPORT_SYMBOL(drm_property_create_enum); * * This creates a new bitmask drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Compared to plain enumeration properties userspace is allowed to set any * or'ed together combination of the predefined property bitflag values @@ -223,7 +246,8 @@ static struct drm_property *property_create_range(struct drm_device *dev, * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Userspace is allowed to set any unsigned integer value in the (min, max) * range inclusive. @@ -250,7 +274,8 @@ EXPORT_SYMBOL(drm_property_create_range); * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Userspace is allowed to set any signed integer value in the (min, max) * range inclusive. @@ -276,7 +301,8 @@ EXPORT_SYMBOL(drm_property_create_signed_range); * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * Userspace is only allowed to set this to any property value of the given * @type. Only useful for atomic properties, which is enforced. @@ -285,7 +311,8 @@ EXPORT_SYMBOL(drm_property_create_signed_range); * A pointer to the newly created property on success, NULL on failure. */ struct drm_property *drm_property_create_object(struct drm_device *dev, - int flags, const char *name, uint32_t type) + int flags, const char *name, + uint32_t type) { struct drm_property *property; @@ -312,7 +339,8 @@ EXPORT_SYMBOL(drm_property_create_object); * * This creates a new generic drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). * * This is implemented as a ranged property with only {0, 1} as valid values. * @@ -320,7 +348,7 @@ EXPORT_SYMBOL(drm_property_create_object); * A pointer to the newly created property on success, NULL on failure. */ struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, - const char *name) + const char *name) { return drm_property_create_range(dev, flags, name, 0, 1); } @@ -407,22 +435,6 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); -/** - * drm_mode_getproperty_ioctl - get the property metadata - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the metadata for a given property, like the different - * possible values for an enum property or the limits for a range property. - * - * Blob properties are special - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -523,14 +535,14 @@ static void drm_property_free_blob(struct kref *kref) /** * drm_property_create_blob - Create new blob property - * - * Creates a new blob property for a specified DRM device, optionally - * copying data. - * * @dev: DRM device to create property for * @length: Length to allocate for blob data * @data: If specified, copies data into blob * + * Creates a new blob property for a specified DRM device, optionally + * copying data. Note that blob properties are meant to be invariant, hence the + * data must be filled out before the blob is used as the value of any property. + * * Returns: * New blob property with a single reference on success, or an ERR_PTR * value on failure. @@ -576,10 +588,9 @@ EXPORT_SYMBOL(drm_property_create_blob); /** * drm_property_unreference_blob - Unreference a blob property + * @blob: Pointer to blob property * * Drop a reference on a blob property. May free the object. - * - * @blob: Pointer to blob property */ void drm_property_unreference_blob(struct drm_property_blob *blob) { @@ -590,11 +601,6 @@ void drm_property_unreference_blob(struct drm_property_blob *blob) } EXPORT_SYMBOL(drm_property_unreference_blob); -/** - * drm_property_destroy_user_blobs - destroy all blobs created by this client - * @dev: DRM device - * @file_priv: destroy all blobs owned by this file handle - */ void drm_property_destroy_user_blobs(struct drm_device *dev, struct drm_file *file_priv) { @@ -612,10 +618,10 @@ void drm_property_destroy_user_blobs(struct drm_device *dev, /** * drm_property_reference_blob - Take a reference on an existing property - * - * Take a new reference on an existing blob property. - * * @blob: Pointer to blob property + * + * Take a new reference on an existing blob property. Returns @blob, which + * allows this to be used as a shorthand in assignments. */ struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) { @@ -632,6 +638,9 @@ EXPORT_SYMBOL(drm_property_reference_blob); * If successful, this takes an additional reference to the blob property. * callers need to make sure to eventually unreference the returned property * again, using @drm_property_unreference_blob. + * + * Return: + * NULL on failure, pointer to the blob on success. */ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, uint32_t id) @@ -647,7 +656,7 @@ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, EXPORT_SYMBOL(drm_property_lookup_blob); /** - * drm_property_replace_global_blob - atomically replace existing blob property + * drm_property_replace_global_blob - replace existing blob property * @dev: drm device * @replace: location of blob property pointer to be replaced * @length: length of data for new blob, or 0 for no data @@ -656,11 +665,8 @@ EXPORT_SYMBOL(drm_property_lookup_blob); * @prop_holds_id: optional property holding blob ID * @return 0 on success or error on failure * - * This function will atomically replace a global property in the blob list, - * optionally updating a property which holds the ID of that property. It is - * guaranteed to be atomic: no caller will be allowed to see intermediate - * results, and either the entire operation will succeed and clean up the - * previous property, or it will fail and the state will be unchanged. + * This function will replace a global property in the blob list, optionally + * updating a property which holds the ID of that property. * * If length is 0 or data is NULL, no new blob will be created, and the holding * property, if specified, will be set to 0. @@ -697,11 +703,6 @@ int drm_property_replace_global_blob(struct drm_device *dev, return PTR_ERR(new_blob); } - /* This does not need to be synchronised with blob_lock, as the - * get_properties ioctl locks all modesetting objects, and - * obj_holds_id must be locked before calling here, so we cannot - * have its value out of sync with the list membership modified - * below under blob_lock. */ if (obj_holds_id) { ret = drm_object_property_set_value(obj_holds_id, prop_holds_id, @@ -722,20 +723,6 @@ int drm_property_replace_global_blob(struct drm_device *dev, } EXPORT_SYMBOL(drm_property_replace_global_blob); -/** - * drm_mode_getblob_ioctl - get the contents of a blob property value - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the contents of a blob property. The value stored in - * an object's blob property is just a normal modeset object id. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -765,21 +752,6 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return ret; } -/** - * drm_mode_createblob_ioctl - create a new blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function creates a new blob property with user-defined values. In order - * to give us sensible validation and checking when creating, rather than at - * every potential use, we also require a type to be provided upfront. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ int drm_mode_createblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -816,19 +788,6 @@ int drm_mode_createblob_ioctl(struct drm_device *dev, return ret; } -/** - * drm_mode_destroyblob_ioctl - destroy a user blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Destroy an existing user-defined blob property. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ int drm_mode_destroyblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -885,7 +844,7 @@ int drm_mode_destroyblob_ioctl(struct drm_device *dev, * reference). */ bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, struct drm_mode_object **ref) + uint64_t value, struct drm_mode_object **ref) { int i; diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h index ac40069358c7..30ab289be05d 100644 --- a/include/drm/drm_property.h +++ b/include/drm/drm_property.h @@ -27,6 +27,183 @@ #include #include +/** + * struct drm_property_enum - symbolic values for enumerations + * @value: numeric property value for this enum entry + * @head: list of enum values, linked to enum_list in &drm_property + * @name: symbolic name for the enum + * + * For enumeration and bitmask properties this structure stores the symbolic + * decoding for each value. This is used for example for the rotation property. + */ +struct drm_property_enum { + uint64_t value; + struct list_head head; + char name[DRM_PROP_NAME_LEN]; +}; + +/** + * struct drm_property - modeset object property + * + * This structure represent a modeset object property. It combines both the name + * of the property with the set of permissible values. This means that when a + * driver wants to use a property with the same name on different objects, but + * with different value ranges, then it must create property for each one. An + * example would be rotation of &drm_plane, when e.g. the primary plane cannot + * be rotated. But if both the name and the value range match, then the same + * property structure can be instantiated multiple times for the same object. + * Userspace must be able to cope with this and cannot assume that the same + * symbolic property will have the same modeset object ID on all modeset + * objects. + * + * Properties are created by one of the special functions, as explained in + * detail in the @flags structure member. + * + * To actually expose a property it must be attached to each object using + * drm_object_attach_property(). Currently properties can only be attached to + * &drm_connector, &drm_crtc and &drm_plane. + * + * Properties are also used as the generic metadatatransport for the atomic + * IOCTL. Everything that was set directly in structures in the legacy modeset + * IOCTLs (like the plane source or destination windows, or e.g. the links to + * the CRTC) is exposed as a property with the DRM_MODE_PROP_ATOMIC flag set. + */ +struct drm_property { + /** + * @head: per-device list of properties, for cleanup. + */ + struct list_head head; + + /** + * @base: base KMS object + */ + struct drm_mode_object base; + + /** + * @flags: + * + * Property flags and type. A property needs to be one of the following + * types: + * + * DRM_MODE_PROP_RANGE + * Range properties report their minimum and maximum admissible unsigned values. + * The KMS core verifies that values set by application fit in that + * range. The range is unsigned. Range properties are created using + * drm_property_create_range(). + * + * DRM_MODE_PROP_SIGNED_RANGE + * Range properties report their minimum and maximum admissible unsigned values. + * The KMS core verifies that values set by application fit in that + * range. The range is signed. Range properties are created using + * drm_property_create_signed_range(). + * + * DRM_MODE_PROP_ENUM + * Enumerated properties take a numerical value that ranges from 0 to + * the number of enumerated values defined by the property minus one, + * and associate a free-formed string name to each value. Applications + * can retrieve the list of defined value-name pairs and use the + * numerical value to get and set property instance values. Enum + * properties are created using drm_property_create_enum(). + * + * DRM_MODE_PROP_BITMASK + * Bitmask properties are enumeration properties that additionally + * restrict all enumerated values to the 0..63 range. Bitmask property + * instance values combine one or more of the enumerated bits defined + * by the property. Bitmask properties are created using + * drm_property_create_bitmask(). + * + * DRM_MODE_PROB_OBJECT + * Object properties are used to link modeset objects. This is used + * extensively in the atomic support to create the display pipeline, + * by linking &drm_framebuffer to &drm_plane, &drm_plane to + * &drm_crtc and &drm_connector to &drm_crtc. An object property can + * only link to a specific type of &drm_mode_object, this limit is + * enforced by the core. Object properties are created using + * drm_property_create_object(). + * + * Object properties work like blob properties, but in a more + * general fashion. They are limited to atomic drivers and must have + * the DRM_MODE_PROP_ATOMIC flag set. + * + * DRM_MODE_PROP_BLOB + * Blob properties store a binary blob without any format restriction. + * The binary blobs are created as KMS standalone objects, and blob + * property instance values store the ID of their associated blob + * object. Blob properties are created by calling + * drm_property_create() with DRM_MODE_PROP_BLOB as the type. + * + * Actual blob objects to contain blob data are created using + * drm_property_create_blob(), or through the corresponding IOCTL. + * + * Besides the built-in limit to only accept blob objects blob + * properties work exactly like object properties. The only reasons + * blob properties exist is backwards compatibility with existing + * userspace. + * + * In addition a property can have any combination of the below flags: + * + * DRM_MODE_PROP_ATOMIC + * Set for properties which encode atomic modeset state. Such + * properties are not exposed to legacy userspace. + * + * DRM_MODE_PROP_IMMUTABLE + * Set for properties where userspace cannot be changed by + * userspace. The kernel is allowed to update the value of these + * properties. This is generally used to expose probe state to + * usersapce, e.g. the EDID, or the connector path property on DP + * MST sinks. + */ + uint32_t flags; + + /** + * @name: symbolic name of the properties + */ + char name[DRM_PROP_NAME_LEN]; + + /** + * @num_values: size of the @values array. + */ + uint32_t num_values; + + /** + * @values: + * + * Array with limits and values for the property. The + * interpretation of these limits is dependent upon the type per @flags. + */ + uint64_t *values; + + /** + * @dev: DRM device + */ + struct drm_device *dev; + + /** + * @enum_list: + * + * List of &drm_prop_enum_list structures with the symbolic names for + * enum and bitmask values. + */ + struct list_head enum_list; +}; + +/** + * struct drm_property_blob - Blob data for &drm_property + * @base: base KMS object + * @dev: DRM device + * @head_global: entry on the global blob list in &drm_mode_config + * property_blob_list. + * @head_file: entry on the per-file blob list in &drm_file blobs list. + * @length: size of the blob in bytes, invariant over the lifetime of the object + * @data: actual data, embedded at the end of this structure + * + * Blobs are used to store bigger values than what fits directly into the 64 + * bits available for a &drm_property. + * + * Blobs are reference counted using drm_property_reference_blob() and + * drm_property_unreference_blob(). They are created using + * drm_property_create_blob(). + */ struct drm_property_blob { struct drm_mode_object base; struct drm_device *dev; @@ -36,24 +213,6 @@ struct drm_property_blob { unsigned char data[]; }; -struct drm_property_enum { - uint64_t value; - struct list_head head; - char name[DRM_PROP_NAME_LEN]; -}; - -struct drm_property { - struct list_head head; - struct drm_mode_object base; - uint32_t flags; - char name[DRM_PROP_NAME_LEN]; - uint32_t num_values; - uint64_t *values; - struct drm_device *dev; - - struct list_head enum_list; -}; - struct drm_prop_enum_list { int type; char *name; @@ -61,8 +220,16 @@ struct drm_prop_enum_list { #define obj_to_property(x) container_of(x, struct drm_property, base) +/** + * drm_property_type_is - check the type of a property + * @property: property to check + * @type: property type to compare with + * + * This is a helper function becauase the uapi encoding of property types is + * a bit special for historical reasons. + */ static inline bool drm_property_type_is(struct drm_property *property, - uint32_t type) + uint32_t type) { /* instanceof for props.. handles extended type vs original types: */ if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) @@ -109,8 +276,15 @@ int drm_property_replace_global_blob(struct drm_device *dev, struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob); void drm_property_unreference_blob(struct drm_property_blob *blob); +/** + * drm_connector_find - find property object + * @dev: DRM device + * @id: property object id + * + * This function looks up the property object specified by id and returns it. + */ static inline struct drm_property *drm_property_find(struct drm_device *dev, - uint32_t id) + uint32_t id) { struct drm_mode_object *mo; mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY); From b0118e7d936c550c88bcb0e4687020e8d5cb9b54 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Mon, 29 Aug 2016 10:30:51 +0100 Subject: [PATCH 23/24] drm: bridge/dw-hdmi: Fix colorspace and scan information registers values Colorspace and scan information values were being written in wrong offsets. This patch corrects this and writes the values at the offsets specified in the databook. Signed-off-by: Jose Abreu Acked-by: Russell King Cc: Carlos Palminha Cc: Archit Taneja Cc: David Airlie Cc: Russell King Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/96fd0f06b7ebc2ee5cb8239a99648f3107e24497.1471530573.git.joabreu@synopsys.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 77ab47341658..cdf39aa3943c 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -940,10 +940,11 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) */ /* - * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6, - * active aspect present in bit 6 rather than 4. + * AVI data byte 1 differences: Colorspace in bits 0,1 rather than 5,6, + * scan info in bits 4,5 rather than 0,1 and active aspect present in + * bit 6 rather than 4. */ - val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3); + val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3); if (frame.active_aspect & 15) val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT; if (frame.top_bar || frame.bottom_bar) From 339fd36238dd3494bc4617d181e7a37922c29ee9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 Aug 2016 14:09:05 +0300 Subject: [PATCH 24/24] drm: drm_probe_helper: Fix output_poll_work scheduling drm_kms_helper_poll_enable_locked() should check if we have delayed event pending and if we have, schedule the work to run without delay. Currently the output_poll_work is only scheduled if any of the connectors have DRM_CONNECTOR_POLL_CONNECT or DRM_CONNECTOR_POLL_DISCONNECT with DRM_OUTPUT_POLL_PERIOD delay. It does not matter if we have delayed event already registered to be handled. The detection will be delayd by DRM_OUTPUT_POLL_PERIOD in any case. Furthermore if none of the connectors are marked as POLL_CONNECT or POLL_DISCONNECT because all connectors are either POLL_HPD or they are always connected: the output_poll_work will not run at all even if we have delayed event marked. When none of the connectors require polling, their initial status change from unknown to connected/disconnected is not going to be handled until the first kms application starts or if we have fb console enabled. Note that in general the output poll work should be enabled already when this happens, but at driver load usually the first probe happens before the output polling is enabled. This patch fixes this case. Signed-off-by: Peter Ujfalusi [danvet: Note when exactly this is an issue, since the probe code schedules the poll work itself already.] Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20160831110905.31289-1-peter.ujfalusi@ti.com --- drivers/gpu/drm/drm_probe_helper.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index a0df377d7d1c..f6b64d7d3528 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -129,6 +129,7 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev) { bool poll = false; struct drm_connector *connector; + unsigned long delay = DRM_OUTPUT_POLL_PERIOD; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); @@ -141,8 +142,13 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev) poll = true; } + if (dev->mode_config.delayed_event) { + poll = true; + delay = 0; + } + if (poll) - schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); + schedule_delayed_work(&dev->mode_config.output_poll_work, delay); } EXPORT_SYMBOL(drm_kms_helper_poll_enable_locked);