From def23aa7e9821a3dfe3fb7b139dd0229a89fdeb0 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 4 Apr 2017 14:31:57 +0200 Subject: drm: bridge: dw-hdmi: Switch to V4L bus format and encodings Switch code to use the newly introduced V4L bus formats IDs instead of custom defines. Also use the V4L encoding defines. Some display pipelines can only provide non-RBG input pixels to the HDMI TX Controller, this patch takes the pixel format from the plat_data if provided. Reviewed-by: Jose Abreu Reviewed-by: Archit Taneja Reviewed-by: Laurent Pinchart Signed-off-by: Neil Armstrong --- include/drm/bridge/dw_hdmi.h | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'include/drm') diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index bcceee8114a4..5d6b92c6c0bc 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -14,6 +14,67 @@ struct dw_hdmi; +/** + * DOC: Supported input formats and encodings + * + * Depending on the Hardware configuration of the Controller IP, it supports + * a subset of the following input formats and encodings on its internal + * 48bit bus. + * + * +----------------------+----------------------------------+------------------------------+ + * + Format Name + Format Code + Encodings + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 8bit + ``MEDIA_BUS_FMT_RGB888_1X24`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 10bits + ``MEDIA_BUS_FMT_RGB101010_1X30`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 12bits + ``MEDIA_BUS_FMT_RGB121212_1X36`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 16bits + ``MEDIA_BUS_FMT_RGB161616_1X48`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 8bit + ``MEDIA_BUS_FMT_YUV8_1X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 10bits + ``MEDIA_BUS_FMT_YUV10_1X30`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 12bits + ``MEDIA_BUS_FMT_YUV12_1X36`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 16bits + ``MEDIA_BUS_FMT_YUV16_1X48`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 8bit + ``MEDIA_BUS_FMT_UYVY8_1X16`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 10bits + ``MEDIA_BUS_FMT_UYVY10_1X20`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 12bits + ``MEDIA_BUS_FMT_UYVY12_1X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 8bit + ``MEDIA_BUS_FMT_UYYVYY8_0_5X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 10bits + ``MEDIA_BUS_FMT_UYYVYY10_0_5X30``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 12bits + ``MEDIA_BUS_FMT_UYYVYY12_0_5X36``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 16bits + ``MEDIA_BUS_FMT_UYYVYY16_0_5X48``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + */ + enum { DW_HDMI_RES_8, DW_HDMI_RES_10, @@ -62,6 +123,8 @@ struct dw_hdmi_plat_data { struct regmap *regm; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); + unsigned long input_bus_format; + unsigned long input_bus_encoding; /* Vendor PHY support */ const struct dw_hdmi_phy_ops *phy_ops; -- cgit v1.2.3-58-ga151 From 386d3299ef7298a3bc57e2ff3498ce41ac7f6184 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 4 Apr 2017 14:31:59 +0200 Subject: drm: bridge: dw-hdmi: Move HPD handling to PHY operations The HDMI TX controller support HPD and RXSENSE signaling from the PHY via it's STAT0 PHY interface, but some vendor PHYs can manage these signals independently from the controller, thus these STAT0 handling should be moved to PHY specific operations and become optional. The existing STAT0 HPD and RXSENSE handling code is refactored into a supplementaty set of default PHY operations that are used automatically when the platform glue doesn't provide its own operations. Reviewed-by: Jose Abreu Reviewed-by: Archit Taneja Reviewed-by: Laurent Pinchart Signed-off-by: Neil Armstrong Link: http://patchwork.freedesktop.org/patch/msgid/1491309119-24220-2-git-send-email-narmstrong@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 135 ++++++++++++++++++------------ include/drm/bridge/dw_hdmi.h | 5 ++ 2 files changed, 86 insertions(+), 54 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 16d5fff3e697..84cc949eae2b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1229,10 +1229,46 @@ static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, connector_status_connected : connector_status_disconnected; } +static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense) +{ + u8 old_mask = hdmi->phy_mask; + + if (force || disabled || !rxsense) + hdmi->phy_mask |= HDMI_PHY_RX_SENSE; + else + hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; + + if (old_mask != hdmi->phy_mask) + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); +} + +static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data) +{ + /* + * Configure the PHY RX SENSE and HPD interrupts polarities and clear + * any pending interrupt. + */ + hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0); + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, + HDMI_IH_PHY_STAT0); + + /* Enable cable hot plug irq. */ + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); + + /* Clear and unmute interrupts. */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, + HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), + HDMI_IH_MUTE_PHY_STAT0); +} + static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { .init = dw_hdmi_phy_init, .disable = dw_hdmi_phy_disable, .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, }; /* ----------------------------------------------------------------------------- @@ -1808,35 +1844,10 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi) */ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi) { - u8 old_mask = hdmi->phy_mask; - - if (hdmi->force || hdmi->disabled || !hdmi->rxsense) - hdmi->phy_mask |= HDMI_PHY_RX_SENSE; - else - hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; - - if (old_mask != hdmi->phy_mask) - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); -} - -static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi) -{ - /* - * Configure the PHY RX SENSE and HPD interrupts polarities and clear - * any pending interrupt. - */ - hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0); - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - - /* Enable cable hot plug irq. */ - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); - - /* Clear and unmute interrupts. */ - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), - HDMI_IH_MUTE_PHY_STAT0); + if (hdmi->phy.ops->update_hpd) + hdmi->phy.ops->update_hpd(hdmi, hdmi->phy.data, + hdmi->force, hdmi->disabled, + hdmi->rxsense); } static enum drm_connector_status @@ -2028,6 +2039,41 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) return ret; } +void __dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) +{ + mutex_lock(&hdmi->mutex); + + if (!hdmi->force) { + /* + * If the RX sense status indicates we're disconnected, + * clear the software rxsense status. + */ + if (!rx_sense) + hdmi->rxsense = false; + + /* + * Only set the software rxsense status when both + * rxsense and hpd indicates we're connected. + * This avoids what seems to be bad behaviour in + * at least iMX6S versions of the phy. + */ + if (hpd) + hdmi->rxsense = true; + + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + } + mutex_unlock(&hdmi->mutex); +} + +void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(dev); + + __dw_hdmi_setup_rx_sense(hdmi, hpd, rx_sense); +} +EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense); + static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) { struct dw_hdmi *hdmi = dev_id; @@ -2060,30 +2106,10 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) * ask the source to re-read the EDID. */ if (intr_stat & - (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) { - mutex_lock(&hdmi->mutex); - if (!hdmi->force) { - /* - * If the RX sense status indicates we're disconnected, - * clear the software rxsense status. - */ - if (!(phy_stat & HDMI_PHY_RX_SENSE)) - hdmi->rxsense = false; - - /* - * Only set the software rxsense status when both - * rxsense and hpd indicates we're connected. - * This avoids what seems to be bad behaviour in - * at least iMX6S versions of the phy. - */ - if (phy_stat & HDMI_PHY_HPD) - hdmi->rxsense = true; - - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - } - mutex_unlock(&hdmi->mutex); - } + (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) + __dw_hdmi_setup_rx_sense(hdmi, + phy_stat & HDMI_PHY_HPD, + phy_stat & HDMI_PHY_RX_SENSE); if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { dev_dbg(hdmi->dev, "EVENT=%s\n", @@ -2357,7 +2383,8 @@ __dw_hdmi_probe(struct platform_device *pdev, #endif dw_hdmi_setup_i2c(hdmi); - dw_hdmi_phy_setup_hpd(hdmi); + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 5d6b92c6c0bc..ed599bea3f6c 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -117,6 +117,9 @@ struct dw_hdmi_phy_ops { struct drm_display_mode *mode); void (*disable)(struct dw_hdmi *hdmi, void *data); enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data); + void (*update_hpd)(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense); + void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); }; struct dw_hdmi_plat_data { @@ -147,6 +150,8 @@ void dw_hdmi_unbind(struct device *dev); int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); +void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense); + void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); -- cgit v1.2.3-58-ga151 From e22717046a7da3cd0c0b0eec323e1dc38c777a42 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 4 Apr 2017 11:52:55 +0200 Subject: drm: Consolidate and document sysfs support - remove docs for internal func, doesn't add value - add short overview snippet instead explaining that drivers don't have to bother themselves with reg/unreg concerns - drop the ttm comment about drmP.h, drmP.h is disappearing ... Reviewed-by: Neil Armstrong Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170404095304.17599-2-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-uapi.rst | 10 ++++++ drivers/gpu/drm/drm_sysfs.c | 70 ++++++++++++++++-------------------------- include/drm/drmP.h | 5 +-- include/drm/drm_sysfs.h | 8 ++--- 4 files changed, 42 insertions(+), 51 deletions(-) (limited to 'include/drm') diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 76356c86e358..8b0f0e403f0c 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -219,6 +219,16 @@ Debugfs Support .. kernel-doc:: drivers/gpu/drm/drm_debugfs.c :export: +Sysfs Support +============= + +.. kernel-doc:: drivers/gpu/drm/drm_sysfs.c + :doc: overview + +.. kernel-doc:: drivers/gpu/drm/drm_sysfs.c + :export: + + VBlank event handling ===================== diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 513288b5c2f6..1c5b5ce1fd7f 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -25,6 +25,20 @@ #define to_drm_minor(d) dev_get_drvdata(d) #define to_drm_connector(d) dev_get_drvdata(d) +/** + * DOC: overview + * + * DRM provides very little additional support to drivers for sysfs + * interactions, beyond just all the standard stuff. Drivers who want to expose + * additional sysfs properties and property groups can attach them at either + * &drm_device.dev or &drm_connector.kdev. + * + * Registration is automatically handled when calling drm_dev_register(), or + * drm_connector_register() in case of hot-plugged connectors. Unregistration is + * also automatically handled by drm_dev_unregister() and + * drm_connector_unregister(). + */ + static struct device_type drm_sysfs_device_minor = { .name = "drm_minor" }; @@ -250,15 +264,6 @@ static const struct attribute_group *connector_dev_groups[] = { NULL }; -/** - * drm_sysfs_connector_add - add a connector to sysfs - * @connector: connector to add - * - * Create a connector device in sysfs, along with its associated connector - * properties (so far, connection status, dpms, mode list and edid) and - * generate a hotplug event so userspace knows there's a new connector - * available. - */ int drm_sysfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -285,19 +290,6 @@ int drm_sysfs_connector_add(struct drm_connector *connector) return 0; } -/** - * drm_sysfs_connector_remove - remove an connector device from sysfs - * @connector: connector to remove - * - * Remove @connector and its associated attributes from sysfs. Note that - * the device model core will take care of sending the "remove" uevent - * at this time, so we don't need to do it. - * - * Note: - * This routine should only be called if the connector was previously - * successfully registered. If @connector hasn't been registered yet, - * you'll likely see a panic somewhere deep in sysfs code when called. - */ void drm_sysfs_connector_remove(struct drm_connector *connector) { if (!connector->kdev) @@ -333,20 +325,6 @@ static void drm_sysfs_release(struct device *dev) kfree(dev); } -/** - * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor - * @minor: minor to allocate sysfs device for - * - * This allocates a new sysfs device for @minor and returns it. The device is - * not registered nor linked. The caller has to use device_add() and - * device_del() to register and unregister it. - * - * Note that dev_get_drvdata() on the new device will return the minor. - * However, the device does not hold a ref-count to the minor nor to the - * underlying drm_device. This is unproblematic as long as you access the - * private data only in sysfs callbacks. device_del() disables those - * synchronously, so they cannot be called after you cleanup a minor. - */ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) { const char *minor_str; @@ -384,15 +362,13 @@ err_free: } /** - * drm_class_device_register - Register a struct device in the drm class. + * drm_class_device_register - register new device with the DRM sysfs class + * @dev: device to register * - * @dev: pointer to struct device to register. - * - * @dev should have all relevant members pre-filled with the exception - * of the class member. In particular, the device_type member must - * be set. + * Registers a new &struct device within the DRM sysfs class. Essentially only + * used by ttm to have a place for its global settings. Drivers should never use + * this. */ - int drm_class_device_register(struct device *dev) { if (!drm_class || IS_ERR(drm_class)) @@ -403,6 +379,14 @@ int drm_class_device_register(struct device *dev) } EXPORT_SYMBOL_GPL(drm_class_device_register); +/** + * drm_class_device_unregister - unregister device with the DRM sysfs class + * @dev: device to unregister + * + * Unregisters a &struct device from the DRM sysfs class. Essentially only used + * by ttm to have a place for its global settings. Drivers should never use + * this. + */ void drm_class_device_unregister(struct device *dev) { return device_unregister(dev); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3bfafcdb8710..e1daa4f343cd 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -80,6 +80,7 @@ #include #include #include +#include struct module; @@ -512,10 +513,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. */ - /* sysfs support (drm_sysfs.c) */ -extern void drm_sysfs_hotplug_event(struct drm_device *dev); - - /*@}*/ /* returns true if currently okay to sleep */ diff --git a/include/drm/drm_sysfs.h b/include/drm/drm_sysfs.h index 23418c1f10d1..70c9a1074aca 100644 --- a/include/drm/drm_sysfs.h +++ b/include/drm/drm_sysfs.h @@ -1,12 +1,12 @@ #ifndef _DRM_SYSFS_H_ #define _DRM_SYSFS_H_ -/** - * This minimalistic include file is intended for users (read TTM) that - * don't want to include the full drmP.h file. - */ +struct drm_device; +struct device; int drm_class_device_register(struct device *dev); void drm_class_device_unregister(struct device *dev); +void drm_sysfs_hotplug_event(struct drm_device *dev); + #endif -- cgit v1.2.3-58-ga151 From 2640981f36004085eeff65849410121809fcf560 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 4 Apr 2017 11:52:57 +0200 Subject: drm: document drm_ioctl.[hc] Also unify/merge with the existing stuff. I was a bit torn where to put this, but in the end I decided to put all the ioctl/sysfs/debugfs stuff into drm-uapi.rst. That means we have a bit a split with the other uapi related stuff used internally, like drm_file.[hc], but I think overall this makes more sense. If it's too confusing we can always add more cross-links to make it more discoverable. But the auto-sprinkling of links kernel-doc already does seems sufficient. Also for prettier docs and more cross-links, switch the internal defines over to an enum, as usual. v2: Update kerneldoc fro drm_compat_ioctl too (caught by 0day), plus a bit more drive-by polish. v3: Fix typo, spotted by xerpi on irc (Sergi). v4: Add missing space in comment (Neil). Cc: Sergi Granell Reviewed-by: Neil Armstrong Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170404095304.17599-4-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 50 ---------------- Documentation/gpu/drm-uapi.rst | 14 +++++ drivers/gpu/drm/drm_ioc32.c | 76 ++++++++++++----------- drivers/gpu/drm/drm_ioctl.c | 50 +++++++++++++++- include/drm/drm_ioctl.h | 116 +++++++++++++++++++++++++++++++----- 5 files changed, 203 insertions(+), 103 deletions(-) (limited to 'include/drm') diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a09c721f9e89..babfb6143bd9 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -255,56 +255,6 @@ File Operations .. kernel-doc:: drivers/gpu/drm/drm_file.c :export: -IOCTLs ------- - -struct drm_ioctl_desc \*ioctls; int num_ioctls; - Driver-specific ioctls descriptors table. - -Driver-specific ioctls numbers start at DRM_COMMAND_BASE. The ioctls -descriptors table is indexed by the ioctl number offset from the base -value. Drivers can use the DRM_IOCTL_DEF_DRV() macro to initialize -the table entries. - -:: - - DRM_IOCTL_DEF_DRV(ioctl, func, flags) - -``ioctl`` is the ioctl name. Drivers must define the DRM_##ioctl and -DRM_IOCTL_##ioctl macros to the ioctl number offset from -DRM_COMMAND_BASE and the ioctl number respectively. The first macro is -private to the device while the second must be exposed to userspace in a -public header. - -``func`` is a pointer to the ioctl handler function compatible with the -``drm_ioctl_t`` type. - -:: - - typedef int drm_ioctl_t(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -``flags`` is a bitmask combination of the following values. It restricts -how the ioctl is allowed to be called. - -- DRM_AUTH - Only authenticated callers allowed - -- DRM_MASTER - The ioctl can only be called on the master file handle - -- DRM_ROOT_ONLY - Only callers with the SYSADMIN capability allowed - -- DRM_CONTROL_ALLOW - The ioctl can only be called on a control - device - -- DRM_UNLOCKED - The ioctl handler will be called without locking the - DRM global mutex. This is the enforced default for kms drivers (i.e. - using the DRIVER_MODESET flag) and hence shouldn't be used any more - for new drivers. - -.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c - :export: - - Misc Utilities ============== diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 8b0f0e403f0c..858457567d3d 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -160,6 +160,20 @@ other hand, a driver requires shared state between clients which is visible to user-space and accessible beyond open-file boundaries, they cannot support render nodes. +IOCTL Support on Device Nodes +============================= + +.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c + :doc: driver specific ioctls + +.. kernel-doc:: include/drm/drm_ioctl.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c + :export: + +.. kernel-doc:: drivers/gpu/drm/drm_ioc32.c + :export: Testing and validation ====================== diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index b134482f4022..ae386783e3ea 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -1,4 +1,4 @@ -/** +/* * \file drm_ioc32.c * * 32-bit ioctl compatibility routines for the DRM. @@ -72,15 +72,15 @@ #define DRM_IOCTL_MODE_ADDFB232 DRM_IOWR(0xb8, drm_mode_fb_cmd232_t) typedef struct drm_version_32 { - int version_major; /**< Major version */ - int version_minor; /**< Minor version */ - int version_patchlevel; /**< Patch level */ - u32 name_len; /**< Length of name buffer */ - u32 name; /**< Name of driver */ - u32 date_len; /**< Length of date buffer */ - u32 date; /**< User-space buffer to hold date */ - u32 desc_len; /**< Length of desc buffer */ - u32 desc; /**< User-space buffer to hold desc */ + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel; /* Patch level */ + u32 name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + u32 date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + u32 desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ } drm_version32_t; static int compat_drm_version(struct file *file, unsigned int cmd, @@ -126,8 +126,8 @@ static int compat_drm_version(struct file *file, unsigned int cmd, } typedef struct drm_unique32 { - u32 unique_len; /**< Length of unique */ - u32 unique; /**< Unique name for driver instantiation */ + u32 unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ } drm_unique32_t; static int compat_drm_getunique(struct file *file, unsigned int cmd, @@ -180,12 +180,12 @@ static int compat_drm_setunique(struct file *file, unsigned int cmd, } typedef struct drm_map32 { - u32 offset; /**< Requested physical address (0 for SAREA)*/ - u32 size; /**< Requested physical size (bytes) */ - enum drm_map_type type; /**< Type of memory to map */ - enum drm_map_flags flags; /**< Flags */ - u32 handle; /**< User-space: "Handle" to pass to mmap() */ - int mtrr; /**< MTRR slot used */ + u32 offset; /* Requested physical address (0 for SAREA) */ + u32 size; /* Requested physical size (bytes) */ + enum drm_map_type type; /* Type of memory to map */ + enum drm_map_flags flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap() */ + int mtrr; /* MTRR slot used */ } drm_map32_t; static int compat_drm_getmap(struct file *file, unsigned int cmd, @@ -286,12 +286,12 @@ static int compat_drm_rmmap(struct file *file, unsigned int cmd, } typedef struct drm_client32 { - int idx; /**< Which client desired? */ - int auth; /**< Is client authenticated? */ - u32 pid; /**< Process ID */ - u32 uid; /**< User ID */ - u32 magic; /**< Magic */ - u32 iocs; /**< Ioctl count */ + int idx; /* Which client desired? */ + int auth; /* Is client authenticated? */ + u32 pid; /* Process ID */ + u32 uid; /* User ID */ + u32 magic; /* Magic */ + u32 iocs; /* Ioctl count */ } drm_client32_t; static int compat_drm_getclient(struct file *file, unsigned int cmd, @@ -366,12 +366,12 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd, } typedef struct drm_buf_desc32 { - int count; /**< Number of buffers of this size */ - int size; /**< Size in bytes */ - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ + int count; /* Number of buffers of this size */ + int size; /* Size in bytes */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ int flags; - u32 agp_start; /**< Start address in the AGP aperture */ + u32 agp_start; /* Start address in the AGP aperture */ } drm_buf_desc32_t; static int compat_drm_addbufs(struct file *file, unsigned int cmd, @@ -1111,13 +1111,18 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = { }; /** - * Called whenever a 32-bit process running under a 64-bit kernel - * performs an ioctl on /dev/drm. + * drm_compat_ioctl - 32bit IOCTL compatibility handler for DRM drivers + * @filp: file this ioctl is called on + * @cmd: ioctl cmd number + * @arg: user argument + * + * Compatibility handler for 32 bit userspace running on 64 kernels. All actual + * IOCTL handling is forwarded to drm_ioctl(), while marshalling structures as + * appropriate. Note that this only handles DRM core IOCTLs, if the driver has + * botched IOCTL itself, it must handle those by wrapping this function. * - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument. - * \return zero on success or negative number on failure. + * Returns: + * Zero on success, negative error code on failure. */ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -1141,5 +1146,4 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return ret; } - EXPORT_SYMBOL(drm_compat_ioctl); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 7d6deaa91281..9f4241f0dd9c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -646,14 +646,60 @@ static const struct drm_ioctl_desc drm_ioctls[] = { #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) +/** + * DOC: driver specific ioctls + * + * First things first, driver private IOCTLs should only be needed for drivers + * supporting rendering. Kernel modesetting is all standardized, and extended + * through properties. There are a few exceptions in some existing drivers, + * which define IOCTL for use by the display DRM master, but they all predate + * properties. + * + * Now if you do have a render driver you always have to support it through + * driver private properties. There's a few steps needed to wire all the things + * up. + * + * First you need to define the structure for your IOCTL in your driver private + * UAPI header in ``include/uapi/drm/my_driver_drm.h``:: + * + * struct my_driver_operation { + * u32 some_thing; + * u32 another_thing; + * }; + * + * Please make sure that you follow all the best practices from + * ``Documentation/ioctl/botching-up-ioctls.txt``. Note that drm_ioctl() + * automatically zero-extends structures, hence make sure you can add more stuff + * at the end, i.e. don't put a variable sized array there. + * + * Then you need to define your IOCTL number, using one of DRM_IO(), DRM_IOR(), + * DRM_IOW() or DRM_IOWR(). It must start with the DRM_IOCTL\_ prefix:: + * + * ##define DRM_IOCTL_MY_DRIVER_OPERATION \ + * DRM_IOW(DRM_COMMAND_BASE, struct my_driver_operation) + * + * DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to + * DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire + * up the handlers and set the access rights: + * + * static const struct drm_ioctl_desc my_driver_ioctls[] = { + * DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation, + * DRM_AUTH|DRM_RENDER_ALLOW), + * }; + * + * And then assign this to the &drm_driver.ioctls field in your driver + * structure. + */ + /** * drm_ioctl - ioctl callback implementation for DRM drivers * @filp: file this ioctl is called on * @cmd: ioctl cmd number * @arg: user argument * - * Looks up the ioctl function in the ::ioctls table, checking for root - * previleges if so required, and dispatches to the respective function. + * Looks up the ioctl function in the DRM core and the driver dispatch table, + * stored in &drm_driver.ioctls. It checks for necessary permission by calling + * drm_ioctl_permit(), and dispatches to the respective function. * * Returns: * Zero on success, negative error code on failure. diff --git a/include/drm/drm_ioctl.h b/include/drm/drm_ioctl.h index f17ee077f649..ee03b3c44b3b 100644 --- a/include/drm/drm_ioctl.h +++ b/include/drm/drm_ioctl.h @@ -33,6 +33,7 @@ #define _DRM_IOCTL_H_ #include +#include #include @@ -41,41 +42,126 @@ struct drm_file; struct file; /** - * Ioctl function type. + * drm_ioctl_t - DRM ioctl function type. + * @dev: DRM device inode + * @data: private pointer of the ioctl call + * @file_priv: DRM file this ioctl was made on * - * \param inode device inode. - * \param file_priv DRM file private pointer. - * \param cmd command. - * \param arg argument. + * This is the DRM ioctl typedef. Note that drm_ioctl() has alrady copied @data + * into kernel-space, and will also copy it back, depending upon the read/write + * settings in the ioctl command code. */ typedef int drm_ioctl_t(struct drm_device *dev, void *data, struct drm_file *file_priv); +/** + * drm_ioctl_compat_t - compatibility DRM ioctl function type. + * @filp: file pointer + * @cmd: ioctl command code + * @arg: DRM file this ioctl was made on + * + * Just a typedef to make declaring an array of compatibility handlers easier. + * New drivers shouldn't screw up the structure layout for their ioctl + * structures and hence never need this. + */ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd, unsigned long arg); #define DRM_IOCTL_NR(n) _IOC_NR(n) #define DRM_MAJOR 226 -#define DRM_AUTH 0x1 -#define DRM_MASTER 0x2 -#define DRM_ROOT_ONLY 0x4 -#define DRM_CONTROL_ALLOW 0x8 -#define DRM_UNLOCKED 0x10 -#define DRM_RENDER_ALLOW 0x20 +/** + * enum drm_ioctl_flags - DRM ioctl flags + * + * Various flags that can be set in &drm_ioctl_desc.flags to control how + * userspace can use a given ioctl. + */ +enum drm_ioctl_flags { + /** + * @DRM_AUTH: + * + * This is for ioctl which are used for rendering, and require that the + * file descriptor is either for a render node, or if it's a + * legacy/primary node, then it must be authenticated. + */ + DRM_AUTH = BIT(0), + /** + * @DRM_MASTER: + * + * This must be set for any ioctl which can change the modeset or + * display state. Userspace must call the ioctl through a primary node, + * while it is the active master. + * + * Note that read-only modeset ioctl can also be called by + * unauthenticated clients, or when a master is not the currently active + * one. + */ + DRM_MASTER = BIT(1), + /** + * @DRM_ROOT_ONLY: + * + * Anything that could potentially wreak a master file descriptor needs + * to have this flag set. Current that's only for the SETMASTER and + * DROPMASTER ioctl, which e.g. logind can call to force a non-behaving + * master (display compositor) into compliance. + * + * This is equivalent to callers with the SYSADMIN capability. + */ + DRM_ROOT_ONLY = BIT(2), + /** + * @DRM_CONTROL_ALLOW: + * + * Deprecated, do not use. Control nodes are in the process of getting + * removed. + */ + DRM_CONTROL_ALLOW = BIT(3), + /** + * @DRM_UNLOCKED: + * + * Whether &drm_ioctl_desc.func should be called with the DRM BKL held + * or not. Enforced as the default for all modern drivers, hence there + * should never be a need to set this flag. + */ + DRM_UNLOCKED = BIT(4), + /** + * @DRM_RENDER_ALLOW: + * + * This is used for all ioctl needed for rendering only, for drivers + * which support render nodes. This should be all new render drivers, + * and hence it should be always set for any ioctl with DRM_AUTH set. + * Note though that read-only query ioctl might have this set, but have + * not set DRM_AUTH because they do not require authentication. + */ + DRM_RENDER_ALLOW = BIT(5), +}; +/** + * struct drm_ioctl_desc - DRM driver ioctl entry + * @cmd: ioctl command number, without flags + * @flags: a bitmask of &enum drm_ioctl_flags + * @func: handler for this ioctl + * @name: user-readable name for debug output + * + * For convenience it's easier to create these using the DRM_IOCTL_DEF_DRV() + * macro. + */ struct drm_ioctl_desc { unsigned int cmd; - int flags; + enum drm_ioctl_flags flags; drm_ioctl_t *func; const char *name; }; /** - * Creates a driver or general drm_ioctl_desc array entry for the given - * ioctl, for use by drm_ioctl(). + * DRM_IOCTL_DEF_DRV() - helper macro to fill out a &struct drm_ioctl_desc + * @ioctl: ioctl command suffix + * @_func: handler for the ioctl + * @_flags: a bitmask of &enum drm_ioctl_flags + * + * Small helper macro to create a &struct drm_ioctl_desc entry. The ioctl + * command number is constructed by prepending ``DRM_IOCTL\_`` and passing that + * to DRM_IOCTL_NR(). */ - #define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = { \ .cmd = DRM_IOCTL_##ioctl, \ -- cgit v1.2.3-58-ga151 From 5f27502655f657989dac0622ecf70b9e173e90f8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 3 Apr 2017 10:32:50 +0200 Subject: drm: Make drm_modeset_lock_crtc internal This is only for legacy paths that need to grab the crtc/plane lock combo. If you want to lock a crtc, just use drm_modeset_lock(). Reviewed-by: Harry Wentland Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170403083304.9083-2-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc_internal.h | 3 +++ drivers/gpu/drm/drm_modeset_lock.c | 14 -------------- include/drm/drm_modeset_lock.h | 2 -- 3 files changed, 3 insertions(+), 16 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 8c04275cf226..de1047530e07 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -61,6 +61,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* drm_modeset_lock.c */ +void drm_modeset_lock_crtc(struct drm_crtc *crtc, + struct drm_plane *plane); /* drm_dumb_buffers.c */ /* IOCTLs */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index bf60f2645e55..c94eff9d7544 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -148,19 +148,6 @@ void drm_modeset_unlock_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_modeset_unlock_all); -/** - * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx for a plane update - * @crtc: DRM CRTC - * @plane: DRM plane to be updated on @crtc - * - * This function locks the given crtc and plane (which should be either the - * primary or cursor plane) using a hidden acquire context. This is necessary so - * that drivers internally using the atomic interfaces can grab further locks - * with the lock acquire context. - * - * Note that @plane can be NULL, e.g. when the cursor support hasn't yet been - * converted to universal planes yet. - */ void drm_modeset_lock_crtc(struct drm_crtc *crtc, struct drm_plane *plane) { @@ -205,7 +192,6 @@ fail: goto retry; } } -EXPORT_SYMBOL(drm_modeset_lock_crtc); /** * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 96d39fbd12ca..88d35bfc9cd8 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -121,8 +121,6 @@ struct drm_plane; void drm_modeset_lock_all(struct drm_device *dev); void drm_modeset_unlock_all(struct drm_device *dev); -void drm_modeset_lock_crtc(struct drm_crtc *crtc, - struct drm_plane *plane); void drm_modeset_unlock_crtc(struct drm_crtc *crtc); void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); struct drm_modeset_acquire_ctx * -- cgit v1.2.3-58-ga151 From b95ff0319a829d5e707d64a3994c75f012f6b6ec Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 3 Apr 2017 10:32:51 +0200 Subject: drm: Remove drm_modeset_(un)lock_crtc The last user, the cursor ioctl, can just open-code this too. We simply have to move the acquire ctx dance from the universal function up into the top-level ioctl handler. Reviewed-by: Harry Wentland Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170403083304.9083-3-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc_internal.h | 3 -- drivers/gpu/drm/drm_modeset_lock.c | 67 ------------------------------------- drivers/gpu/drm/drm_plane.c | 49 +++++++++++++-------------- include/drm/drm_modeset_lock.h | 1 - 4 files changed, 24 insertions(+), 96 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index de1047530e07..8c04275cf226 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -61,9 +61,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); -/* drm_modeset_lock.c */ -void drm_modeset_lock_crtc(struct drm_crtc *crtc, - struct drm_plane *plane); /* drm_dumb_buffers.c */ /* IOCTLs */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index c94eff9d7544..c3ca6b859236 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -148,51 +148,6 @@ void drm_modeset_unlock_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_modeset_unlock_all); -void drm_modeset_lock_crtc(struct drm_crtc *crtc, - struct drm_plane *plane) -{ - struct drm_modeset_acquire_ctx *ctx; - int ret; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (WARN_ON(!ctx)) - return; - - drm_modeset_acquire_init(ctx, 0); - -retry: - ret = drm_modeset_lock(&crtc->mutex, ctx); - if (ret) - goto fail; - - if (plane) { - ret = drm_modeset_lock(&plane->mutex, ctx); - if (ret) - goto fail; - - if (plane->crtc) { - ret = drm_modeset_lock(&plane->crtc->mutex, ctx); - if (ret) - goto fail; - } - } - - WARN_ON(crtc->acquire_ctx); - - /* now we hold the locks, so now that it is safe, stash the - * ctx for drm_modeset_unlock_crtc(): - */ - crtc->acquire_ctx = ctx; - - return; - -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } -} - /** * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls * @crtc: drm crtc @@ -214,28 +169,6 @@ drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); -/** - * drm_modeset_unlock_crtc - drop crtc lock - * @crtc: drm crtc - * - * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other - * locks acquired through the hidden context. - */ -void drm_modeset_unlock_crtc(struct drm_crtc *crtc) -{ - struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx; - - if (WARN_ON(!ctx)) - return; - - crtc->acquire_ctx = NULL; - drm_modeset_drop_locks(ctx); - drm_modeset_acquire_fini(ctx); - - kfree(ctx); -} -EXPORT_SYMBOL(drm_modeset_unlock_crtc); - /** * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked * @dev: device diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index bc71aa2b7872..838ca742a28b 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -620,7 +620,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data, static int drm_mode_cursor_universal(struct drm_crtc *crtc, struct drm_mode_cursor2 *req, - struct drm_file *file_priv) + struct drm_file *file_priv, + struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; struct drm_framebuffer *fb = NULL; @@ -634,21 +635,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, int32_t crtc_x, crtc_y; uint32_t crtc_w = 0, crtc_h = 0; uint32_t src_w = 0, src_h = 0; - struct drm_modeset_acquire_ctx ctx; int ret = 0; BUG_ON(!crtc->cursor); WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); - drm_modeset_acquire_init(&ctx, 0); -retry: - ret = drm_modeset_lock(&crtc->mutex, &ctx); - if (ret) - goto fail; - ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx); - if (ret) - goto fail; - /* * Obtain fb we'll be using (either new or existing) and take an extra * reference to it if fb != null. setplane will take care of dropping @@ -693,7 +684,7 @@ retry: */ ret = __setplane_internal(crtc->cursor, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, - 0, 0, src_w, src_h, &ctx); + 0, 0, src_w, src_h, ctx); /* Update successful; save new cursor position, if necessary */ if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { @@ -701,15 +692,6 @@ retry: crtc->cursor_y = req->y; } -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(&ctx); - goto retry; - } - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - return ret; } @@ -718,6 +700,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_file *file_priv) { struct drm_crtc *crtc; + struct drm_modeset_acquire_ctx ctx; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -732,14 +715,24 @@ static int drm_mode_cursor_common(struct drm_device *dev, return -ENOENT; } + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto out; + ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx); + if (ret) + goto out; + /* * If this crtc has a universal cursor plane, call that plane's update * handler rather than using legacy cursor handlers. */ - if (crtc->cursor) - return drm_mode_cursor_universal(crtc, req, file_priv); + if (crtc->cursor) { + ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx); + goto out; + } - drm_modeset_lock_crtc(crtc, crtc->cursor); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -763,7 +756,13 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - drm_modeset_unlock_crtc(crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); return ret; diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 88d35bfc9cd8..2bb065bf0593 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -121,7 +121,6 @@ struct drm_plane; void drm_modeset_lock_all(struct drm_device *dev); void drm_modeset_unlock_all(struct drm_device *dev); -void drm_modeset_unlock_crtc(struct drm_crtc *crtc); void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); struct drm_modeset_acquire_ctx * drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc); -- cgit v1.2.3-58-ga151 From b260ac3ebef5eb748207cd542dba00af6c5caaa5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 3 Apr 2017 10:32:52 +0200 Subject: drm: Remove drm_modeset_legacy_acquire_ctx and crtc->acquire_ctx With all the callers of drm_modeset_lock_crtc gone, and all the places it was formerly used properly wiring the acquire ctx through, we can remove this. The only hidden context magic we still have is now the global one. Reviewed-by: Harry Wentland Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170403083304.9083-4-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_atomic.c | 14 -------------- drivers/gpu/drm/drm_atomic_helper.c | 2 +- drivers/gpu/drm/drm_modeset_lock.c | 21 --------------------- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_pipe_crc.c | 2 +- include/drm/drm_crtc.h | 9 --------- include/drm/drm_modeset_lock.h | 2 -- 7 files changed, 4 insertions(+), 50 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9b892af7811a..345310213820 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1516,19 +1516,9 @@ EXPORT_SYMBOL(drm_atomic_add_affected_planes); void drm_atomic_legacy_backoff(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; - unsigned crtc_mask = 0; - struct drm_crtc *crtc; int ret; bool global = false; - drm_for_each_crtc(crtc, dev) { - if (crtc->acquire_ctx != state->acquire_ctx) - continue; - - crtc_mask |= drm_crtc_mask(crtc); - crtc->acquire_ctx = NULL; - } - if (WARN_ON(dev->mode_config.acquire_ctx == state->acquire_ctx)) { global = true; @@ -1542,10 +1532,6 @@ retry: if (ret) goto retry; - drm_for_each_crtc(crtc, dev) - if (drm_crtc_mask(crtc) & crtc_mask) - crtc->acquire_ctx = state->acquire_ctx; - if (global) dev->mode_config.acquire_ctx = state->acquire_ctx; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index c3994b4d5f32..3160d0881348 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2975,7 +2975,7 @@ int drm_atomic_helper_connector_dpms(struct drm_connector *connector, if (!state) return -ENOMEM; - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; retry: crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) { diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index c3ca6b859236..64ef09a6cccb 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -148,27 +148,6 @@ void drm_modeset_unlock_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_modeset_unlock_all); -/** - * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls - * @crtc: drm crtc - * - * Legacy ioctl operations like cursor updates or page flips only have per-crtc - * locking, and store the acquire ctx in the corresponding crtc. All other - * legacy operations take all locks and use a global acquire context. This - * function grabs the right one. - */ -struct drm_modeset_acquire_ctx * -drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) -{ - if (crtc->acquire_ctx) - return crtc->acquire_ctx; - - WARN_ON(!crtc->dev->mode_config.acquire_ctx); - - return crtc->dev->mode_config.acquire_ctx; -} -EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); - /** * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked * @dev: device diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e27ea89efd67..84abff3f43d9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10715,7 +10715,7 @@ out_hang: state = drm_atomic_state_alloc(dev); if (!state) return -ENOMEM; - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = dev->mode_config.acquire_ctx; retry: plane_state = drm_atomic_get_plane_state(state, primary); @@ -13075,7 +13075,7 @@ void intel_crtc_restore_mode(struct drm_crtc *crtc) return; } - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; retry: crtc_state = drm_atomic_get_crtc_state(state, crtc); diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c index 9fd9c70baeed..206ee4f0150e 100644 --- a/drivers/gpu/drm/i915/intel_pipe_crc.c +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c @@ -522,7 +522,7 @@ static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv, goto unlock; } - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base); + state->acquire_ctx = crtc->base.dev->mode_config.acquire_ctx; pipe_config = intel_atomic_get_crtc_state(state, crtc); if (IS_ERR(pipe_config)) { ret = PTR_ERR(pipe_config); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 2be2192b1373..ede60d67976f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -782,15 +782,6 @@ struct drm_crtc { */ spinlock_t commit_lock; - /** - * @acquire_ctx: - * - * Per-CRTC implicit acquire context used by atomic drivers for legacy - * IOCTLs, so that atomic drivers can get at the locking acquire - * context. - */ - struct drm_modeset_acquire_ctx *acquire_ctx; - #ifdef CONFIG_DEBUG_FS /** * @debugfs_entry: diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 2bb065bf0593..4b27c2bb955c 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -122,8 +122,6 @@ struct drm_plane; void drm_modeset_lock_all(struct drm_device *dev); void drm_modeset_unlock_all(struct drm_device *dev); void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); -struct drm_modeset_acquire_ctx * -drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc); int drm_modeset_lock_all_ctx(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); -- cgit v1.2.3-58-ga151 From 6d124ff845334bc466f56c059147e7ad587c2e7e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 3 Apr 2017 10:33:01 +0200 Subject: drm: Add acquire ctx to ->gamma_set hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atomic helpers really want this instead of the hacked-up legacy backoff trick, which unfortunately prevents drivers from using their own private drm_modeset_locks. Aside: There's a few atomic drivers (nv50, vc4, soon vmwgfx) which don't yet use the new atomic color mgmt/gamma table stuff. Would be nice if they could switch over and just hook up drm_atomic_helper_legacy_gamma_set() instead. Cc: Dave Airlie Cc: Alex Deucher Cc: Christian König Cc: Gerd Hoffmann Cc: Ben Skeggs Cc: Sinclair Yeh Cc: Thomas Hellstrom Cc: Eric Anholt Reviewed-by: Maarten Lankhorst Acked-by: Alex Deucher Reviewed-by: Sinclair Yeh Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170403083304.9083-13-daniel.vetter@ffwll.ch --- drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 3 ++- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 3 ++- drivers/gpu/drm/amd/amdgpu/dce_v6_0.c | 3 ++- drivers/gpu/drm/amd/amdgpu/dce_v8_0.c | 3 ++- drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 3 ++- drivers/gpu/drm/ast/ast_mode.c | 3 ++- drivers/gpu/drm/cirrus/cirrus_mode.c | 3 ++- drivers/gpu/drm/drm_atomic_helper.c | 4 +++- drivers/gpu/drm/drm_color_mgmt.c | 3 ++- drivers/gpu/drm/drm_fb_helper.c | 3 ++- drivers/gpu/drm/gma500/gma_display.c | 3 ++- drivers/gpu/drm/gma500/gma_display.h | 3 ++- drivers/gpu/drm/mgag200/mgag200_mode.c | 3 ++- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 3 ++- drivers/gpu/drm/nouveau/nv50_display.c | 3 ++- drivers/gpu/drm/radeon/radeon_display.c | 3 ++- drivers/gpu/drm/vc4/vc4_crtc.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 3 ++- include/drm/drm_atomic_helper.h | 3 ++- include/drm/drm_crtc.h | 3 ++- 21 files changed, 43 insertions(+), 21 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index f525ae4e0576..daf003dd2351 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2631,7 +2631,8 @@ static void dce_v10_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v10_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 3eac27f24d94..3a7296724457 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2651,7 +2651,8 @@ static void dce_v11_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v11_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 838cf1a778f2..8ccada5d6f39 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -1998,7 +1998,8 @@ static void dce_v6_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v6_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 1b0717b11efe..6943f2641c90 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2482,7 +2482,8 @@ static void dce_v8_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v8_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index 5c51f9a97811..81a24b6b4846 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -165,7 +165,8 @@ static void dce_virtual_bandwidth_update(struct amdgpu_device *adev) } static int dce_virtual_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t size) + u16 *green, u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 47b78e52691c..aaef0a652f10 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -645,7 +645,8 @@ static void ast_crtc_reset(struct drm_crtc *crtc) } static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct ast_crtc *ast_crtc = to_ast_crtc(crtc); int i; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index ed43ab10ac99..53f6f0f84206 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -327,7 +327,8 @@ static void cirrus_crtc_commit(struct drm_crtc *crtc) * but it's a requirement that we provide the function */ static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); int i; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0a3d91c51d61..25569e5b3adc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3479,6 +3479,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); * @green: green correction table * @blue: green correction table * @size: size of the tables + * @ctx: lock acquire context * * Implements support for legacy gamma correction table for drivers * that support color management through the DEGAMMA_LUT/GAMMA_LUT @@ -3486,7 +3487,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); */ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; struct drm_mode_config *config = &dev->mode_config; diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index e1b4084c3d16..b81dcb1d4cb3 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -261,7 +261,8 @@ retry: goto out; } - ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, + crtc->gamma_size, &ctx); out: if (ret == -EDEADLK) { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 9147abb774e8..6dc5381e1c45 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -256,7 +256,8 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) g_base = r_base + crtc->gamma_size; b_base = g_base + crtc->gamma_size; - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, + crtc->gamma_size, NULL); } /** diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 93ff46535c04..e7fd356acf2e 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -177,7 +177,8 @@ void gma_crtc_load_lut(struct drm_crtc *crtc) } int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - u32 size) + u32 size, + struct drm_modeset_acquire_ctx *ctx) { struct gma_crtc *gma_crtc = to_gma_crtc(crtc); int i; diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h index 166e608923db..239c374b6169 100644 --- a/drivers/gpu/drm/gma500/gma_display.h +++ b/drivers/gpu/drm/gma500/gma_display.h @@ -73,7 +73,8 @@ extern int gma_crtc_cursor_set(struct drm_crtc *crtc, extern int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); extern void gma_crtc_load_lut(struct drm_crtc *crtc); extern int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, u32 size); + u16 *blue, u32 size, + struct drm_modeset_acquire_ctx *ctx); extern void gma_crtc_dpms(struct drm_crtc *crtc, int mode); extern void gma_crtc_prepare(struct drm_crtc *crtc); extern void gma_crtc_commit(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index f2e9b2bc18a5..adb411a078e8 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1393,7 +1393,8 @@ static void mga_crtc_commit(struct drm_crtc *crtc) * but it's a requirement that we provide the function */ static int mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct mga_crtc *mga_crtc = to_mga_crtc(crtc); int i; diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 43ab560de7f9..4b4b0b496262 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -788,7 +788,8 @@ nv_crtc_disable(struct drm_crtc *crtc) static int nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); int i; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 3d381d5c82ce..bf504788bce6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2218,7 +2218,8 @@ nv50_head_help = { static int nv50_head_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 i; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 146297a702ab..981385eb5389 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -232,7 +232,8 @@ void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, } static int radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); int i; diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index e8f28c9145a0..d86c8cce3182 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -314,7 +314,8 @@ vc4_crtc_lut_load(struct drm_crtc *crtc) static int vc4_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); u32 i; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6078654d033b..ef9f3a2a4030 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2026,7 +2026,8 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); int i; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 0c226b2adea5..13f2f1d2818a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -254,7 +254,8 @@ void vmw_du_crtc_save(struct drm_crtc *crtc); void vmw_du_crtc_restore(struct drm_crtc *crtc); int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index fd395dc050ee..f0a8678ae98e 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -176,7 +176,8 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); /** * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ede60d67976f..a8176a836e25 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -322,7 +322,8 @@ struct drm_crtc_funcs { * hooks. */ int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); /** * @destroy: -- cgit v1.2.3-58-ga151 From 6c5ed5ae353cdf156f9ac4db17e15db56b4de880 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 6 Apr 2017 20:55:20 +0200 Subject: drm/atomic: Acquire connection_mutex lock in drm_helper_probe_single_connector_modes, v4. mode_valid() called from drm_helper_probe_single_connector_modes() may need to look at connector->state because what a valid mode is may depend on connector properties being set. For example some HDMI modes might be rejected when a connector property forces the connector into DVI mode. Some implementations of detect() already lock all state, so we have to pass an acquire_ctx to them to prevent a deadlock. This means changing the function signature of detect() slightly, and passing the acquire_ctx for locking multiple crtc's. For the callbacks, it will always be non-zero. To allow callers not to worry about this, drm_helper_probe_detect_ctx is added which might handle -EDEADLK for you. Changes since v1: - Always set ctx parameter. Changes since v2: - Always take connection_mutex when probing. Changes since v3: - Remove the ctx from intel_dp_long_pulse, and add WARN_ON(!connection_mutex) (danvet) - Update docs to clarify the locking situation. (danvet) Signed-off-by: Maarten Lankhorst Cc: Boris Brezillon Reviewed-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1491504920-4017-1-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_probe_helper.c | 100 ++++++++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_crt.c | 25 ++++---- drivers/gpu/drm/i915/intel_display.c | 25 ++++---- drivers/gpu/drm/i915/intel_dp.c | 21 +++---- drivers/gpu/drm/i915/intel_drv.h | 8 +-- drivers/gpu/drm/i915/intel_hotplug.c | 3 +- drivers/gpu/drm/i915/intel_tv.c | 21 +++---- include/drm/drm_connector.h | 6 ++ include/drm/drm_crtc_helper.h | 3 + include/drm/drm_modeset_helper_vtables.h | 36 +++++++++++ 10 files changed, 187 insertions(+), 61 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index efb5e5e8ce62..1b0c14ab3fff 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -169,12 +169,73 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) EXPORT_SYMBOL(drm_kms_helper_poll_enable); static enum drm_connector_status -drm_connector_detect(struct drm_connector *connector, bool force) +drm_helper_probe_detect_ctx(struct drm_connector *connector, bool force) { - return connector->funcs->detect ? - connector->funcs->detect(connector, force) : - connector_status_connected; + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_modeset_acquire_ctx ctx; + int ret; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + ret = drm_modeset_lock(&connector->dev->mode_config.connection_mutex, &ctx); + if (!ret) { + if (funcs->detect_ctx) + ret = funcs->detect_ctx(connector, &ctx, force); + else if (connector->funcs->detect) + ret = connector->funcs->detect(connector, force); + else + ret = connector_status_connected; + } + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + if (WARN_ON(ret < 0)) + ret = connector_status_unknown; + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; +} + +/** + * drm_helper_probe_detect - probe connector status + * @connector: connector to probe + * @ctx: acquire_ctx, or NULL to let this function handle locking. + * @force: Whether destructive probe operations should be performed. + * + * This function calls the detect callbacks of the connector. + * This function returns &drm_connector_status, or + * if @ctx is set, it might also return -EDEADLK. + */ +int +drm_helper_probe_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_device *dev = connector->dev; + int ret; + + if (!ctx) + return drm_helper_probe_detect_ctx(connector, force); + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); + if (ret) + return ret; + + if (funcs->detect_ctx) + return funcs->detect_ctx(connector, ctx, force); + else if (connector->funcs->detect) + return connector->funcs->detect(connector, force); + else + return connector_status_connected; } +EXPORT_SYMBOL(drm_helper_probe_detect); /** * drm_helper_probe_single_connector_modes - get complete set of display modes @@ -239,15 +300,27 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, struct drm_display_mode *mode; const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; - int count = 0; + int count = 0, ret; int mode_flags = 0; bool verbose_prune = true; enum drm_connector_status old_status; + struct drm_modeset_acquire_ctx ctx; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + drm_modeset_acquire_init(&ctx, 0); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + +retry: + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else + WARN_ON(ret < 0); + /* set all old modes to the stale state */ list_for_each_entry(mode, &connector->modes, head) mode->status = MODE_STALE; @@ -263,7 +336,15 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, if (connector->funcs->force) connector->funcs->force(connector); } else { - connector->status = drm_connector_detect(connector, true); + ret = drm_helper_probe_detect(connector, &ctx, true); + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else if (WARN(ret < 0, "Invalid return value %i for connector detection\n", ret)) + ret = connector_status_unknown; + + connector->status = ret; } /* @@ -355,6 +436,9 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, prune: drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + if (list_empty(&connector->modes)) return 0; @@ -440,7 +524,7 @@ static void output_poll_execute(struct work_struct *work) repoll = true; - connector->status = drm_connector_detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); if (old_status != connector->status) { const char *old, *new; @@ -588,7 +672,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) old_status = connector->status; - connector->status = drm_connector_detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", connector->base.id, connector->name, diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 8c82607294c6..2797bf37c3ac 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -669,15 +669,16 @@ static const struct dmi_system_id intel_spurious_crt_detect[] = { { } }; -static enum drm_connector_status -intel_crt_detect(struct drm_connector *connector, bool force) +static int +intel_crt_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_crt *crt = intel_attached_crt(connector); struct intel_encoder *intel_encoder = &crt->base; - enum drm_connector_status status; + int status, ret; struct intel_load_detect_pipe tmp; - struct drm_modeset_acquire_ctx ctx; DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", connector->base.id, connector->name, @@ -721,10 +722,9 @@ intel_crt_detect(struct drm_connector *connector, bool force) goto out; } - drm_modeset_acquire_init(&ctx, 0); - /* for pre-945g platforms use load detect */ - if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { + ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx); + if (ret > 0) { if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else if (INTEL_GEN(dev_priv) < 4) @@ -734,12 +734,11 @@ intel_crt_detect(struct drm_connector *connector, bool force) status = connector_status_disconnected; else status = connector_status_unknown; - intel_release_load_detect_pipe(connector, &tmp, &ctx); - } else + intel_release_load_detect_pipe(connector, &tmp, ctx); + } else if (ret == 0) status = connector_status_unknown; - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + else if (ret < 0) + status = ret; out: intel_display_power_put(dev_priv, intel_encoder->power_domain); @@ -811,7 +810,6 @@ void intel_crt_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_crt_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_crt_detect, .fill_modes = drm_helper_probe_single_connector_modes, .late_register = intel_connector_register, .early_unregister = intel_connector_unregister, @@ -823,6 +821,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { + .detect_ctx = intel_crt_detect, .mode_valid = intel_crt_mode_valid, .get_modes = intel_crt_get_modes, }; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 602fd479dd30..e217d04133e6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9480,10 +9480,10 @@ static int intel_modeset_setup_plane_state(struct drm_atomic_state *state, return 0; } -bool intel_get_load_detect_pipe(struct drm_connector *connector, - struct drm_display_mode *mode, - struct intel_load_detect_pipe *old, - struct drm_modeset_acquire_ctx *ctx) +int intel_get_load_detect_pipe(struct drm_connector *connector, + struct drm_display_mode *mode, + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx) { struct intel_crtc *intel_crtc; struct intel_encoder *intel_encoder = @@ -9506,10 +9506,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, old->restore_state = NULL; -retry: - ret = drm_modeset_lock(&config->connection_mutex, ctx); - if (ret) - goto fail; + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); /* * Algorithm gets a little messy: @@ -9659,10 +9656,8 @@ fail: restore_state = NULL; } - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } + if (ret == -EDEADLK) + return ret; return false; } @@ -15030,6 +15025,7 @@ static void intel_enable_pipe_a(struct drm_device *dev) struct drm_connector *crt = NULL; struct intel_load_detect_pipe load_detect_temp; struct drm_modeset_acquire_ctx *ctx = dev->mode_config.acquire_ctx; + int ret; /* We can't just switch on the pipe A, we need to set things up with a * proper mode and output configuration. As a gross hack, enable pipe A @@ -15046,7 +15042,10 @@ static void intel_enable_pipe_a(struct drm_device *dev) if (!crt) return; - if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx)) + ret = intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx); + WARN(ret < 0, "All modeset mutexes are locked, but intel_get_load_detect_pipe failed\n"); + + if (ret > 0) intel_release_load_detect_pipe(crt, &load_detect_temp, ctx); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fd96a6cf7326..6e04cb54e3ff 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4566,7 +4566,7 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) intel_dp->has_audio = false; } -static enum drm_connector_status +static int intel_dp_long_pulse(struct intel_connector *intel_connector) { struct drm_connector *connector = &intel_connector->base; @@ -4577,6 +4577,8 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) enum drm_connector_status status; u8 sink_irq_vector = 0; + WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex)); + intel_display_power_get(to_i915(dev), intel_dp->aux_power_domain); /* Can't disconnect eDP, but you can close the lid... */ @@ -4635,14 +4637,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) status = connector_status_disconnected; goto out; } else if (connector->status == connector_status_connected) { - /* - * If display was connected already and is still connected - * check links status, there has been known issues of - * link loss triggerring long pulse!!!! - */ - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); intel_dp_check_link_status(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); goto out; } @@ -4682,11 +4677,13 @@ out: return status; } -static enum drm_connector_status -intel_dp_detect(struct drm_connector *connector, bool force) +static int +intel_dp_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct intel_dp *intel_dp = intel_attached_dp(connector); - enum drm_connector_status status = connector->status; + int status = connector->status; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); @@ -5014,7 +5011,6 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_dp_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_dp_detect, .force = intel_dp_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, @@ -5027,6 +5023,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { + .detect_ctx = intel_dp_detect, .get_modes = intel_dp_get_modes, .mode_valid = intel_dp_mode_valid, }; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 51228fe4283b..fb7afcf9e8be 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1353,10 +1353,10 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport, unsigned int expected_mask); -bool intel_get_load_detect_pipe(struct drm_connector *connector, - struct drm_display_mode *mode, - struct intel_load_detect_pipe *old, - struct drm_modeset_acquire_ctx *ctx); +int intel_get_load_detect_pipe(struct drm_connector *connector, + struct drm_display_mode *mode, + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx); void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old, struct drm_modeset_acquire_ctx *ctx); diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c index 7d210097eefa..f1200272a699 100644 --- a/drivers/gpu/drm/i915/intel_hotplug.c +++ b/drivers/gpu/drm/i915/intel_hotplug.c @@ -243,7 +243,8 @@ static bool intel_hpd_irq_event(struct drm_device *dev, WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); old_status = connector->status; - connector->status = connector->funcs->detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); + if (old_status == connector->status) return false; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 6ed1a3ce47b7..e077c2a9e694 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1315,8 +1315,10 @@ static void intel_tv_find_better_format(struct drm_connector *connector) * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure * we have a pipe programmed in order to probe the TV. */ -static enum drm_connector_status -intel_tv_detect(struct drm_connector *connector, bool force) +static int +intel_tv_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct drm_display_mode mode; struct intel_tv *intel_tv = intel_attached_tv(connector); @@ -1331,21 +1333,20 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (force) { struct intel_load_detect_pipe tmp; - struct drm_modeset_acquire_ctx ctx; + int ret; - drm_modeset_acquire_init(&ctx, 0); + ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx); + if (ret < 0) + return ret; - if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) { + if (ret > 0) { type = intel_tv_detect_type(intel_tv, connector); - intel_release_load_detect_pipe(connector, &tmp, &ctx); + intel_release_load_detect_pipe(connector, &tmp, ctx); status = type < 0 ? connector_status_disconnected : connector_status_connected; } else status = connector_status_unknown; - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); } else return connector->status; @@ -1516,7 +1517,6 @@ out: static const struct drm_connector_funcs intel_tv_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_tv_detect, .late_register = intel_connector_register, .early_unregister = intel_connector_unregister, .destroy = intel_tv_destroy, @@ -1528,6 +1528,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { + .detect_ctx = intel_tv_detect, .mode_valid = intel_tv_mode_valid, .get_modes = intel_tv_get_modes, }; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 941f57f311aa..e2ee74c20b8f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -32,6 +32,7 @@ struct drm_device; struct drm_connector_helper_funcs; +struct drm_modeset_acquire_ctx; struct drm_device; struct drm_crtc; struct drm_encoder; @@ -378,6 +379,11 @@ struct drm_connector_funcs { * the helper library vtable purely for historical reasons. The only DRM * core entry point to probe connector state is @fill_modes. * + * Note that the helper library will already hold + * &drm_mode_config.connection_mutex. Drivers which need to grab additional + * locks to avoid races with concurrent modeset changes need to use + * &drm_connector_helper_funcs.detect_ctx instead. + * * RETURNS: * * drm_connector_status indicating the connector's status. diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 43505c7b2b3f..76e237bd989b 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -67,6 +67,9 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); +int drm_helper_probe_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); void drm_kms_helper_poll_init(struct drm_device *dev); void drm_kms_helper_poll_fini(struct drm_device *dev); bool drm_helper_hpd_irq_event(struct drm_device *dev); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 091c42205667..7847babd893c 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -747,12 +747,44 @@ struct drm_connector_helper_funcs { * This callback is used by the probe helpers in e.g. * drm_helper_probe_single_connector_modes(). * + * To avoid races with concurrent connector state updates, the helper + * libraries always call this with the &drm_mode_config.connection_mutex + * held. Because of this it's safe to inspect &drm_connector->state. + * * RETURNS: * * The number of modes added by calling drm_mode_probed_add(). */ int (*get_modes)(struct drm_connector *connector); + /** + * @detect_ctx: + * + * Check to see if anything is attached to the connector. The parameter + * force is set to false whilst polling, true when checking the + * connector due to a user request. force can be used by the driver to + * avoid expensive, destructive operations during automated probing. + * + * This callback is optional, if not implemented the connector will be + * considered as always being attached. + * + * This is the atomic version of &drm_connector_funcs.detect. + * + * To avoid races against concurrent connector state updates, the + * helper libraries always call this with ctx set to a valid context, + * and &drm_mode_config.connection_mutex will always be locked with + * the ctx parameter set to this ctx. This allows taking additional + * locks as required. + * + * RETURNS: + * + * &drm_connector_status indicating the connector's status, + * or the error code returned by drm_modeset_lock(), -EDEADLK. + */ + int (*detect_ctx)(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); + /** * @mode_valid: * @@ -771,6 +803,10 @@ struct drm_connector_helper_funcs { * CRTC helpers will not call this function. Drivers therefore must * still fully validate any mode passed in in a modeset request. * + * To avoid races with concurrent connector state updates, the helper + * libraries always call this with the &drm_mode_config.connection_mutex + * held. Because of this it's safe to inspect &drm_connector->state. + * * RETURNS: * * Either &drm_mode_status.MODE_OK or one of the failure reasons in &enum -- cgit v1.2.3-58-ga151 From 44596b8c4750d9a2323f52aecdeecf34f6aaa2e0 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 6 Apr 2017 13:19:00 +0200 Subject: drm/atomic: Unify conflicting encoder handling. Currently we use a flag to change behavior in atomic commit whether a conflicting encoder should be enabled or disabled. This is used for the legacy set_config helper, which disables connectors that have a conflicting encoder but not part of the active crtc list. There's no need for this to be handled in atomic commit, it could be done in the set_config helper instead. This will let the atomic check function reject any conflicting encoders, while set_config can disable conflicting crtc's. This makes it possible to recalculate the changed flags in 1 loop. Signed-off-by: Maarten Lankhorst Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/1491477543-31257-2-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 7 +++++-- include/drm/drm_atomic.h | 2 -- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index a90d67b6310b..37a7fc7649a4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -517,7 +517,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } } - ret = handle_conflicting_encoders(state, state->legacy_set_config); + ret = handle_conflicting_encoders(state, false); if (ret) return ret; @@ -2289,12 +2289,15 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set, if (!state) return -ENOMEM; - state->legacy_set_config = true; state->acquire_ctx = ctx; ret = __drm_atomic_helper_set_config(set, state); if (ret != 0) goto fail; + ret = handle_conflicting_encoders(state, true); + if (ret) + return ret; + ret = drm_atomic_commit(state); fail: diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index fd33ed5eaeb4..788daf756f48 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -160,7 +160,6 @@ struct __drm_connnectors_state { * @dev: parent DRM device * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics - * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL. * @planes: pointer to array of structures with per-plane data * @crtcs: pointer to array of CRTC pointers * @num_connector: size of the @connectors and @connector_states arrays @@ -173,7 +172,6 @@ struct drm_atomic_state { struct drm_device *dev; bool allow_modeset : 1; bool legacy_cursor_update : 1; - bool legacy_set_config : 1; struct __drm_planes_state *planes; struct __drm_crtcs_state *crtcs; int num_connector; -- cgit v1.2.3-58-ga151 From ce09d7667dd225564df1e20b8185d4ce7be886cc Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 6 Apr 2017 13:19:03 +0200 Subject: drm/atomic: Add connector atomic_check function, v2. The atomic_check function is useful for implementing properties, but it can be used for other connector modeset related checks as well. Similar to plane check functions, on a modeset atomic_check() is always called. Changes since v1: - Make sure atomic_check() is called on any modeset. Signed-off-by: Maarten Lankhorst Reviewed-by: Dhinakaran Pandiyan Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/1491477543-31257-5-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 44 +++++++++++++++++++++++++++++--- include/drm/drm_modeset_helper_vtables.h | 34 ++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ff3c6eb5b6bd..8be9719284b0 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -459,10 +459,20 @@ mode_fixup(struct drm_atomic_state *state) * * Check the state object to see if the requested state is physically possible. * This does all the crtc and connector related computations for an atomic - * update and adds any additional connectors needed for full modesets and calls - * down into &drm_crtc_helper_funcs.mode_fixup and - * &drm_encoder_helper_funcs.mode_fixup or - * &drm_encoder_helper_funcs.atomic_check functions of the driver backend. + * update and adds any additional connectors needed for full modesets. It calls + * the various per-object callbacks in the follow order: + * + * 1. &drm_connector_helper_funcs.atomic_best_encoder for determining the new encoder. + * 2. &drm_connector_helper_funcs.atomic_check to validate the connector state. + * 3. If it's determined a modeset is needed then all connectors on the affected crtc + * crtc are added and &drm_connector_helper_funcs.atomic_check is run on them. + * 4. &drm_bridge_funcs.mode_fixup is called on all encoder bridges. + * 5. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state. + * This function is only called when the encoder will be part of a configured crtc, + * it must not be used for implementing connector property validation. + * If this function is NULL, &drm_atomic_encoder_helper_funcs.mode_fixup is called + * instead. + * 6. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with crtc constraints. * * &drm_crtc_state.mode_changed is set when the input mode is changed. * &drm_crtc_state.connectors_changed is set when a connector is added or @@ -492,6 +502,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_connector *connector; struct drm_connector_state *old_connector_state, *new_connector_state; int i, ret; + unsigned connectors_mask = 0; for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { bool has_connectors = @@ -538,6 +549,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + /* * This only sets crtc->connectors_changed for routing changes, * drivers must set crtc->connectors_changed themselves when @@ -555,6 +568,13 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, new_connector_state->link_status) new_crtc_state->connectors_changed = true; } + + if (funcs->atomic_check) + ret = funcs->atomic_check(connector, new_connector_state); + if (ret) + return ret; + + connectors_mask += BIT(i); } /* @@ -581,6 +601,22 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; } + /* + * Iterate over all connectors again, to make sure atomic_check() + * has been called on them when a modeset is forced. + */ + for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + + if (connectors_mask & BIT(i)) + continue; + + if (funcs->atomic_check) + ret = funcs->atomic_check(connector, new_connector_state); + if (ret) + return ret; + } + return mode_fixup(state); } EXPORT_SYMBOL(drm_atomic_helper_check_modeset); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 7847babd893c..c01c328f6cc8 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -872,6 +872,40 @@ struct drm_connector_helper_funcs { */ struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector, struct drm_connector_state *connector_state); + + /** + * @atomic_check: + * + * This hook is used to validate connector state. This function is + * called from &drm_atomic_helper_check_modeset, and is called when + * a connector property is set, or a modeset on the crtc is forced. + * + * Because &drm_atomic_helper_check_modeset may be called multiple times, + * this function should handle being called multiple times as well. + * + * This function is also allowed to inspect any other object's state and + * can add more state objects to the atomic commit if needed. Care must + * be taken though to ensure that state check and compute functions for + * these added states are all called, and derived state in other objects + * all updated. Again the recommendation is to just call check helpers + * until a maximal configuration is reached. + * + * NOTE: + * + * This function is called in the check phase of an atomic update. The + * driver is not allowed to change anything outside of the free-standing + * state objects passed-in or assembled in the overall &drm_atomic_state + * update tracking structure. + * + * RETURNS: + * + * 0 on success, -EINVAL if the state or the transition can't be + * supported, -ENOMEM on memory allocation failure and -EDEADLK if an + * attempt to obtain another state object ran into a &drm_modeset_lock + * deadlock. + */ + int (*atomic_check)(struct drm_connector *connector, + struct drm_connector_state *state); }; /** -- cgit v1.2.3-58-ga151 From b61c8d5d9a27715dd31781910aef12be4882ebfd Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 22 Mar 2017 08:26:04 -0500 Subject: drm: make of_drm_find_panel also depend on CONFIG_DRM_PANEL For drm_of_find_panel_or_bridge() added in the next commit, an empty version of of_drm_find_panel is needed for !CONFIG_DRM_PANEL. Signed-off-by: Rob Herring Reviewed-by: Eric Anholt Signed-off-by: Sean Paul --- include/drm/drm_panel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 4b76cf2d5a7b..1b364b0100f4 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -192,7 +192,7 @@ void drm_panel_remove(struct drm_panel *panel); int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector); int drm_panel_detach(struct drm_panel *panel); -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) struct drm_panel *of_drm_find_panel(const struct device_node *np); #else static inline struct drm_panel *of_drm_find_panel(const struct device_node *np) -- cgit v1.2.3-58-ga151 From 1f2db3034c9f16ed918e34809167546f21d0fcb4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 22 Mar 2017 08:26:05 -0500 Subject: drm: of: introduce drm_of_find_panel_or_bridge Many drivers have a common pattern of searching the OF graph for either an attached panel or bridge and then finding the DRM struct for the panel or bridge. Also, most drivers need to handle deferred probing when the DRM device is not yet instantiated. Create a common function, drm_of_find_panel_or_bridge, to find the connected node and the associated DRM panel or bridge device. Signed-off-by: Rob Herring Acked-by: Philipp Zabel [seanpaul dropped extern from drm_of.h] Signed-off-by: Sean Paul --- drivers/gpu/drm/drm_of.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_of.h | 13 ++++++++++++ 2 files changed, 65 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index b5f2f0fece99..2120f33bdf4a 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include static void drm_release_of(struct device *dev, void *data) @@ -208,3 +210,53 @@ int drm_of_encoder_active_endpoint(struct device_node *node, return -EINVAL; } EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); + +/* + * drm_of_find_panel_or_bridge - return connected panel or bridge device + * @np: device tree node containing encoder output ports + * @panel: pointer to hold returned drm_panel + * @bridge: pointer to hold returned drm_bridge + * + * Given a DT node's port and endpoint number, find the connected node and + * return either the associated struct drm_panel or drm_bridge device. Either + * @panel or @bridge must not be NULL. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + int ret = -EPROBE_DEFER; + struct device_node *remote; + + if (!panel && !bridge) + return -EINVAL; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + if (panel) { + *panel = of_drm_find_panel(remote); + if (*panel) + ret = 0; + } + + /* No panel found yet, check for a bridge next. */ + if (bridge) { + if (ret) { + *bridge = of_drm_find_bridge(remote); + if (*bridge) + ret = 0; + } else { + *bridge = NULL; + } + + } + + of_node_put(remote); + return ret; +} +EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index d1fc563f068a..104dd517fdbe 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -8,6 +8,8 @@ struct component_match; struct device; struct drm_device; struct drm_encoder; +struct drm_panel; +struct drm_bridge; struct device_node; #ifdef CONFIG_OF @@ -23,6 +25,10 @@ int drm_of_component_probe(struct device *dev, int drm_of_encoder_active_endpoint(struct device_node *node, struct drm_encoder *encoder, struct of_endpoint *endpoint); +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge); #else static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port) @@ -52,6 +58,13 @@ static inline int drm_of_encoder_active_endpoint(struct device_node *node, { return -EINVAL; } +static inline int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + return -EINVAL; +} #endif static inline int drm_of_encoder_active_endpoint_id(struct device_node *node, -- cgit v1.2.3-58-ga151