From 3ae7fb202d86b7847f237daa474f3946bdc3b0c6 Mon Sep 17 00:00:00 2001 From: Haneen Mohammed Date: Tue, 20 Mar 2018 09:37:49 -0400 Subject: [PATCH 001/122] drm: Remove drm_property_{un/reference}_blob aliases This patch remove the compatibility aliases drm_property_{reference/unreference}_blob of drm_property_blob_{get/put} since all callers have been converted to the prefered _{get/put}. Remove the helpers from the semantic patch drm-get-put-cocci. Signed-off-by: Haneen Mohammed Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20180320133749.GA11695@haneen-VirtualBox --- include/drm/drm_property.h | 26 ------------------------ scripts/coccinelle/api/drm-get-put.cocci | 10 --------- 2 files changed, 36 deletions(-) diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h index d1423c7f3c73e..ab8167baade5b 100644 --- a/include/drm/drm_property.h +++ b/include/drm/drm_property.h @@ -280,32 +280,6 @@ bool drm_property_replace_blob(struct drm_property_blob **blob, struct drm_property_blob *drm_property_blob_get(struct drm_property_blob *blob); void drm_property_blob_put(struct drm_property_blob *blob); -/** - * drm_property_reference_blob - acquire a blob property reference - * @blob: DRM blob property - * - * This is a compatibility alias for drm_property_blob_get() and should not be - * used by new code. - */ -static inline struct drm_property_blob * -drm_property_reference_blob(struct drm_property_blob *blob) -{ - return drm_property_blob_get(blob); -} - -/** - * drm_property_unreference_blob - release a blob property reference - * @blob: DRM blob property - * - * This is a compatibility alias for drm_property_blob_put() and should not be - * used by new code. - */ -static inline void -drm_property_unreference_blob(struct drm_property_blob *blob) -{ - drm_property_blob_put(blob); -} - /** * drm_property_find - find property object * @dev: DRM device diff --git a/scripts/coccinelle/api/drm-get-put.cocci b/scripts/coccinelle/api/drm-get-put.cocci index ceb71ea7f61c7..3a09c97ad87d9 100644 --- a/scripts/coccinelle/api/drm-get-put.cocci +++ b/scripts/coccinelle/api/drm-get-put.cocci @@ -40,12 +40,6 @@ expression object; - drm_gem_object_unreference_unlocked(object) + drm_gem_object_put_unlocked(object) | -- drm_property_reference_blob(object) -+ drm_property_blob_get(object) -| -- drm_property_unreference_blob(object) -+ drm_property_blob_put(object) -| - drm_dev_unref(object) + drm_dev_put(object) ) @@ -72,10 +66,6 @@ __drm_gem_object_unreference(object) | drm_gem_object_unreference_unlocked(object) | -drm_property_unreference_blob@p(object) -| -drm_property_reference_blob@p(object) -| drm_dev_unref@p(object) ) From aec06c7606be1635d82c16dda66282c2010bcd39 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Sun, 25 Mar 2018 23:31:32 +0530 Subject: [PATCH 002/122] gpu: drm/lease:: Use list_{next/prev}_entry instead of list_entry It's better to use list_entry instead of list_{next/prev}_entry as it makes the code more clear to read. This patch replace list_entry with list_{next/prev}_entry. Signed-off-by: Arushi Singhal Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1522000893-5331-2-git-send-email-arushisinghal19971997@gmail.com --- drivers/gpu/drm/drm_lease.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index d345563fdff31..50c73c0a20b92 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -340,7 +340,7 @@ static void _drm_lease_revoke(struct drm_master *top) break; /* Over */ - master = list_entry(master->lessee_list.next, struct drm_master, lessee_list); + master = list_next_entry(master, lessee_list); } } } From 3f07f28b9712605d1adb589344ced72e8397dc8a Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Sun, 25 Mar 2018 23:31:33 +0530 Subject: [PATCH 003/122] gpu: drm: nouveau: Use list_{next/prev}_entry instead of list_entry It's better to use list_entry instead of list_{next/prev}_entry as it makes the code more clear to read. This patch replace list_entry with list_{next/prev}_entry. Signed-off-by: Arushi Singhal Acked-by: Ben Skeggs Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1522000893-5331-3-git-send-email-arushisinghal19971997@gmail.com --- drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c index e4c8d310d8707..81c3567d4e67c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c @@ -134,7 +134,7 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, nvkm_volt_map(volt, volt->max2_id, clk->temp)); for (cstate = start; &cstate->head != &pstate->list; - cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { + cstate = list_prev_entry(cstate, head)) { if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp)) break; } From 3903117609f40a0bc21b8c48533ac07a574c4cdc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 22 Mar 2018 09:02:33 +0100 Subject: [PATCH 004/122] drm/gem: Document that handle_create must be the last step It published the gem object to userspace, by that point other threads can guess the id and start using it. And gem IDs are _very_ easy to guess (it's just an idr). Since gem objects is the only thing we allow drivers to create themselves (all the kms/prime/syncobj stuff is handled by the core) no other functions seem to be in need of this clarification. Motivated by reviewing the xen-front kms driver. Cc: Oleksandr Andrushchenko Reviewed-by: Oleksandr Andrushchenko Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180322080233.17266-1-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 4975ba9a7bc89..4a16d7b26c89c 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -436,9 +436,12 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, * @obj: object to register * @handlep: pionter to return the created handle to the caller * - * Create a handle for this object. This adds a handle reference - * to the object, which includes a regular reference count. Callers - * will likely want to dereference the object afterwards. + * Create a handle for this object. This adds a handle reference to the object, + * which includes a regular reference count. Callers will likely want to + * dereference the object afterwards. + * + * Since this publishes @obj to userspace it must be fully set up by this point, + * drivers must call this last in their buffer object creation callbacks. */ int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, From 8017e422af3f5ab90210bfbd17f9a8d8d56db289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 23 Mar 2018 20:25:37 +0200 Subject: [PATCH 005/122] drm/scdc-helper: Convert errors into debug messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we may attempt to reconfigure SCDC when the sink has already been disconnected we probably shouldn't scare the user with errors in dmesg that are 100% expected in that case. Just leave it up to the caller whether to print an error message or not, and just output debug messages from the helper itself. Cc: Shashank Sharma Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180323182537.30784-1-ville.syrjala@linux.intel.com Reviewed-by: Shashank Sharma --- drivers/gpu/drm/drm_scdc_helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c index 657ea5ab6c3f8..870e25f1f788f 100644 --- a/drivers/gpu/drm/drm_scdc_helper.c +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -141,7 +141,7 @@ bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter) ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status); if (ret < 0) { - DRM_ERROR("Failed to read scrambling status: %d\n", ret); + DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret); return false; } @@ -168,7 +168,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable) ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); if (ret < 0) { - DRM_ERROR("Failed to read TMDS config: %d\n", ret); + DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret); return false; } @@ -179,7 +179,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable) ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); if (ret < 0) { - DRM_ERROR("Failed to enable scrambling: %d\n", ret); + DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret); return false; } @@ -223,7 +223,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set) ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); if (ret < 0) { - DRM_ERROR("Failed to read TMDS config: %d\n", ret); + DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret); return false; } @@ -234,7 +234,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set) ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); if (ret < 0) { - DRM_ERROR("Failed to set TMDS clock ratio: %d\n", ret); + DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret); return false; } From a7d2a87e99deb5481b5dd723408c42f460de25a3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 22 Mar 2018 11:51:28 +0100 Subject: [PATCH 006/122] drm/tinydrm: Use gem_free_object_unlocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tinydrm doesn't use dev->struct_mutex and therefore has no need to use gem_free_object. Signed-off-by: Daniel Vetter Cc: "Noralf Trønnes" Acked-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/20180322105133.11211-2-daniel.vetter@ffwll.ch --- drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 2 +- include/drm/tinydrm/tinydrm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 4c6616278c48b..24a33bf862fa1 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -91,7 +91,7 @@ EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table); * GEM object state and frees the memory used to store the object itself using * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers - * can use this as their &drm_driver->gem_free_object callback. + * can use this as their &drm_driver->gem_free_object_unlocked callback. */ void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj) { diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h index 07a9a11fe19dc..77a93ec577fdf 100644 --- a/include/drm/tinydrm/tinydrm.h +++ b/include/drm/tinydrm/tinydrm.h @@ -41,7 +41,7 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe) * the &drm_driver structure. */ #define TINYDRM_GEM_DRIVER_OPS \ - .gem_free_object = tinydrm_gem_cma_free_object, \ + .gem_free_object_unlocked = tinydrm_gem_cma_free_object, \ .gem_print_info = drm_gem_cma_print_info, \ .gem_vm_ops = &drm_gem_cma_vm_ops, \ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ From 7056a2bccc3b5afc51f9b35b30a46f0d9219968d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 Mar 2018 16:19:32 +0200 Subject: [PATCH 007/122] drm/dp/mst: Fix off-by-one typo when dump payload table It seems there is a classical off-by-one typo from the beginning when commit ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") introduced a new helper. Fix a typo by introducing a macro constant. Cc: Dave Airlie Signed-off-by: Andy Shevchenko Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180319141932.37290-1-andriy.shevchenko@linux.intel.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 6fac4129e6a25..658830620ca36 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2941,12 +2941,14 @@ static void drm_dp_mst_dump_mstb(struct seq_file *m, } } +#define DP_PAYLOAD_TABLE_SIZE 64 + static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, char *buf) { int i; - for (i = 0; i < 64; i += 16) { + for (i = 0; i < DP_PAYLOAD_TABLE_SIZE; i += 16) { if (drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + i, &buf[i], 16) != 16) @@ -3015,7 +3017,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, mutex_lock(&mgr->lock); if (mgr->mst_primary) { - u8 buf[64]; + u8 buf[DP_PAYLOAD_TABLE_SIZE]; int ret; ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE); @@ -3033,8 +3035,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n", buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]); if (dump_dp_payload_table(mgr, buf)) - seq_printf(m, "payload table: %*ph\n", 63, buf); - + seq_printf(m, "payload table: %*ph\n", DP_PAYLOAD_TABLE_SIZE, buf); } mutex_unlock(&mgr->lock); From d1a9d710d12485710d424daaa2430fe93bc767f8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 27 Mar 2018 23:47:20 +0300 Subject: [PATCH 008/122] drm: prefer inline over __inline__ Remove last users of __inline__. Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180327204722.31246-1-jani.nikula@intel.com --- include/drm/drmP.h | 5 ++--- include/drm/drm_legacy.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c6666cd09347f..4bbef061c9c0c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -123,8 +123,7 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) #define DRM_SWITCH_POWER_CHANGING 2 #define DRM_SWITCH_POWER_DYNAMIC_OFF 3 -static __inline__ int drm_core_check_feature(struct drm_device *dev, - int feature) +static inline int drm_core_check_feature(struct drm_device *dev, int feature) { return ((dev->driver->driver_features & feature) ? 1 : 0); } @@ -143,7 +142,7 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev, /*@}*/ /* returns true if currently okay to sleep */ -static __inline__ bool drm_can_sleep(void) +static inline bool drm_can_sleep(void) { if (in_atomic() || in_dbg_master() || irqs_disabled()) return false; diff --git a/include/drm/drm_legacy.h b/include/drm/drm_legacy.h index cf0e7d89bcdfa..8fad66f88e4f3 100644 --- a/include/drm/drm_legacy.h +++ b/include/drm/drm_legacy.h @@ -194,8 +194,8 @@ void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev); void drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev); void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev); -static __inline__ struct drm_local_map *drm_legacy_findmap(struct drm_device *dev, - unsigned int token) +static inline struct drm_local_map *drm_legacy_findmap(struct drm_device *dev, + unsigned int token) { struct drm_map_list *_entry; list_for_each_entry(_entry, &dev->maplist, head) From 885a31cb6c752d5403adc6389894c27560fc6e6c Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 27 Mar 2018 23:47:21 +0300 Subject: [PATCH 009/122] drm: remove old documentation comment cruft from drmP.h Throw out the leftovers. Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180327204722.31246-2-jani.nikula@intel.com --- include/drm/drmP.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4bbef061c9c0c..b5d52a3d7d199 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -95,14 +95,6 @@ struct dma_buf_attachment; struct pci_dev; struct pci_controller; -/***********************************************************************/ -/** \name DRM template customization defaults */ -/*@{*/ - -/***********************************************************************/ -/** \name Internal types and structures */ -/*@{*/ - #define DRM_IF_VERSION(maj, min) (maj << 16 | min) /** @@ -128,19 +120,6 @@ static inline int drm_core_check_feature(struct drm_device *dev, int feature) return ((dev->driver->driver_features & feature) ? 1 : 0); } -/******************************************************************/ -/** \name Internal function definitions */ -/*@{*/ - - /* Driver support (drm_drv.h) */ - -/* - * These are exported to drivers so that they can implement fencing using - * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. - */ - -/*@}*/ - /* returns true if currently okay to sleep */ static inline bool drm_can_sleep(void) { From f4392860b4fe55d7d7cadaa64743c9b2466e4fd8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 27 Mar 2018 23:47:22 +0300 Subject: [PATCH 010/122] drm: make drm_core_check_feature() bool that it is Bool is the more appropriate return type here, use it. Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180327204722.31246-3-jani.nikula@intel.com --- include/drm/drmP.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b5d52a3d7d199..f5099c12c6a6d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -115,9 +115,9 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) #define DRM_SWITCH_POWER_CHANGING 2 #define DRM_SWITCH_POWER_DYNAMIC_OFF 3 -static inline int drm_core_check_feature(struct drm_device *dev, int feature) +static inline bool drm_core_check_feature(struct drm_device *dev, int feature) { - return ((dev->driver->driver_features & feature) ? 1 : 0); + return dev->driver->driver_features & feature; } /* returns true if currently okay to sleep */ From 49efffc7fbd48d5ea3d0dd60c218c7502d4a179d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:24 +0200 Subject: [PATCH 011/122] drm: Add drm_mode_config->normalize_zpos boolean Instead of drivers duplicating the drm_atomic_helper_check() code to be able to normalize the zpos they can use the normalize_zpos flag to let the drm core to do it. Signed-off-by: Peter Ujfalusi Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-2-peter.ujfalusi@ti.com --- drivers/gpu/drm/drm_atomic_helper.c | 11 +++++++++++ include/drm/drm_mode_config.h | 8 ++++++++ include/drm/drm_plane.h | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index c35654591c124..d63c806e7d38c 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -875,6 +875,11 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes); * functions depend upon an updated adjusted_mode.clock to e.g. properly compute * watermarks. * + * Note that zpos normalization will add all enable planes to the state which + * might not desired for some drivers. + * For example enable/disable of a cursor plane which have fixed zpos value + * would trigger all other enabled planes to be forced to the state change. + * * RETURNS: * Zero for success or -errno */ @@ -887,6 +892,12 @@ int drm_atomic_helper_check(struct drm_device *dev, if (ret) return ret; + if (dev->mode_config.normalize_zpos) { + ret = drm_atomic_normalize_zpos(dev, state); + if (ret) + return ret; + } + ret = drm_atomic_helper_check_planes(dev, state); if (ret) return ret; diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 7569f22ffef6b..33b3a96d66d0a 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -795,6 +795,14 @@ struct drm_mode_config { */ bool allow_fb_modifiers; + /** + * @normalize_zpos: + * + * If true the drm core will call drm_atomic_normalize_zpos() as part of + * atomic mode checking from drm_atomic_helper_check() + */ + bool normalize_zpos; + /** * @modifiers_property: Plane property to list support modifier/format * combination. diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index f7bf4a48b1c3d..d6da26d66a4b8 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -51,8 +51,8 @@ struct drm_modeset_acquire_ctx; * plane with a lower ID. * @normalized_zpos: normalized value of zpos: unique, range from 0 to N-1 * where N is the number of active planes for given crtc. Note that - * the driver must call drm_atomic_normalize_zpos() to update this before - * it can be trusted. + * the driver must set drm_mode_config.normalize_zpos or call + * drm_atomic_normalize_zpos() to update this before it can be trusted. * @src: clipped source coordinates of the plane (in 16.16) * @dst: clipped destination coordinates of the plane * @state: backpointer to global drm_atomic_state From a7da5cfe0cd6d36af6dc05ee4aa3e506c88e8f0e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:25 +0200 Subject: [PATCH 012/122] drm/exynos: Let core take care of normalizing the zpos Instead of re-implementing the drm_atomic_helper_check() locally with just adding drm_atomic_normalize_zpos() into it, set the drm_mode_config->normalize_zpos. Signed-off-by: Peter Ujfalusi CC: Inki Dae CC: Joonyoung Shim CC: Seung-Woo Kim CC: Kyungmin Park Acked-by: Daniel Vetter Acked-by: Inki Dae Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-3-peter.ujfalusi@ti.com --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 20 -------------------- drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 - drivers/gpu/drm/exynos/exynos_drm_fb.c | 4 +++- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index a518e9c6d6ccf..39284bb7c2c28 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -37,26 +37,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -int exynos_atomic_check(struct drm_device *dev, - struct drm_atomic_state *state) -{ - int ret; - - ret = drm_atomic_helper_check_modeset(dev, state); - if (ret) - return ret; - - ret = drm_atomic_normalize_zpos(dev, state); - if (ret) - return ret; - - ret = drm_atomic_helper_check_planes(dev, state); - if (ret) - return ret; - - return ret; -} - static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index df2262f70d916..075957cb6ba16 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -275,7 +275,6 @@ static inline int exynos_dpi_bind(struct drm_device *dev, int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock); -int exynos_atomic_check(struct drm_device *dev, struct drm_atomic_state *state); extern struct platform_driver fimd_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 0faaf829f5bf5..2379d732da673 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -206,7 +206,7 @@ static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = { static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, .output_poll_changed = drm_fb_helper_output_poll_changed, - .atomic_check = exynos_atomic_check, + .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -227,4 +227,6 @@ void exynos_drm_mode_config_init(struct drm_device *dev) dev->mode_config.helper_private = &exynos_drm_mode_config_helpers; dev->mode_config.allow_fb_modifiers = true; + + dev->mode_config.normalize_zpos = true; } From a18301b9f556101b4b87cd83b050c553652e91e4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:26 +0200 Subject: [PATCH 013/122] drm/tegra: Let core take care of normalizing the zpos Set the drm_mode_config->normalize_zpos and call the generic drm_atomic_helper_check() instead of duplicating it within tegra_atomic_check(). Call tegra_display_hub_atomic_check() after the drm_atomic_helpre_check() returned without error. Signed-off-by: Peter Ujfalusi CC: Thierry Reding Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-4-peter.ujfalusi@ti.com --- drivers/gpu/drm/tegra/drm.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index e20e013151f04..ac1121172dc95 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -38,26 +38,11 @@ static int tegra_atomic_check(struct drm_device *drm, { int err; - err = drm_atomic_helper_check_modeset(drm, state); + err = drm_atomic_helper_check(drm, state); if (err < 0) return err; - err = tegra_display_hub_atomic_check(drm, state); - if (err < 0) - return err; - - err = drm_atomic_normalize_zpos(drm, state); - if (err < 0) - return err; - - err = drm_atomic_helper_check_planes(drm, state); - if (err < 0) - return err; - - if (state->legacy_cursor_update) - state->async_update = !drm_atomic_helper_async_check(drm, state); - - return 0; + return tegra_display_hub_atomic_check(drm, state); } static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { @@ -151,6 +136,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm->mode_config.allow_fb_modifiers = true; + drm->mode_config.normalize_zpos = true; + drm->mode_config.funcs = &tegra_drm_mode_config_funcs; drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; From 352f9a8419df87b925ccc7fc56f1a75aa2290e93 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:27 +0200 Subject: [PATCH 014/122] drm/sti: Let core take care of normalizing the zpos Instead of re-implementing the drm_atomic_helper_check() locally with just adding drm_atomic_normalize_zpos() into it, set the drm_mode_config->normalize_zpos. Note: the drm_atomic_helper_check() now includes if (state->legacy_cursor_update) state->async_update = !drm_atomic_helper_async_check(drm, state); which was added after the driver moved away from using it (38d868e41c4b9250d5a115c049dc2d48f4909581 drm: Don't force all planes to be added to the state due to zpos) Signed-off-by: Peter Ujfalusi CC: Benjamin Gaignard CC: Vincent Abriou Acked-by: Daniel Vetter Acked-by: Benjamin Gaignard Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-5-peter.ujfalusi@ti.com --- drivers/gpu/drm/sti/sti_drv.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 55b6967d27e10..90c46b49c9315 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -119,30 +119,10 @@ static int sti_drm_dbg_init(struct drm_minor *minor) return ret; } -static int sti_atomic_check(struct drm_device *dev, - struct drm_atomic_state *state) -{ - int ret; - - ret = drm_atomic_helper_check_modeset(dev, state); - if (ret) - return ret; - - ret = drm_atomic_normalize_zpos(dev, state); - if (ret) - return ret; - - ret = drm_atomic_helper_check_planes(dev, state); - if (ret) - return ret; - - return ret; -} - static const struct drm_mode_config_funcs sti_mode_config_funcs = { .fb_create = drm_gem_fb_create, .output_poll_changed = drm_fb_helper_output_poll_changed, - .atomic_check = sti_atomic_check, + .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -160,6 +140,8 @@ static void sti_mode_config_init(struct drm_device *dev) dev->mode_config.max_height = STI_MAX_FB_HEIGHT; dev->mode_config.funcs = &sti_mode_config_funcs; + + dev->mode_config.normalize_zpos = true; } DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops); From 75def7785f4901b65a89dc99ea9506b1395242fa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:28 +0200 Subject: [PATCH 015/122] drm: rcar-du: Let core take care of normalizing the zpos Set the drm_mode_config->normalize_zpos and call drm_atomic_helper_check() from rcar_du_atomic_check() instead of re implementing the function locally. Signed-off-by: Peter Ujfalusi CC: Laurent Pinchart Acked-by: Daniel Vetter Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-6-peter.ujfalusi@ti.com --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 0329b354bfa08..ab59d2061e063 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -233,15 +233,7 @@ static int rcar_du_atomic_check(struct drm_device *dev, struct rcar_du_device *rcdu = dev->dev_private; int ret; - ret = drm_atomic_helper_check_modeset(dev, state); - if (ret) - return ret; - - ret = drm_atomic_normalize_zpos(dev, state); - if (ret) - return ret; - - ret = drm_atomic_helper_check_planes(dev, state); + ret = drm_atomic_helper_check(dev, state); if (ret) return ret; @@ -529,6 +521,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) dev->mode_config.min_height = 0; dev->mode_config.max_width = 4095; dev->mode_config.max_height = 2047; + dev->mode_config.normalize_zpos = true; dev->mode_config.funcs = &rcar_du_mode_config_funcs; dev->mode_config.helper_private = &rcar_du_mode_config_helper; From 23936ba940fbccf08f9f61d7c6d39ba0feb383bc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Mar 2018 12:20:29 +0200 Subject: [PATCH 016/122] drm/omap: Use normalized zpos for plane placement Planes with identical zpos value will result undefined behavior: disappearing planes, screen flickering and it is not supported by the hardware. Use normalized zpos to make sure that we don't encounter invalid configuration. Signed-off-by: Peter Ujfalusi CC: Tomi Valkeinen Acked-by: Daniel Vetter Reviewed-by: Tomi Valkeinen Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20180321102029.15248-7-peter.ujfalusi@ti.com --- drivers/gpu/drm/omapdrm/omap_drv.c | 3 +++ drivers/gpu/drm/omapdrm/omap_plane.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3632854c2b911..ef3b0e3571ec8 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -319,6 +319,9 @@ static int omap_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; + /* We want the zpos to be normalized */ + dev->mode_config.normalize_zpos = true; + dev->mode_config.funcs = &omap_mode_config_funcs; dev->mode_config.helper_private = &omap_mode_config_helper_funcs; diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2899435cad6e4..161233cbc9a0f 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -65,7 +65,7 @@ static void omap_plane_atomic_update(struct drm_plane *plane, info.rotation_type = OMAP_DSS_ROT_NONE; info.rotation = DRM_MODE_ROTATE_0; info.global_alpha = 0xff; - info.zorder = state->zpos; + info.zorder = state->normalized_zpos; /* update scanout: */ omap_framebuffer_update_scanout(state->fb, state, &info); From 606f1fc5cf2c27e86f2ea03d293e77fac789fae5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 27 Mar 2018 10:23:52 +0200 Subject: [PATCH 017/122] staging/vboxvideo: Use gem_free_object_unlocked vboxvideo doesn't use dev->struct_mutex and therefore has no need to use gem_free_object. Signed-off-by: Daniel Vetter Cc: Greg Kroah-Hartman Cc: Hans de Goede Cc: Michael Thayer Cc: Colin Ian King Cc: Daniel Vetter Cc: Stephen Rothwell Reviewed-by: Greg Kroah-Hartman Link: https://patchwork.freedesktop.org/patch/msgid/20180327082356.24516-1-daniel.vetter@ffwll.ch --- drivers/staging/vboxvideo/vbox_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/vboxvideo/vbox_drv.c b/drivers/staging/vboxvideo/vbox_drv.c index e18642e5027ed..f6d26beffa543 100644 --- a/drivers/staging/vboxvideo/vbox_drv.c +++ b/drivers/staging/vboxvideo/vbox_drv.c @@ -242,7 +242,7 @@ static struct drm_driver driver = { .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, - .gem_free_object = vbox_gem_free_object, + .gem_free_object_unlocked = vbox_gem_free_object, .dumb_create = vbox_dumb_create, .dumb_map_offset = vbox_dumb_mmap_offset, .dumb_destroy = drm_gem_dumb_destroy, From fcb1e57f79c0ecf4c85fecd2294a469367cbddd0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 27 Mar 2018 10:23:53 +0200 Subject: [PATCH 018/122] drm/rockchip: fixup comment for gem_free_object_unlocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just want to clean out all grep hits. gem_free_object is deprecated. Signed-off-by: Daniel Vetter Cc: Sandy Huang Cc: "Heiko Stübner" Cc: linux-arm-kernel@lists.infradead.org Cc: linux-rockchip@lists.infradead.org Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20180327082356.24516-2-daniel.vetter@ffwll.ch --- drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 074db7a928095..a8db758d523e9 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -357,8 +357,8 @@ rockchip_gem_create_object(struct drm_device *drm, unsigned int size, } /* - * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback - * function + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object_unlocked + * callback function */ void rockchip_gem_free_object(struct drm_gem_object *obj) { From ae358dacd217370cc362f1674712c4e9246ace8d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 27 Mar 2018 10:23:54 +0200 Subject: [PATCH 019/122] drm/udl: Get rid of dev->struct_mutex usage It's only used to protect our page list, and only when we know we have a full reference. This means none of these code paths can ever race with the final unref, and hence we do not need dev->struct_mutex serialization and can simply switch to our own locking. For more context the only magic the locked gem_free_object provides is that it prevents concurrent final unref (and destruction) of gem objects while anyone is holding dev->struct_mutex. This was used by i915 (and other drivers) to implement eviction handling with less headaches. Signed-off-by: Daniel Vetter Cc: Dave Airlie Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20180327082356.24516-3-daniel.vetter@ffwll.ch --- drivers/gpu/drm/udl/udl_dmabuf.c | 5 +++-- drivers/gpu/drm/udl/udl_drv.c | 2 +- drivers/gpu/drm/udl/udl_drv.h | 2 ++ drivers/gpu/drm/udl/udl_gem.c | 5 +++-- drivers/gpu/drm/udl/udl_main.c | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_dmabuf.c b/drivers/gpu/drm/udl/udl_dmabuf.c index 2867ed155ff6d..0a20695eb120e 100644 --- a/drivers/gpu/drm/udl/udl_dmabuf.c +++ b/drivers/gpu/drm/udl/udl_dmabuf.c @@ -76,6 +76,7 @@ static struct sg_table *udl_map_dma_buf(struct dma_buf_attachment *attach, struct udl_drm_dmabuf_attachment *udl_attach = attach->priv; struct udl_gem_object *obj = to_udl_bo(attach->dmabuf->priv); struct drm_device *dev = obj->base.dev; + struct udl_device *udl = dev->dev_private; struct scatterlist *rd, *wr; struct sg_table *sgt = NULL; unsigned int i; @@ -112,7 +113,7 @@ static struct sg_table *udl_map_dma_buf(struct dma_buf_attachment *attach, return ERR_PTR(-ENOMEM); } - mutex_lock(&dev->struct_mutex); + mutex_lock(&udl->gem_lock); rd = obj->sg->sgl; wr = sgt->sgl; @@ -137,7 +138,7 @@ static struct sg_table *udl_map_dma_buf(struct dma_buf_attachment *attach, attach->priv = udl_attach; err_unlock: - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&udl->gem_lock); return sgt; } diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 3c45a30647265..9ef515df724b1 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -53,7 +53,7 @@ static struct drm_driver driver = { .unload = udl_driver_unload, /* gem hooks */ - .gem_free_object = udl_gem_free_object, + .gem_free_object_unlocked = udl_gem_free_object, .gem_vm_ops = &udl_gem_vm_ops, .dumb_create = udl_dumb_create, diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 2a75ab80527a9..55c0cc3091981 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -54,6 +54,8 @@ struct udl_device { struct usb_device *udev; struct drm_crtc *crtc; + struct mutex gem_lock; + int sku_pixel_limit; struct urb_list urbs; diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index dee6bd9a3dd1b..9a15cce22ccee 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -214,9 +214,10 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, { struct udl_gem_object *gobj; struct drm_gem_object *obj; + struct udl_device *udl = dev->dev_private; int ret = 0; - mutex_lock(&dev->struct_mutex); + mutex_lock(&udl->gem_lock); obj = drm_gem_object_lookup(file, handle); if (obj == NULL) { ret = -ENOENT; @@ -236,6 +237,6 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, out: drm_gem_object_put(&gobj->base); unlock: - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&udl->gem_lock); return ret; } diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index f1ec4528a73e7..d518de8f496bc 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -324,6 +324,8 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) udl->ddev = dev; dev->dev_private = udl; + mutex_init(&udl->gem_lock); + if (!udl_parse_vendor_descriptor(dev, udl->udev)) { ret = -ENODEV; DRM_ERROR("firmware not recognized. Assume incompatible device\n"); From 0c9c7fd00e17907efb35697ecb9f2df39a0b536c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 22:27:37 +0200 Subject: [PATCH 020/122] drm/simple-kms-helper: Plumb plane state to the enable hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tinydrm enable hook wants to play around with the new fb in .atomic_enable(), thus we'll need access to the plane state. Performed with coccinelle: @r1@ identifier F =~ ".*enable$"; identifier P, CS; @@ F( struct drm_simple_display_pipe *P ,struct drm_crtc_state *CS + ,struct drm_plane_state *plane_state ) { ... } @@ struct drm_simple_display_pipe *P; expression E; @@ { + struct drm_plane *plane; ... + plane = &P->plane; P->funcs->enable(P ,E + ,plane->state ); ... } @@ identifier P, CS; @@ struct drm_simple_display_pipe_funcs { ... void (*enable)(struct drm_simple_display_pipe *P ,struct drm_crtc_state *CS + ,struct drm_plane_state *plane_state ); ... }; v2: Pimp the commit message (David) Cc: Marek Vasut Cc: Eric Anholt Cc: David Lechner Cc: "Noralf Trønnes" Cc: Linus Walleij Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322202738.25817-1-ville.syrjala@linux.intel.com Reviewed-by: Noralf Trønnes --- drivers/gpu/drm/drm_simple_kms_helper.c | 4 +++- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 ++- drivers/gpu/drm/pl111/pl111_display.c | 3 ++- drivers/gpu/drm/tinydrm/ili9225.c | 3 ++- drivers/gpu/drm/tinydrm/mi0283qt.c | 3 ++- drivers/gpu/drm/tinydrm/repaper.c | 3 ++- drivers/gpu/drm/tinydrm/st7586.c | 3 ++- drivers/gpu/drm/tinydrm/st7735r.c | 3 ++- drivers/gpu/drm/tve200/tve200_display.c | 3 ++- include/drm/drm_simple_kms_helper.h | 3 ++- 10 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 987a353c7f720..7a00455ca568b 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -64,13 +64,15 @@ static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { + struct drm_plane *plane; struct drm_simple_display_pipe *pipe; pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); if (!pipe->funcs || !pipe->funcs->enable) return; - pipe->funcs->enable(pipe, crtc->state); + plane = &pipe->plane; + pipe->funcs->enable(pipe, crtc->state, plane->state); } static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 5cae8db9dcd48..b9c7507813dbe 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -99,7 +99,8 @@ static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = { }; static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index 3106464279071..1fee578e05b05 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -120,7 +120,8 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe, } static void pl111_display_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *cstate) + struct drm_crtc_state *cstate, + struct drm_plane_state *plane_state) { struct drm_crtc *crtc = &pipe->crtc; struct drm_plane *plane = &pipe->plane; diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c index a0759502b81ac..089d22798c8bb 100644 --- a/drivers/gpu/drm/tinydrm/ili9225.c +++ b/drivers/gpu/drm/tinydrm/ili9225.c @@ -176,7 +176,8 @@ static const struct drm_framebuffer_funcs ili9225_fb_funcs = { }; static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index d8ed6e6f8e059..82ad9b61898ed 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -49,7 +49,8 @@ #define ILI9341_MADCTL_MY BIT(7) static void mi0283qt_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c index 75740630c4106..33b4a71916e47 100644 --- a/drivers/gpu/drm/tinydrm/repaper.c +++ b/drivers/gpu/drm/tinydrm/repaper.c @@ -659,7 +659,8 @@ static void power_off(struct repaper_epd *epd) } static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); struct repaper_epd *epd = epd_from_tinydrm(tdev); diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index a6396ef9cc4ae..bb08b293c8ce5 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -175,7 +175,8 @@ static const struct drm_framebuffer_funcs st7586_fb_funcs = { }; static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c index 67d197ecfc4b0..19b28f8c78dbe 100644 --- a/drivers/gpu/drm/tinydrm/st7735r.c +++ b/drivers/gpu/drm/tinydrm/st7735r.c @@ -37,7 +37,8 @@ #define ST7735R_MV BIT(5) static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); diff --git a/drivers/gpu/drm/tve200/tve200_display.c b/drivers/gpu/drm/tve200/tve200_display.c index db397fcb345a5..108f3b2b5d25b 100644 --- a/drivers/gpu/drm/tve200/tve200_display.c +++ b/drivers/gpu/drm/tve200/tve200_display.c @@ -120,7 +120,8 @@ static int tve200_display_check(struct drm_simple_display_pipe *pipe, } static void tve200_display_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *cstate) + struct drm_crtc_state *cstate, + struct drm_plane_state *plane_state) { struct drm_crtc *crtc = &pipe->crtc; struct drm_plane *plane = &pipe->plane; diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 1b4e352143fd7..b027937423179 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -64,7 +64,8 @@ struct drm_simple_display_pipe_funcs { * This hook is optional. */ void (*enable)(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state); + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state); /** * @disable: * From e85d30060eddccfcfbf7fdbd61a23cfbda05cc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 23 Mar 2018 17:35:09 +0200 Subject: [PATCH 021/122] drm/tinydrm: Make fb_dirty into a lower level hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mipi_dbi_enable_flush() wants to call the fb->dirty() hook from the bowels of the .atomic_enable() hook. That prevents us from taking the plane mutex in fb->dirty() unless we also plumb down the acquire context. Instead it seems simpler to split the fb->dirty() into a tinydrm specific lower level hook that can be called from mipi_dbi_enable_flush() and from a generic higher level tinydrm_fb_dirty() helper. As we don't have a tinydrm specific vfuncs table we'll just stick it into tinydrm_device directly for now. v2: Deal with the fb->dirty() in tinydrm_display_pipe_update() as well (Noralf) Cc: "Noralf Trønnes" Cc: David Lechner Signed-off-by: Ville Syrjälä Reviewed-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/20180323153509.15287-1-ville.syrjala@linux.intel.com Reviewed-by: Noralf Trønnes Tested-by: Noralf Trønnes --- .../gpu/drm/tinydrm/core/tinydrm-helpers.c | 30 +++++++++++++++++++ drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c | 5 ++-- drivers/gpu/drm/tinydrm/ili9225.c | 23 ++++---------- drivers/gpu/drm/tinydrm/mi0283qt.c | 2 +- drivers/gpu/drm/tinydrm/mipi-dbi.c | 30 +++++++------------ drivers/gpu/drm/tinydrm/repaper.c | 28 ++++++----------- drivers/gpu/drm/tinydrm/st7586.c | 23 ++++---------- drivers/gpu/drm/tinydrm/st7735r.c | 2 +- include/drm/tinydrm/mipi-dbi.h | 4 ++- include/drm/tinydrm/tinydrm-helpers.h | 5 ++++ include/drm/tinydrm/tinydrm.h | 4 +++ 11 files changed, 78 insertions(+), 78 deletions(-) diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c index d1c3ce9ab2943..dcd390163a4a9 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c @@ -78,6 +78,36 @@ bool tinydrm_merge_clips(struct drm_clip_rect *dst, } EXPORT_SYMBOL(tinydrm_merge_clips); +int tinydrm_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct tinydrm_device *tdev = fb->dev->dev_private; + struct drm_plane *plane = &tdev->pipe.plane; + int ret = 0; + + drm_modeset_lock(&plane->mutex, NULL); + + /* fbdev can flush even when we're not interested */ + if (plane->state->fb == fb) { + mutex_lock(&tdev->dirty_lock); + ret = tdev->fb_dirty(fb, file_priv, flags, + color, clips, num_clips); + mutex_unlock(&tdev->dirty_lock); + } + + drm_modeset_unlock(&plane->mutex); + + if (ret) + dev_err_once(fb->dev->dev, + "Failed to update display %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(tinydrm_fb_dirty); + /** * tinydrm_memcpy - Copy clip buffer * @dst: Destination buffer diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c index 11ae950b0fc94..e68b528ae64d9 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c @@ -125,9 +125,8 @@ void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_crtc *crtc = &tdev->pipe.crtc; if (fb && (fb != old_state->fb)) { - pipe->plane.fb = fb; - if (fb->funcs->dirty) - fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); + if (tdev->fb_dirty) + tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0); } if (crtc->state->event) { diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c index 089d22798c8bb..0874e877b1119 100644 --- a/drivers/gpu/drm/tinydrm/ili9225.c +++ b/drivers/gpu/drm/tinydrm/ili9225.c @@ -88,14 +88,8 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb, bool full; void *tr; - mutex_lock(&tdev->dirty_lock); - if (!mipi->enabled) - goto out_unlock; - - /* fbdev can flush even when we're not interested */ - if (tdev->pipe.plane.fb != fb) - goto out_unlock; + return 0; full = tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width, fb->height); @@ -108,7 +102,7 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb, tr = mipi->tx_buf; ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap); if (ret) - goto out_unlock; + return ret; } else { tr = cma_obj->vaddr; } @@ -159,20 +153,13 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb, ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr, (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); -out_unlock: - mutex_unlock(&tdev->dirty_lock); - - if (ret) - dev_err_once(fb->dev->dev, "Failed to update display %d\n", - ret); - return ret; } static const struct drm_framebuffer_funcs ili9225_fb_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, - .dirty = ili9225_fb_dirty, + .dirty = tinydrm_fb_dirty, }; static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, @@ -269,7 +256,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017); - mipi_dbi_enable_flush(mipi); + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); } static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -342,6 +329,8 @@ static int ili9225_init(struct device *dev, struct mipi_dbi *mipi, if (ret) return ret; + tdev->fb_dirty = ili9225_fb_dirty; + ret = tinydrm_display_pipe_init(tdev, pipe_funcs, DRM_MODE_CONNECTOR_VIRTUAL, ili9225_formats, diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index 82ad9b61898ed..4e6d2ee94e556 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -127,7 +127,7 @@ static void mi0283qt_enable(struct drm_simple_display_pipe *pipe, msleep(100); out_enable: - mipi_dbi_enable_flush(mipi); + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); } static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = { diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index 9e903812b5731..4d1fb31a781ff 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -219,14 +219,8 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, bool full; void *tr; - mutex_lock(&tdev->dirty_lock); - if (!mipi->enabled) - goto out_unlock; - - /* fbdev can flush even when we're not interested */ - if (tdev->pipe.plane.fb != fb) - goto out_unlock; + return 0; full = tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width, fb->height); @@ -239,7 +233,7 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, tr = mipi->tx_buf; ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap); if (ret) - goto out_unlock; + return ret; } else { tr = cma_obj->vaddr; } @@ -254,20 +248,13 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr, (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); -out_unlock: - mutex_unlock(&tdev->dirty_lock); - - if (ret) - dev_err_once(fb->dev->dev, "Failed to update display %d\n", - ret); - return ret; } static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, - .dirty = mipi_dbi_fb_dirty, + .dirty = tinydrm_fb_dirty, }; /** @@ -278,13 +265,16 @@ static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = { * enables the backlight. Drivers can use this in their * &drm_simple_display_pipe_funcs->enable callback. */ -void mipi_dbi_enable_flush(struct mipi_dbi *mipi) +void mipi_dbi_enable_flush(struct mipi_dbi *mipi, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { - struct drm_framebuffer *fb = mipi->tinydrm.pipe.plane.fb; + struct tinydrm_device *tdev = &mipi->tinydrm; + struct drm_framebuffer *fb = plane_state->fb; mipi->enabled = true; if (fb) - fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); + tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0); backlight_enable(mipi->backlight); } @@ -381,6 +371,8 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi, if (ret) return ret; + tdev->fb_dirty = mipi_dbi_fb_dirty; + /* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */ ret = tinydrm_display_pipe_init(tdev, pipe_funcs, DRM_MODE_CONNECTOR_VIRTUAL, diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c index 33b4a71916e47..bb6f80a81899c 100644 --- a/drivers/gpu/drm/tinydrm/repaper.c +++ b/drivers/gpu/drm/tinydrm/repaper.c @@ -540,14 +540,8 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, clip.y1 = 0; clip.y2 = fb->height; - mutex_lock(&tdev->dirty_lock); - if (!epd->enabled) - goto out_unlock; - - /* fbdev can flush even when we're not interested */ - if (tdev->pipe.plane.fb != fb) - goto out_unlock; + return 0; repaper_get_temperature(epd); @@ -555,16 +549,14 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, epd->factored_stage_time); buf = kmalloc(fb->width * fb->height, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto out_unlock; - } + if (!buf) + return -ENOMEM; if (import_attach) { ret = dma_buf_begin_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE); if (ret) - goto out_unlock; + goto out_free; } tinydrm_xrgb8888_to_gray8(buf, cma_obj->vaddr, fb, &clip); @@ -573,7 +565,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, ret = dma_buf_end_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE); if (ret) - goto out_unlock; + goto out_free; } repaper_gray8_to_mono_reversed(buf, fb->width, fb->height); @@ -625,11 +617,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, } } -out_unlock: - mutex_unlock(&tdev->dirty_lock); - - if (ret) - DRM_DEV_ERROR(fb->dev->dev, "Failed to update display (%d)\n", ret); +out_free: kfree(buf); return ret; @@ -638,7 +626,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, static const struct drm_framebuffer_funcs repaper_fb_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, - .dirty = repaper_fb_dirty, + .dirty = tinydrm_fb_dirty, }; static void power_off(struct repaper_epd *epd) @@ -1070,6 +1058,8 @@ static int repaper_probe(struct spi_device *spi) if (ret) return ret; + tdev->fb_dirty = repaper_fb_dirty; + ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs, DRM_MODE_CONNECTOR_VIRTUAL, repaper_formats, diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index bb08b293c8ce5..22644b88199a8 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -120,14 +120,8 @@ static int st7586_fb_dirty(struct drm_framebuffer *fb, int start, end; int ret = 0; - mutex_lock(&tdev->dirty_lock); - if (!mipi->enabled) - goto out_unlock; - - /* fbdev can flush even when we're not interested */ - if (tdev->pipe.plane.fb != fb) - goto out_unlock; + return 0; tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width, fb->height); @@ -141,7 +135,7 @@ static int st7586_fb_dirty(struct drm_framebuffer *fb, ret = st7586_buf_copy(mipi->tx_buf, fb, &clip); if (ret) - goto out_unlock; + return ret; /* Pixels are packed 3 per byte */ start = clip.x1 / 3; @@ -158,20 +152,13 @@ static int st7586_fb_dirty(struct drm_framebuffer *fb, (u8 *)mipi->tx_buf, (end - start) * (clip.y2 - clip.y1)); -out_unlock: - mutex_unlock(&tdev->dirty_lock); - - if (ret) - dev_err_once(fb->dev->dev, "Failed to update display %d\n", - ret); - return ret; } static const struct drm_framebuffer_funcs st7586_fb_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, - .dirty = st7586_fb_dirty, + .dirty = tinydrm_fb_dirty, }; static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, @@ -238,7 +225,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON); - mipi_dbi_enable_flush(mipi); + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); } static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -278,6 +265,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi, if (ret) return ret; + tdev->fb_dirty = st7586_fb_dirty; + ret = tinydrm_display_pipe_init(tdev, pipe_funcs, DRM_MODE_CONNECTOR_VIRTUAL, st7586_formats, diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c index 19b28f8c78dbe..189a07894d368 100644 --- a/drivers/gpu/drm/tinydrm/st7735r.c +++ b/drivers/gpu/drm/tinydrm/st7735r.c @@ -99,7 +99,7 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe, msleep(20); - mipi_dbi_enable_flush(mipi); + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); } static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = { diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h index 44e824af2ef6c..b8ba588619867 100644 --- a/include/drm/tinydrm/mipi-dbi.h +++ b/include/drm/tinydrm/mipi-dbi.h @@ -67,7 +67,9 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi, const struct drm_simple_display_pipe_funcs *pipe_funcs, struct drm_driver *driver, const struct drm_display_mode *mode, unsigned int rotation); -void mipi_dbi_enable_flush(struct mipi_dbi *mipi); +void mipi_dbi_enable_flush(struct mipi_dbi *mipi, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plan_state); void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe); void mipi_dbi_hw_reset(struct mipi_dbi *mipi); bool mipi_dbi_display_is_on(struct mipi_dbi *mipi); diff --git a/include/drm/tinydrm/tinydrm-helpers.h b/include/drm/tinydrm/tinydrm-helpers.h index 0a4ddbc04c603..5b96f0b12c8ce 100644 --- a/include/drm/tinydrm/tinydrm-helpers.h +++ b/include/drm/tinydrm/tinydrm-helpers.h @@ -36,6 +36,11 @@ static inline bool tinydrm_machine_little_endian(void) bool tinydrm_merge_clips(struct drm_clip_rect *dst, struct drm_clip_rect *src, unsigned int num_clips, unsigned int flags, u32 max_width, u32 max_height); +int tinydrm_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips); void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_clip_rect *clip); void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h index 77a93ec577fdf..6e2b960e25eb2 100644 --- a/include/drm/tinydrm/tinydrm.h +++ b/include/drm/tinydrm/tinydrm.h @@ -26,6 +26,10 @@ struct tinydrm_device { struct drm_simple_display_pipe pipe; struct mutex dirty_lock; const struct drm_framebuffer_funcs *fb_funcs; + int (*fb_dirty)(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, + unsigned color, struct drm_clip_rect *clips, + unsigned num_clips); }; static inline struct tinydrm_device * From 790861cc34f872015806cef311d5c64cc3167a0d Mon Sep 17 00:00:00 2001 From: Samuel Li Date: Mon, 26 Mar 2018 15:22:25 -0400 Subject: [PATCH 022/122] drm: add parameter explanation for some gem dmabuf_ops To reduce some warnings. Reviewed-by: Alex Deucher Signed-off-by: Samuel Li Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/1522092145-12645-1-git-send-email-Samuel.Li@amd.com --- drivers/gpu/drm/drm_prime.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 7856a9b3f8a8c..caf675e3e692c 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -331,6 +331,9 @@ EXPORT_SYMBOL(drm_gem_map_dma_buf); /** * drm_gem_unmap_dma_buf - unmap_dma_buf implementation for GEM + * @attach: attachment to unmap buffer from + * @sgt: scatterlist info of the buffer to unmap + * @dir: direction of DMA transfer * * Not implemented. The unmap is done at drm_gem_map_detach(). This can be * used as the &dma_buf_ops.unmap_dma_buf callback. @@ -429,6 +432,8 @@ EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); /** * drm_gem_dmabuf_kmap_atomic - map_atomic implementation for GEM + * @dma_buf: buffer to be mapped + * @page_num: page number within the buffer * * Not implemented. This can be used as the &dma_buf_ops.map_atomic callback. */ @@ -441,6 +446,9 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kmap_atomic); /** * drm_gem_dmabuf_kunmap_atomic - unmap_atomic implementation for GEM + * @dma_buf: buffer to be unmapped + * @page_num: page number within the buffer + * @addr: virtual address of the buffer * * Not implemented. This can be used as the &dma_buf_ops.unmap_atomic callback. */ @@ -453,6 +461,8 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kunmap_atomic); /** * drm_gem_dmabuf_kmap - map implementation for GEM + * @dma_buf: buffer to be mapped + * @page_num: page number within the buffer * * Not implemented. This can be used as the &dma_buf_ops.map callback. */ @@ -464,6 +474,9 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kmap); /** * drm_gem_dmabuf_kunmap - unmap implementation for GEM + * @dma_buf: buffer to be unmapped + * @page_num: page number within the buffer + * @addr: virtual address of the buffer * * Not implemented. This can be used as the &dma_buf_ops.unmap callback. */ From bee330f3d67273a68dcb99f59480d59553c008b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Wed, 28 Mar 2018 10:38:35 +0300 Subject: [PATCH 023/122] drm: Use srcu to protect drm_device.unplugged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use srcu to protect drm_device.unplugged in a race free manner. Drivers can use drm_dev_enter()/drm_dev_exit() to protect and mark sections preventing access to device resources that are not available after the device is gone. Suggested-by: Daniel Vetter Signed-off-by: Noralf Trønnes Signed-off-by: Oleksandr Andrushchenko Reviewed-by: Oleksandr Andrushchenko Tested-by: Oleksandr Andrushchenko Cc: intel-gfx@lists.freedesktop.org Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1522222715-11814-1-git-send-email-andr2000@gmail.com --- drivers/gpu/drm/drm_drv.c | 54 +++++++++++++++++++++++++++++++++++---- include/drm/drm_device.h | 9 ++++++- include/drm/drm_drv.h | 15 ++++++++--- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index a1b9338736e3b..32a83b41ab61e 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,8 @@ static bool drm_core_init_complete = false; static struct dentry *drm_debugfs_root; +DEFINE_STATIC_SRCU(drm_unplug_srcu); + /* * DRM Minors * A DRM device can provide several char-dev interfaces on the DRM-Major. Each @@ -318,18 +321,51 @@ void drm_put_dev(struct drm_device *dev) } EXPORT_SYMBOL(drm_put_dev); -static void drm_device_set_unplugged(struct drm_device *dev) +/** + * drm_dev_enter - Enter device critical section + * @dev: DRM device + * @idx: Pointer to index that will be passed to the matching drm_dev_exit() + * + * This function marks and protects the beginning of a section that should not + * be entered after the device has been unplugged. The section end is marked + * with drm_dev_exit(). Calls to this function can be nested. + * + * Returns: + * True if it is OK to enter the section, false otherwise. + */ +bool drm_dev_enter(struct drm_device *dev, int *idx) +{ + *idx = srcu_read_lock(&drm_unplug_srcu); + + if (dev->unplugged) { + srcu_read_unlock(&drm_unplug_srcu, *idx); + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_dev_enter); + +/** + * drm_dev_exit - Exit device critical section + * @idx: index returned from drm_dev_enter() + * + * This function marks the end of a section that should not be entered after + * the device has been unplugged. + */ +void drm_dev_exit(int idx) { - smp_wmb(); - atomic_set(&dev->unplugged, 1); + srcu_read_unlock(&drm_unplug_srcu, idx); } +EXPORT_SYMBOL(drm_dev_exit); /** * drm_dev_unplug - unplug a DRM device * @dev: DRM device * * This unplugs a hotpluggable DRM device, which makes it inaccessible to - * userspace operations. Entry-points can use drm_dev_is_unplugged(). This + * userspace operations. Entry-points can use drm_dev_enter() and + * drm_dev_exit() to protect device resources in a race free manner. This * essentially unregisters the device like drm_dev_unregister(), but can be * called while there are still open users of @dev. */ @@ -338,10 +374,18 @@ void drm_dev_unplug(struct drm_device *dev) drm_dev_unregister(dev); mutex_lock(&drm_global_mutex); - drm_device_set_unplugged(dev); if (dev->open_count == 0) drm_dev_put(dev); mutex_unlock(&drm_global_mutex); + + /* + * After synchronizing any critical read section is guaranteed to see + * the new value of ->unplugged, and any critical section which might + * still have seen the old value of ->unplugged is guaranteed to have + * finished. + */ + dev->unplugged = true; + synchronize_srcu(&drm_unplug_srcu); } EXPORT_SYMBOL(drm_dev_unplug); diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 7c4fa32f3fc6f..3a0eac2885b79 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -46,7 +46,14 @@ struct drm_device { /* currently active master for this device. Protected by master_mutex */ struct drm_master *master; - atomic_t unplugged; /**< Flag whether dev is dead */ + /** + * @unplugged: + * + * Flag to tell if the device has been unplugged. + * See drm_dev_enter() and drm_dev_is_unplugged(). + */ + bool unplugged; + struct inode *anon_inode; /**< inode for private address-space */ char *unique; /**< unique name of the device */ /*@} */ diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index d32b688eb346c..ff7312c40cd8f 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -623,6 +623,8 @@ void drm_dev_get(struct drm_device *dev); void drm_dev_put(struct drm_device *dev); void drm_dev_unref(struct drm_device *dev); void drm_put_dev(struct drm_device *dev); +bool drm_dev_enter(struct drm_device *dev, int *idx); +void drm_dev_exit(int idx); void drm_dev_unplug(struct drm_device *dev); /** @@ -634,11 +636,16 @@ void drm_dev_unplug(struct drm_device *dev); * unplugged, these two functions guarantee that any store before calling * drm_dev_unplug() is visible to callers of this function after it completes */ -static inline int drm_dev_is_unplugged(struct drm_device *dev) +static inline bool drm_dev_is_unplugged(struct drm_device *dev) { - int ret = atomic_read(&dev->unplugged); - smp_rmb(); - return ret; + int idx; + + if (drm_dev_enter(dev, &idx)) { + drm_dev_exit(idx); + return false; + } + + return true; } From 5e9cfeba6abb7e1a3f240bd24eb29178f0b83716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:51 +0200 Subject: [PATCH 024/122] drm/atomic-helper: Drop plane->fb references only for drm_atomic_helper_shutdown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_atomic_helper_shutdown() needs to release the reference held by plane->fb. Since commit 49d70aeaeca8 ("drm/atomic-helper: Fix leak in disable_all") we're doing that by calling drm_atomic_clean_old_fb() in drm_atomic_helper_disable_all(). This also leaves plane->fb == NULL afterwards. However, since drm_atomic_helper_disable_all() is also used by the i915 gpu reset code drm_atomic_helper_commit_duplicated_state() then has to undo the damage and put the correct plane->fb pointers back in (and also adjust the ref counts to match again as well). That approach doesn't work so well for load detection as nothing sets up the plane->old_fb pointers for us. This causes us to leak an extra reference for each plane->fb when drm_atomic_helper_commit_duplicated_state() calls drm_atomic_clean_old_fb() after load detection. To fix this let's call drm_atomic_clean_old_fb() only for drm_atomic_helper_shutdown() as that's the only time we need to actually drop the plane->fb references. In all the other cases (load detection, gpu reset) we want to leave plane->fb alone. v2: Don't inflict the clean_old_fbs bool to drivers (Daniel) v3: Squash in the revert and rewrite the commit msg (Daniel) Cc: martin.peres@free.fr Cc: chris@chris-wilson.co.uk Cc: Dave Airlie Cc: Maarten Lankhorst Cc: Daniel Vetter Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-3-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst #pre-squash Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 78 ++++++++++++++--------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d63c806e7d38c..ef4ddfecc4a4b 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2892,31 +2892,9 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set, return 0; } -/** - * drm_atomic_helper_disable_all - disable all currently active outputs - * @dev: DRM device - * @ctx: lock acquisition context - * - * Loops through all connectors, finding those that aren't turned off and then - * turns them off by setting their DPMS mode to OFF and deactivating the CRTC - * that they are connected to. - * - * This is used for example in suspend/resume to disable all currently active - * functions when suspending. If you just want to shut down everything at e.g. - * driver unload, look at drm_atomic_helper_shutdown(). - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * 0 on success or a negative error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and - * drm_atomic_helper_shutdown(). - */ -int drm_atomic_helper_disable_all(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) +static int __drm_atomic_helper_disable_all(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx, + bool clean_old_fbs) { struct drm_atomic_state *state; struct drm_connector_state *conn_state; @@ -2968,8 +2946,11 @@ int drm_atomic_helper_disable_all(struct drm_device *dev, goto free; drm_atomic_set_fb_for_plane(plane_state, NULL); - plane_mask |= BIT(drm_plane_index(plane)); - plane->old_fb = plane->fb; + + if (clean_old_fbs) { + plane->old_fb = plane->fb; + plane_mask |= BIT(drm_plane_index(plane)); + } } ret = drm_atomic_commit(state); @@ -2980,6 +2961,34 @@ int drm_atomic_helper_disable_all(struct drm_device *dev, return ret; } +/** + * drm_atomic_helper_disable_all - disable all currently active outputs + * @dev: DRM device + * @ctx: lock acquisition context + * + * Loops through all connectors, finding those that aren't turned off and then + * turns them off by setting their DPMS mode to OFF and deactivating the CRTC + * that they are connected to. + * + * This is used for example in suspend/resume to disable all currently active + * functions when suspending. If you just want to shut down everything at e.g. + * driver unload, look at drm_atomic_helper_shutdown(). + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * 0 on success or a negative error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and + * drm_atomic_helper_shutdown(). + */ +int drm_atomic_helper_disable_all(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + return __drm_atomic_helper_disable_all(dev, ctx, false); +} EXPORT_SYMBOL(drm_atomic_helper_disable_all); /** @@ -3002,7 +3011,7 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) while (1) { ret = drm_modeset_lock_all_ctx(dev, &ctx); if (!ret) - ret = drm_atomic_helper_disable_all(dev, &ctx); + ret = __drm_atomic_helper_disable_all(dev, &ctx, true); if (ret != -EDEADLK) break; @@ -3106,16 +3115,11 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, struct drm_connector_state *new_conn_state; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; - unsigned plane_mask = 0; - struct drm_device *dev = state->dev; - int ret; state->acquire_ctx = ctx; - for_each_new_plane_in_state(state, plane, new_plane_state, i) { - plane_mask |= BIT(drm_plane_index(plane)); + for_each_new_plane_in_state(state, plane, new_plane_state, i) state->planes[i].old_state = plane->state; - } for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) state->crtcs[i].old_state = crtc->state; @@ -3123,11 +3127,7 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, for_each_new_connector_in_state(state, connector, new_conn_state, i) state->connectors[i].old_state = connector->state; - ret = drm_atomic_commit(state); - if (plane_mask) - drm_atomic_clean_old_fb(dev, plane_mask, ret); - - return ret; + return drm_atomic_commit(state); } EXPORT_SYMBOL(drm_atomic_helper_commit_duplicated_state); From 7e7de761af2e4760cc5ad2968c37c2814317ede6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:53 +0200 Subject: [PATCH 025/122] drm: Clear crtc->primary->crtc when disabling the crtc via setcrtc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the primary->crtc in sync with the state->crtc (also with primary->fb and state->fb) when disabling the crtc (and thus also the primary) via setcrtc(). Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-4-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 03583887cfec3..7a973ada7195f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -471,7 +471,7 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, ret = crtc->funcs->set_config(set, ctx); if (ret == 0) { - crtc->primary->crtc = crtc; + crtc->primary->crtc = fb ? crtc : NULL; crtc->primary->fb = fb; } From 5e78d01fa1a7f1794dfc44a6eb77bfec7a8d590d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:54 +0200 Subject: [PATCH 026/122] drm/atomic-helper: WARN if legacy plane fb pointers are bogus when committing duplicated state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_atomic_helper_commit_duplicated_state() should only be called resume/reset/load_detect paths where plane->old_fb should always be NULL and plane->fb should be equal to the new_plane_state->fb. Assert that is indeed the case. Cc: martin.peres@free.fr Cc: chris@chris-wilson.co.uk Cc: Dave Airlie (v1) Cc: Maarten Lankhorst Cc: Daniel Vetter Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-5-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_atomic_helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ef4ddfecc4a4b..fe09d6254c192 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3118,8 +3118,13 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, state->acquire_ctx = ctx; - for_each_new_plane_in_state(state, plane, new_plane_state, i) + for_each_new_plane_in_state(state, plane, new_plane_state, i) { + WARN_ON(plane->crtc != new_plane_state->crtc); + WARN_ON(plane->fb != new_plane_state->fb); + WARN_ON(plane->old_fb); + state->planes[i].old_state = plane->state; + } for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) state->crtcs[i].old_state = crtc->state; From 64c32b490333c9ccb05b172997c4f2f940c5d4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:55 +0200 Subject: [PATCH 027/122] drm: Add local 'plane' variable for primary/cursor planes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the code a bit more readable by storing the plane pointer in a local variable rather than having to do crtc->{primary,cursor} all the time. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-6-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_crtc.c | 32 +++++++++++++++++++------------- drivers/gpu/drm/drm_plane.c | 32 ++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7a973ada7195f..8552ed4190569 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -402,6 +402,7 @@ int drm_mode_getcrtc(struct drm_device *dev, { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc; + struct drm_plane *plane; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -410,21 +411,23 @@ int drm_mode_getcrtc(struct drm_device *dev, if (!crtc) return -ENOENT; + plane = crtc->primary; + crtc_resp->gamma_size = crtc->gamma_size; - drm_modeset_lock(&crtc->primary->mutex, NULL); - if (crtc->primary->state && crtc->primary->state->fb) - crtc_resp->fb_id = crtc->primary->state->fb->base.id; - else if (!crtc->primary->state && crtc->primary->fb) - crtc_resp->fb_id = crtc->primary->fb->base.id; + drm_modeset_lock(&plane->mutex, NULL); + if (plane->state && plane->state->fb) + crtc_resp->fb_id = plane->state->fb->base.id; + else if (!plane->state && plane->fb) + crtc_resp->fb_id = plane->fb->base.id; else crtc_resp->fb_id = 0; - if (crtc->primary->state) { - crtc_resp->x = crtc->primary->state->src_x >> 16; - crtc_resp->y = crtc->primary->state->src_y >> 16; + if (plane->state) { + crtc_resp->x = plane->state->src_x >> 16; + crtc_resp->y = plane->state->src_y >> 16; } - drm_modeset_unlock(&crtc->primary->mutex); + drm_modeset_unlock(&plane->mutex); drm_modeset_lock(&crtc->mutex, NULL); if (crtc->state) { @@ -554,6 +557,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; struct drm_crtc *crtc; + struct drm_plane *plane; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL; struct drm_display_mode *mode = NULL; @@ -580,6 +584,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); + plane = crtc->primary; + mutex_lock(&crtc->dev->mode_config.mutex); drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); retry: @@ -590,12 +596,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ if (crtc_req->fb_id == -1) { - if (!crtc->primary->fb) { + if (!plane->fb) { DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } - fb = crtc->primary->fb; + fb = plane->fb; /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_get(fb); } else { @@ -627,8 +633,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, * match real hardware capabilities. Skip the check in that * case. */ - if (!crtc->primary->format_default) { - ret = drm_plane_check_pixel_format(crtc->primary, + if (!plane->format_default) { + ret = drm_plane_check_pixel_format(plane, fb->format->format, fb->modifier); if (ret) { diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 6d2a6e428a3ea..38e2a628bfa22 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -756,6 +756,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; + struct drm_plane *plane = crtc->cursor; struct drm_framebuffer *fb = NULL; struct drm_mode_fb_cmd2 fbreq = { .width = req->width, @@ -769,8 +770,8 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, uint32_t src_w = 0, src_h = 0; int ret = 0; - BUG_ON(!crtc->cursor); - WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); + BUG_ON(!plane); + WARN_ON(plane->crtc != crtc && plane->crtc != NULL); /* * Obtain fb we'll be using (either new or existing) and take an extra @@ -790,7 +791,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, fb = NULL; } } else { - fb = crtc->cursor->fb; + fb = plane->fb; if (fb) drm_framebuffer_get(fb); } @@ -810,7 +811,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, src_h = fb->height << 16; } - ret = __setplane_internal(crtc->cursor, crtc, fb, + ret = __setplane_internal(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, 0, 0, src_w, src_h, ctx); @@ -931,6 +932,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, { struct drm_mode_crtc_page_flip_target *page_flip = data; struct drm_crtc *crtc; + struct drm_plane *plane; struct drm_framebuffer *fb = NULL; struct drm_pending_vblank_event *e = NULL; u32 target_vblank = page_flip->sequence; @@ -959,6 +961,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT; + plane = crtc->primary; + if (crtc->funcs->page_flip_target) { u32 current_vblank; int r; @@ -1003,11 +1007,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ret = drm_modeset_lock(&crtc->mutex, &ctx); if (ret) goto out; - ret = drm_modeset_lock(&crtc->primary->mutex, &ctx); + ret = drm_modeset_lock(&plane->mutex, &ctx); if (ret) goto out; - if (crtc->primary->fb == NULL) { + if (plane->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. @@ -1023,7 +1027,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, } if (crtc->state) { - const struct drm_plane_state *state = crtc->primary->state; + const struct drm_plane_state *state = plane->state; ret = drm_framebuffer_check_src_coords(state->src_x, state->src_y, @@ -1036,7 +1040,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) goto out; - if (crtc->primary->fb->format != fb->format) { + if (plane->fb->format != fb->format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; @@ -1060,7 +1064,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, } } - crtc->primary->old_fb = crtc->primary->fb; + plane->old_fb = plane->fb; if (crtc->funcs->page_flip_target) ret = crtc->funcs->page_flip_target(crtc, fb, e, page_flip->flags, @@ -1073,9 +1077,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) drm_event_cancel_free(dev, &e->base); /* Keep the old fb, don't unref it. */ - crtc->primary->old_fb = NULL; + plane->old_fb = NULL; } else { - crtc->primary->fb = fb; + plane->fb = fb; /* Unref only the old framebuffer. */ fb = NULL; } @@ -1083,9 +1087,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, out: if (fb) drm_framebuffer_put(fb); - if (crtc->primary->old_fb) - drm_framebuffer_put(crtc->primary->old_fb); - crtc->primary->old_fb = NULL; + if (plane->old_fb) + drm_framebuffer_put(plane->old_fb); + plane->old_fb = NULL; if (ret == -EDEADLK) { ret = drm_modeset_backoff(&ctx); From bf2d5eb902e29d6e7c25540b85242744591b703e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:56 +0200 Subject: [PATCH 028/122] drm: Adjust whitespace for legibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bit of whitespace here and there to make the code look a bit more structured. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-7-ville.syrjala@linux.intel.com Acked-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_crtc.c | 4 +++- drivers/gpu/drm/drm_plane.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8552ed4190569..537ffaab855c5 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -434,13 +434,13 @@ int drm_mode_getcrtc(struct drm_device *dev, if (crtc->state->enable) { drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); crtc_resp->mode_valid = 1; - } else { crtc_resp->mode_valid = 0; } } else { crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; + if (crtc->enabled) { drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); crtc_resp->mode_valid = 1; @@ -592,6 +592,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx); if (ret) goto out; + if (crtc_req->mode_valid) { /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ @@ -601,6 +602,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; } + fb = plane->fb; /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_get(fb); diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 38e2a628bfa22..bedceca7dd06c 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -785,6 +785,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); return PTR_ERR(fb); } + fb->hot_x = req->hot_x; fb->hot_y = req->hot_y; } else { @@ -1035,7 +1036,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, state->src_h, fb); } else { - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); + ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, + &crtc->mode, fb); } if (ret) goto out; @@ -1052,10 +1054,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ret = -ENOMEM; goto out; } + e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof(e->event); e->event.vbl.user_data = page_flip->user_data; e->event.vbl.crtc_id = crtc->base.id; + ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); if (ret) { kfree(e); From 23a5e1fb349d80d8c0eb97644dea16d2d7cfac26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:57 +0200 Subject: [PATCH 029/122] drm: Make the fb refcount handover less magic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of assigning the plane->fb pointer and clearing the fb pointer to hand over the reference, let's just do it by grabbing another referece for plane->fb and let fb keep its original one. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-8-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_plane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index bedceca7dd06c..008f9456a5e84 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -1084,8 +1084,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, plane->old_fb = NULL; } else { plane->fb = fb; - /* Unref only the old framebuffer. */ - fb = NULL; + drm_framebuffer_get(fb); } out: From a36c027db57b6a33970c5c830a0d143f0e98c248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:58 +0200 Subject: [PATCH 030/122] drm: Use plane->state->fb over plane->fb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop looking at plane->fb on atomic drivers. Use plane->state->fb instead. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-9-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/drm_atomic_helper.c | 2 +- drivers/gpu/drm/drm_crtc.c | 11 +++++++++-- drivers/gpu/drm/drm_plane.c | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index fe09d6254c192..ee03c1ed25210 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2670,7 +2670,7 @@ int drm_atomic_helper_disable_plane(struct drm_plane *plane, goto fail; } - if (plane_state->crtc && (plane == plane->crtc->cursor)) + if (plane_state->crtc && plane_state->crtc->cursor == plane) plane_state->state->legacy_cursor_update = true; ret = __drm_atomic_helper_disable_plane(plane, plane_state); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 537ffaab855c5..a231dd5dce16e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -597,13 +597,20 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ if (crtc_req->fb_id == -1) { - if (!plane->fb) { + struct drm_framebuffer *old_fb; + + if (plane->state) + old_fb = plane->state->fb; + else + old_fb = plane->fb; + + if (!old_fb) { DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } - fb = plane->fb; + fb = old_fb; /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_get(fb); } else { diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 008f9456a5e84..0350544553010 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -792,7 +792,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, fb = NULL; } } else { - fb = plane->fb; + if (plane->state) + fb = plane->state->fb; + else + fb = plane->fb; + if (fb) drm_framebuffer_get(fb); } @@ -934,7 +938,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, struct drm_mode_crtc_page_flip_target *page_flip = data; struct drm_crtc *crtc; struct drm_plane *plane; - struct drm_framebuffer *fb = NULL; + struct drm_framebuffer *fb = NULL, *old_fb; struct drm_pending_vblank_event *e = NULL; u32 target_vblank = page_flip->sequence; struct drm_modeset_acquire_ctx ctx; @@ -1012,7 +1016,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) goto out; - if (plane->fb == NULL) { + if (plane->state) + old_fb = plane->state->fb; + else + old_fb = plane->fb; + + if (old_fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. @@ -1027,7 +1036,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, goto out; } - if (crtc->state) { + if (plane->state) { const struct drm_plane_state *state = plane->state; ret = drm_framebuffer_check_src_coords(state->src_x, @@ -1042,7 +1051,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) goto out; - if (plane->fb->format != fb->format) { + if (old_fb->format != fb->format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; From 8bc20f6594a10d3e69b37fc223e43da3804673ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:22:59 +0200 Subject: [PATCH 031/122] drm/i915: Stop consulting plane->fb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to get rid of plane->fb on atomic drivers. Stop looking at it. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-10-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fbdev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3b48fd2561fec..54d4c369cae25 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2824,7 +2824,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, continue; if (intel_plane_ggtt_offset(state) == plane_config->base) { - fb = c->primary->fb; + fb = state->base.fb; drm_framebuffer_get(fb); goto valid_fb; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 6f12adc063650..89592ecc44cab 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -640,7 +640,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, if (!crtc->state->active) continue; - WARN(!crtc->primary->fb, + WARN(!crtc->primary->state->fb, "re-used BIOS config but lost an fb on crtc %d\n", crtc->base.id); } From bf4a7a227511410cd6d6ca623e9ed17e5fc5f30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:23:01 +0200 Subject: [PATCH 032/122] drm/sti: Stop consulting plane->fb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to get rid of plane->fb on atomic drivers. Stop looking at it. Cc: Benjamin Gaignard Cc: Vincent Abriou Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-12-ville.syrjala@linux.intel.com Acked-by: Benjamin Gaignard Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/sti/sti_plane.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index b074609c960aa..b48cd86e02508 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c @@ -40,6 +40,7 @@ void sti_plane_update_fps(struct sti_plane *plane, bool new_frame, bool new_field) { + struct drm_plane_state *state = plane->drm_plane.state; ktime_t now; struct sti_fps_info *fps; int fpks, fipks, ms_since_last, num_frames, num_fields; @@ -66,14 +67,14 @@ void sti_plane_update_fps(struct sti_plane *plane, fps->last_timestamp = now; fps->last_frame_counter = fps->curr_frame_counter; - if (plane->drm_plane.fb) { + if (state->fb) { fpks = (num_frames * 1000000) / ms_since_last; snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-8s %4dx%-4d %.4s @ %3d.%-3.3d fps (%s)", plane->drm_plane.name, - plane->drm_plane.fb->width, - plane->drm_plane.fb->height, - (char *)&plane->drm_plane.fb->format->format, + state->fb->width, + state->fb->height, + (char *)&state->fb->format->format, fpks / 1000, fpks % 1000, sti_plane_to_str(plane)); } From 2bb01c4cc0592ff69323077c093a67f45155d72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:23:02 +0200 Subject: [PATCH 033/122] drm/vmwgfx: Stop consulting plane->fb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to get rid of plane->fb on atomic drivers. Stop looking at it. Cc: VMware Graphics Cc: Sinclair Yeh Cc: Thomas Hellstrom Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-13-ville.syrjala@linux.intel.com Reviewed-by: Thomas Hellstrom Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 34ecc27fc30a2..9fdb3ec9b4c4e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -385,9 +385,9 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, hotspot_x = du->hotspot_x; hotspot_y = du->hotspot_y; - if (plane->fb) { - hotspot_x += plane->fb->hot_x; - hotspot_y += plane->fb->hot_y; + if (plane->state->fb) { + hotspot_x += plane->state->fb->hot_x; + hotspot_y += plane->state->fb->hot_y; } du->cursor_surface = vps->surf; From 6f6887dad5405bc87fc26eddca1fad64d04360fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 26 Mar 2018 15:14:42 +0300 Subject: [PATCH 034/122] drm/zte: Stop consulting plane->crtc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to get rid of plane->crtc on atomic drivers. Stop looking at it. v2: Use old_state->crtc (Maarten) v3: s/fb/crtc/ in commit message to actually match the patch (Shawn) Cc: Shawn Guo Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Acked-by: Shawn Guo Link: https://patchwork.freedesktop.org/patch/msgid/20180326121442.32009-1-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/zte/zx_plane.c | 2 +- drivers/gpu/drm/zte/zx_vou.c | 5 +++-- drivers/gpu/drm/zte/zx_vou.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index 94545adac50d2..d1931f5ea0b2e 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -268,7 +268,7 @@ static void zx_plane_atomic_disable(struct drm_plane *plane, struct zx_plane *zplane = to_zx_plane(plane); void __iomem *hbsc = zplane->hbsc; - zx_vou_layer_disable(plane); + zx_vou_layer_disable(plane, old_state); /* Disable HBSC block */ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0); diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c index 7491813131f35..442311d311101 100644 --- a/drivers/gpu/drm/zte/zx_vou.c +++ b/drivers/gpu/drm/zte/zx_vou.c @@ -627,9 +627,10 @@ void zx_vou_layer_enable(struct drm_plane *plane) zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable); } -void zx_vou_layer_disable(struct drm_plane *plane) +void zx_vou_layer_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) { - struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc); + struct zx_crtc *zcrtc = to_zx_crtc(old_state->crtc); struct zx_vou_hw *vou = zcrtc->vou; struct zx_plane *zplane = to_zx_plane(plane); const struct vou_layer_bits *bits = zplane->bits; diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h index 97d72bfce982c..5b7f84fbb112a 100644 --- a/drivers/gpu/drm/zte/zx_vou.h +++ b/drivers/gpu/drm/zte/zx_vou.h @@ -62,6 +62,7 @@ void zx_vou_config_dividers(struct drm_crtc *crtc, struct vou_div_config *configs, int num); void zx_vou_layer_enable(struct drm_plane *plane); -void zx_vou_layer_disable(struct drm_plane *plane); +void zx_vou_layer_disable(struct drm_plane *plane, + struct drm_plane_state *old_state); #endif /* __ZX_VOU_H__ */ From be90cc318b4c8d5c3999eb25783d8055e6a67855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 17:23:12 +0200 Subject: [PATCH 035/122] drm/i915: Restore planes after load detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Actually turn the planes back on after were done with the load detection. Fixes: 20bdc112bbe4 ("drm/i915: Disable all planes for load detection, v2.") Cc: Maarten Lankhorst Cc: Daniel Vetter Reviewed-by: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322152313.6561-23-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 54d4c369cae25..182f9bf98484a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9974,6 +9974,8 @@ int intel_get_load_detect_pipe(struct drm_connector *connector, ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(restore_state, connector)); if (!ret) ret = PTR_ERR_OR_ZERO(drm_atomic_get_crtc_state(restore_state, crtc)); + if (!ret) + ret = drm_atomic_add_affected_planes(restore_state, crtc); if (ret) { DRM_DEBUG_KMS("Failed to create a copy of old state to restore: %i\n", ret); goto fail; From 4165791d29f64e01860a064f3c649447dbac41c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 Mar 2018 19:41:35 +0200 Subject: [PATCH 036/122] drm/i915: Make force_load_detect effective even w/ DMI quirks/hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing forced load detection testing we should totally ignore any hotplug status for the connector. This is mostly relevant for machines where we already ignore the hotplug status based on the DMI quirks. On other machines we would currently skip the force load detection tests on account of the connector already being connected. v2: Drop the other force_load_detect check since it's useless now (Maarten) Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180322174135.5982-1-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst --- drivers/gpu/drm/i915/intel_crt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index c0a8805b277fa..de0e22322c76e 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -748,6 +748,11 @@ intel_crt_detect(struct drm_connector *connector, connector->base.id, connector->name, force); + if (i915_modparams.load_detect_test) { + intel_display_power_get(dev_priv, intel_encoder->power_domain); + goto load_detect; + } + /* Skip machines without VGA that falsely report hotplug events */ if (dmi_check_system(intel_spurious_crt_detect)) return connector_status_disconnected; @@ -776,11 +781,12 @@ intel_crt_detect(struct drm_connector *connector, * broken monitor (without edid) to work behind a broken kvm (that fails * to have the right resistors for HP detection) needs to fix this up. * For now just bail out. */ - if (I915_HAS_HOTPLUG(dev_priv) && !i915_modparams.load_detect_test) { + if (I915_HAS_HOTPLUG(dev_priv)) { status = connector_status_disconnected; goto out; } +load_detect: if (!force) { status = connector->status; goto out; From 487da6172f5678496699bec685797dc816f6a131 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 23 Mar 2018 13:45:51 +0000 Subject: [PATCH 037/122] drm: Reshuffle getfb error returns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it a little more clear what's going on inside of getfb, and also make it easier to add alternate paths to get a handle in future. Signed-off-by: Daniel Stone Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180323134553.15993-3-daniels@collabora.com --- drivers/gpu/drm/drm_framebuffer.c | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index ad67203de7157..8c4d32adcc178 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -468,29 +468,31 @@ int drm_mode_getfb(struct drm_device *dev, goto out; } + if (!fb->funcs->create_handle) { + ret = -ENODEV; + goto out; + } + r->height = fb->height; r->width = fb->width; r->depth = fb->format->depth; r->bpp = fb->format->cpp[0] * 8; r->pitch = fb->pitches[0]; - if (fb->funcs->create_handle) { - if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || - drm_is_control_client(file_priv)) { - ret = fb->funcs->create_handle(fb, file_priv, - &r->handle); - } else { - /* GET_FB() is an unprivileged ioctl so we must not - * return a buffer-handle to non-master processes! For - * backwards-compatibility reasons, we cannot make - * GET_FB() privileged, so just return an invalid handle - * for non-masters. */ - r->handle = 0; - ret = 0; - } - } else { - ret = -ENODEV; + + /* GET_FB() is an unprivileged ioctl so we must not return a + * buffer-handle to non-master processes! For + * backwards-compatibility reasons, we cannot make GET_FB() privileged, + * so just return an invalid handle for non-masters. + */ + if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN) && + !drm_is_control_client(file_priv)) { + r->handle = 0; + ret = 0; + goto out; } + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + out: drm_framebuffer_put(fb); From c575b7eeb89f94356997abd62d6d5a0590e259b7 Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Tue, 3 Apr 2018 14:23:17 +0300 Subject: [PATCH 038/122] drm/xen-front: Add support for Xen PV display frontend Add support for Xen para-virtualized frontend display driver. Accompanying backend [1] is implemented as a user-space application and its helper library [2], capable of running as a Weston client or DRM master. Configuration of both backend and frontend is done via Xen guest domain configuration options [3]. Driver limitations: 1. Only primary plane without additional properties is supported. 2. Only one video mode supported which resolution is configured via XenStore. 3. All CRTCs operate at fixed frequency of 60Hz. 1. Implement Xen bus state machine for the frontend driver according to the state diagram and recovery flow from display para-virtualized protocol: xen/interface/io/displif.h. 2. Read configuration values from Xen store according to xen/interface/io/displif.h protocol: - read connector(s) configuration - read buffer allocation mode (backend/frontend) 3. Handle Xen event channels: - create for all configured connectors and publish corresponding ring references and event channels in Xen store, so backend can connect - implement event channels interrupt handlers - create and destroy event channels with respect to Xen bus state 4. Implement shared buffer handling according to the para-virtualized display device protocol at xen/interface/io/displif.h: - handle page directories according to displif protocol: - allocate and share page directories - grant references to the required set of pages for the page directory - allocate xen balllooned pages via Xen balloon driver with alloc_xenballooned_pages/free_xenballooned_pages - grant references to the required set of pages for the shared buffer itself - implement pages map/unmap for the buffers allocated by the backend (gnttab_map_refs/gnttab_unmap_refs) 5. Implement kernel modesetiing/connector handling using DRM simple KMS helper pipeline: - implement KMS part of the driver with the help of DRM simple pipepline helper which is possible due to the fact that the para-virtualized driver only supports a single (primary) plane: - initialize connectors according to XenStore configuration - handle frame done events from the backend - create and destroy frame buffers and propagate those to the backend - propagate set/reset mode configuration to the backend on display enable/disable callbacks - send page flip request to the backend and implement logic for reporting backend IO errors on prepare fb callback - implement virtual connector handling: - support only pixel formats suitable for single plane modes - make sure the connector is always connected - support a single video mode as per para-virtualized driver configuration 6. Implement GEM handling depending on driver mode of operation: depending on the requirements for the para-virtualized environment, namely requirements dictated by the accompanying DRM/(v)GPU drivers running in both host and guest environments, number of operating modes of para-virtualized display driver are supported: - display buffers can be allocated by either frontend driver or backend - display buffers can be allocated to be contiguous in memory or not Note! Frontend driver itself has no dependency on contiguous memory for its operation. 6.1. Buffers allocated by the frontend driver. The below modes of operation are configured at compile-time via frontend driver's kernel configuration. 6.1.1. Front driver configured to use GEM CMA helpers This use-case is useful when used with accompanying DRM/vGPU driver in guest domain which was designed to only work with contiguous buffers, e.g. DRM driver based on GEM CMA helpers: such drivers can only import contiguous PRIME buffers, thus requiring frontend driver to provide such. In order to implement this mode of operation para-virtualized frontend driver can be configured to use GEM CMA helpers. 6.1.2. Front driver doesn't use GEM CMA If accompanying drivers can cope with non-contiguous memory then, to lower pressure on CMA subsystem of the kernel, driver can allocate buffers from system memory. Note! If used with accompanying DRM/(v)GPU drivers this mode of operation may require IOMMU support on the platform, so accompanying DRM/vGPU hardware can still reach display buffer memory while importing PRIME buffers from the frontend driver. 6.2. Buffers allocated by the backend This mode of operation is run-time configured via guest domain configuration through XenStore entries. For systems which do not provide IOMMU support, but having specific requirements for display buffers it is possible to allocate such buffers at backend side and share those with the frontend. For example, if host domain is 1:1 mapped and has DRM/GPU hardware expecting physically contiguous memory, this allows implementing zero-copying use-cases. Note, while using this scenario the following should be considered: a) If guest domain dies then pages/grants received from the backend cannot be claimed back b) Misbehaving guest may send too many requests to the backend exhausting its grant references and memory (consider this from security POV). Note! Configuration options 1.1 (contiguous display buffers) and 2 (backend allocated buffers) are not supported at the same time. 7. Handle communication with the backend: - send requests and wait for the responses according to the displif protocol - serialize access to the communication channel - time-out used for backend communication is set to 3000 ms - manage display buffers shared with the backend [1] https://github.com/xen-troops/displ_be [2] https://github.com/xen-troops/libxenbe [3] https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=docs/man/xl.cfg.pod.5.in;h=a699367779e2ae1212ff8f638eff0206ec1a1cc9;hb=refs/heads/master#l1257 Signed-off-by: Oleksandr Andrushchenko Reviewed-by: Boris Ostrovsky Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180403112317.28751-2-andr2000@gmail.com --- Documentation/gpu/drivers.rst | 1 + Documentation/gpu/xen-front.rst | 43 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/xen/Kconfig | 30 + drivers/gpu/drm/xen/Makefile | 16 + drivers/gpu/drm/xen/xen_drm_front.c | 882 ++++++++++++++++++++ drivers/gpu/drm/xen/xen_drm_front.h | 188 +++++ drivers/gpu/drm/xen/xen_drm_front_cfg.c | 77 ++ drivers/gpu/drm/xen/xen_drm_front_cfg.h | 37 + drivers/gpu/drm/xen/xen_drm_front_conn.c | 115 +++ drivers/gpu/drm/xen/xen_drm_front_conn.h | 27 + drivers/gpu/drm/xen/xen_drm_front_evtchnl.c | 387 +++++++++ drivers/gpu/drm/xen/xen_drm_front_evtchnl.h | 81 ++ drivers/gpu/drm/xen/xen_drm_front_gem.c | 314 +++++++ drivers/gpu/drm/xen/xen_drm_front_gem.h | 43 + drivers/gpu/drm/xen/xen_drm_front_gem_cma.c | 79 ++ drivers/gpu/drm/xen/xen_drm_front_kms.c | 372 +++++++++ drivers/gpu/drm/xen/xen_drm_front_kms.h | 26 + drivers/gpu/drm/xen/xen_drm_front_shbuf.c | 436 ++++++++++ drivers/gpu/drm/xen/xen_drm_front_shbuf.h | 72 ++ 21 files changed, 3229 insertions(+) create mode 100644 Documentation/gpu/xen-front.rst create mode 100644 drivers/gpu/drm/xen/Kconfig create mode 100644 drivers/gpu/drm/xen/Makefile create mode 100644 drivers/gpu/drm/xen/xen_drm_front.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_cfg.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_cfg.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_conn.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_conn.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_evtchnl.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_evtchnl.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_gem.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_gem.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_gem_cma.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_kms.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_kms.h create mode 100644 drivers/gpu/drm/xen/xen_drm_front_shbuf.c create mode 100644 drivers/gpu/drm/xen/xen_drm_front_shbuf.h diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst index e8c84419a2a15..d3ab6abae8385 100644 --- a/Documentation/gpu/drivers.rst +++ b/Documentation/gpu/drivers.rst @@ -12,6 +12,7 @@ GPU Driver Documentation tve200 vc4 bridge/dw-hdmi + xen-front .. only:: subproject and html diff --git a/Documentation/gpu/xen-front.rst b/Documentation/gpu/xen-front.rst new file mode 100644 index 0000000000000..009d942386c53 --- /dev/null +++ b/Documentation/gpu/xen-front.rst @@ -0,0 +1,43 @@ +==================================================== + drm/xen-front Xen para-virtualized frontend driver +==================================================== + +This frontend driver implements Xen para-virtualized display +according to the display protocol described at +include/xen/interface/io/displif.h + +Driver modes of operation in terms of display buffers used +========================================================== + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: Driver modes of operation in terms of display buffers used + +Buffers allocated by the frontend driver +---------------------------------------- + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: Buffers allocated by the frontend driver + +With GEM CMA helpers +~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: With GEM CMA helpers + +Without GEM CMA helpers +~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: Without GEM CMA helpers + +Buffers allocated by the backend +-------------------------------- + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: Buffers allocated by the backend + +Driver limitations +================== + +.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h + :doc: Driver limitations diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index deeefa7a1773b..757825ac60dfb 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -289,6 +289,8 @@ source "drivers/gpu/drm/pl111/Kconfig" source "drivers/gpu/drm/tve200/Kconfig" +source "drivers/gpu/drm/xen/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 50093ff4479b4..9d66657ea1174 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -103,3 +103,4 @@ obj-$(CONFIG_DRM_MXSFB) += mxsfb/ obj-$(CONFIG_DRM_TINYDRM) += tinydrm/ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ +obj-$(CONFIG_DRM_XEN) += xen/ diff --git a/drivers/gpu/drm/xen/Kconfig b/drivers/gpu/drm/xen/Kconfig new file mode 100644 index 0000000000000..4f4abc91f3b62 --- /dev/null +++ b/drivers/gpu/drm/xen/Kconfig @@ -0,0 +1,30 @@ +config DRM_XEN + bool "DRM Support for Xen guest OS" + depends on XEN + help + Choose this option if you want to enable DRM support + for Xen. + +config DRM_XEN_FRONTEND + tristate "Para-virtualized frontend driver for Xen guest OS" + depends on DRM_XEN + depends on DRM + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + select XEN_XENBUS_FRONTEND + help + Choose this option if you want to enable a para-virtualized + frontend DRM/KMS driver for Xen guest OSes. + +config DRM_XEN_FRONTEND_CMA + bool "Use DRM CMA to allocate dumb buffers" + depends on DRM_XEN_FRONTEND + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Use DRM CMA helpers to allocate display buffers. + This is useful for the use-cases when guest driver needs to + share or export buffers to other drivers which only expect + contiguous buffers. + Note: in this mode driver cannot use buffers allocated + by the backend. diff --git a/drivers/gpu/drm/xen/Makefile b/drivers/gpu/drm/xen/Makefile new file mode 100644 index 0000000000000..352730dc6c136 --- /dev/null +++ b/drivers/gpu/drm/xen/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 OR MIT + +drm_xen_front-objs := xen_drm_front.o \ + xen_drm_front_kms.o \ + xen_drm_front_conn.o \ + xen_drm_front_evtchnl.o \ + xen_drm_front_shbuf.o \ + xen_drm_front_cfg.o + +ifeq ($(CONFIG_DRM_XEN_FRONTEND_CMA),y) + drm_xen_front-objs += xen_drm_front_gem_cma.o +else + drm_xen_front-objs += xen_drm_front_gem.o +endif + +obj-$(CONFIG_DRM_XEN_FRONTEND) += drm_xen_front.o diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c new file mode 100644 index 0000000000000..4a08b77f1c9e2 --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual DRM device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "xen_drm_front.h" +#include "xen_drm_front_cfg.h" +#include "xen_drm_front_evtchnl.h" +#include "xen_drm_front_gem.h" +#include "xen_drm_front_kms.h" +#include "xen_drm_front_shbuf.h" + +struct xen_drm_front_dbuf { + struct list_head list; + u64 dbuf_cookie; + u64 fb_cookie; + struct xen_drm_front_shbuf *shbuf; +}; + +static int dbuf_add_to_list(struct xen_drm_front_info *front_info, + struct xen_drm_front_shbuf *shbuf, u64 dbuf_cookie) +{ + struct xen_drm_front_dbuf *dbuf; + + dbuf = kzalloc(sizeof(*dbuf), GFP_KERNEL); + if (!dbuf) + return -ENOMEM; + + dbuf->dbuf_cookie = dbuf_cookie; + dbuf->shbuf = shbuf; + list_add(&dbuf->list, &front_info->dbuf_list); + return 0; +} + +static struct xen_drm_front_dbuf *dbuf_get(struct list_head *dbuf_list, + u64 dbuf_cookie) +{ + struct xen_drm_front_dbuf *buf, *q; + + list_for_each_entry_safe(buf, q, dbuf_list, list) + if (buf->dbuf_cookie == dbuf_cookie) + return buf; + + return NULL; +} + +static void dbuf_flush_fb(struct list_head *dbuf_list, u64 fb_cookie) +{ + struct xen_drm_front_dbuf *buf, *q; + + list_for_each_entry_safe(buf, q, dbuf_list, list) + if (buf->fb_cookie == fb_cookie) + xen_drm_front_shbuf_flush(buf->shbuf); +} + +static void dbuf_free(struct list_head *dbuf_list, u64 dbuf_cookie) +{ + struct xen_drm_front_dbuf *buf, *q; + + list_for_each_entry_safe(buf, q, dbuf_list, list) + if (buf->dbuf_cookie == dbuf_cookie) { + list_del(&buf->list); + xen_drm_front_shbuf_unmap(buf->shbuf); + xen_drm_front_shbuf_free(buf->shbuf); + kfree(buf); + break; + } +} + +static void dbuf_free_all(struct list_head *dbuf_list) +{ + struct xen_drm_front_dbuf *buf, *q; + + list_for_each_entry_safe(buf, q, dbuf_list, list) { + list_del(&buf->list); + xen_drm_front_shbuf_unmap(buf->shbuf); + xen_drm_front_shbuf_free(buf->shbuf); + kfree(buf); + } +} + +static struct xendispl_req * +be_prepare_req(struct xen_drm_front_evtchnl *evtchnl, u8 operation) +{ + struct xendispl_req *req; + + req = RING_GET_REQUEST(&evtchnl->u.req.ring, + evtchnl->u.req.ring.req_prod_pvt); + req->operation = operation; + req->id = evtchnl->evt_next_id++; + evtchnl->evt_id = req->id; + return req; +} + +static int be_stream_do_io(struct xen_drm_front_evtchnl *evtchnl, + struct xendispl_req *req) +{ + reinit_completion(&evtchnl->u.req.completion); + if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) + return -EIO; + + xen_drm_front_evtchnl_flush(evtchnl); + return 0; +} + +static int be_stream_wait_io(struct xen_drm_front_evtchnl *evtchnl) +{ + if (wait_for_completion_timeout(&evtchnl->u.req.completion, + msecs_to_jiffies(XEN_DRM_FRONT_WAIT_BACK_MS)) <= 0) + return -ETIMEDOUT; + + return evtchnl->u.req.resp_status; +} + +int xen_drm_front_mode_set(struct xen_drm_front_drm_pipeline *pipeline, + u32 x, u32 y, u32 width, u32 height, + u32 bpp, u64 fb_cookie) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_info *front_info; + struct xendispl_req *req; + unsigned long flags; + int ret; + + front_info = pipeline->drm_info->front_info; + evtchnl = &front_info->evt_pairs[pipeline->index].req; + if (unlikely(!evtchnl)) + return -EIO; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_SET_CONFIG); + req->op.set_config.x = x; + req->op.set_config.y = y; + req->op.set_config.width = width; + req->op.set_config.height = height; + req->op.set_config.bpp = bpp; + req->op.set_config.fb_cookie = fb_cookie; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +static int be_dbuf_create_int(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u32 width, u32 height, + u32 bpp, u64 size, struct page **pages, + struct sg_table *sgt) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_shbuf *shbuf; + struct xendispl_req *req; + struct xen_drm_front_shbuf_cfg buf_cfg; + unsigned long flags; + int ret; + + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + if (unlikely(!evtchnl)) + return -EIO; + + memset(&buf_cfg, 0, sizeof(buf_cfg)); + buf_cfg.xb_dev = front_info->xb_dev; + buf_cfg.pages = pages; + buf_cfg.size = size; + buf_cfg.sgt = sgt; + buf_cfg.be_alloc = front_info->cfg.be_alloc; + + shbuf = xen_drm_front_shbuf_alloc(&buf_cfg); + if (!shbuf) + return -ENOMEM; + + ret = dbuf_add_to_list(front_info, shbuf, dbuf_cookie); + if (ret < 0) { + xen_drm_front_shbuf_free(shbuf); + return ret; + } + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_CREATE); + req->op.dbuf_create.gref_directory = + xen_drm_front_shbuf_get_dir_start(shbuf); + req->op.dbuf_create.buffer_sz = size; + req->op.dbuf_create.dbuf_cookie = dbuf_cookie; + req->op.dbuf_create.width = width; + req->op.dbuf_create.height = height; + req->op.dbuf_create.bpp = bpp; + if (buf_cfg.be_alloc) + req->op.dbuf_create.flags |= XENDISPL_DBUF_FLG_REQ_ALLOC; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + goto fail; + + ret = be_stream_wait_io(evtchnl); + if (ret < 0) + goto fail; + + ret = xen_drm_front_shbuf_map(shbuf); + if (ret < 0) + goto fail; + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return 0; + +fail: + mutex_unlock(&evtchnl->u.req.req_io_lock); + dbuf_free(&front_info->dbuf_list, dbuf_cookie); + return ret; +} + +int xen_drm_front_dbuf_create_from_sgt(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u32 width, u32 height, + u32 bpp, u64 size, struct sg_table *sgt) +{ + return be_dbuf_create_int(front_info, dbuf_cookie, width, height, + bpp, size, NULL, sgt); +} + +int xen_drm_front_dbuf_create_from_pages(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u32 width, u32 height, + u32 bpp, u64 size, struct page **pages) +{ + return be_dbuf_create_int(front_info, dbuf_cookie, width, height, + bpp, size, pages, NULL); +} + +static int xen_drm_front_dbuf_destroy(struct xen_drm_front_info *front_info, + u64 dbuf_cookie) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xendispl_req *req; + unsigned long flags; + bool be_alloc; + int ret; + + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + if (unlikely(!evtchnl)) + return -EIO; + + be_alloc = front_info->cfg.be_alloc; + + /* + * For the backend allocated buffer release references now, so backend + * can free the buffer. + */ + if (be_alloc) + dbuf_free(&front_info->dbuf_list, dbuf_cookie); + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_DESTROY); + req->op.dbuf_destroy.dbuf_cookie = dbuf_cookie; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + /* + * Do this regardless of communication status with the backend: + * if we cannot remove remote resources remove what we can locally. + */ + if (!be_alloc) + dbuf_free(&front_info->dbuf_list, dbuf_cookie); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_drm_front_fb_attach(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u64 fb_cookie, u32 width, + u32 height, u32 pixel_format) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_dbuf *buf; + struct xendispl_req *req; + unsigned long flags; + int ret; + + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + if (unlikely(!evtchnl)) + return -EIO; + + buf = dbuf_get(&front_info->dbuf_list, dbuf_cookie); + if (!buf) + return -EINVAL; + + buf->fb_cookie = fb_cookie; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_FB_ATTACH); + req->op.fb_attach.dbuf_cookie = dbuf_cookie; + req->op.fb_attach.fb_cookie = fb_cookie; + req->op.fb_attach.width = width; + req->op.fb_attach.height = height; + req->op.fb_attach.pixel_format = pixel_format; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_drm_front_fb_detach(struct xen_drm_front_info *front_info, + u64 fb_cookie) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xendispl_req *req; + unsigned long flags; + int ret; + + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + if (unlikely(!evtchnl)) + return -EIO; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_FB_DETACH); + req->op.fb_detach.fb_cookie = fb_cookie; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_drm_front_page_flip(struct xen_drm_front_info *front_info, + int conn_idx, u64 fb_cookie) +{ + struct xen_drm_front_evtchnl *evtchnl; + struct xendispl_req *req; + unsigned long flags; + int ret; + + if (unlikely(conn_idx >= front_info->num_evt_pairs)) + return -EINVAL; + + dbuf_flush_fb(&front_info->dbuf_list, fb_cookie); + evtchnl = &front_info->evt_pairs[conn_idx].req; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_PG_FLIP); + req->op.pg_flip.fb_cookie = fb_cookie; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +void xen_drm_front_on_frame_done(struct xen_drm_front_info *front_info, + int conn_idx, u64 fb_cookie) +{ + struct xen_drm_front_drm_info *drm_info = front_info->drm_info; + + if (unlikely(conn_idx >= front_info->cfg.num_connectors)) + return; + + xen_drm_front_kms_on_frame_done(&drm_info->pipeline[conn_idx], + fb_cookie); +} + +static int xen_drm_drv_dumb_create(struct drm_file *filp, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct xen_drm_front_drm_info *drm_info = dev->dev_private; + struct drm_gem_object *obj; + int ret; + + /* + * Dumb creation is a two stage process: first we create a fully + * constructed GEM object which is communicated to the backend, and + * only after that we can create GEM's handle. This is done so, + * because of the possible races: once you create a handle it becomes + * immediately visible to user-space, so the latter can try accessing + * object without pages etc. + * For details also see drm_gem_handle_create + */ + args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + args->size = args->pitch * args->height; + + obj = xen_drm_front_gem_create(dev, args->size); + if (IS_ERR_OR_NULL(obj)) { + ret = PTR_ERR(obj); + goto fail; + } + + /* + * In case of CONFIG_DRM_XEN_FRONTEND_CMA gem_obj is constructed + * via DRM CMA helpers and doesn't have ->pages allocated + * (xendrm_gem_get_pages will return NULL), but instead can provide + * sg table + */ + if (xen_drm_front_gem_get_pages(obj)) + ret = xen_drm_front_dbuf_create_from_pages(drm_info->front_info, + xen_drm_front_dbuf_to_cookie(obj), + args->width, args->height, args->bpp, + args->size, + xen_drm_front_gem_get_pages(obj)); + else + ret = xen_drm_front_dbuf_create_from_sgt(drm_info->front_info, + xen_drm_front_dbuf_to_cookie(obj), + args->width, args->height, args->bpp, + args->size, + xen_drm_front_gem_get_sg_table(obj)); + if (ret) + goto fail_backend; + + /* This is the tail of GEM object creation */ + ret = drm_gem_handle_create(filp, obj, &args->handle); + if (ret) + goto fail_handle; + + /* Drop reference from allocate - handle holds it now */ + drm_gem_object_put_unlocked(obj); + return 0; + +fail_handle: + xen_drm_front_dbuf_destroy(drm_info->front_info, + xen_drm_front_dbuf_to_cookie(obj)); +fail_backend: + /* drop reference from allocate */ + drm_gem_object_put_unlocked(obj); +fail: + DRM_ERROR("Failed to create dumb buffer: %d\n", ret); + return ret; +} + +static void xen_drm_drv_free_object_unlocked(struct drm_gem_object *obj) +{ + struct xen_drm_front_drm_info *drm_info = obj->dev->dev_private; + int idx; + + if (drm_dev_enter(obj->dev, &idx)) { + xen_drm_front_dbuf_destroy(drm_info->front_info, + xen_drm_front_dbuf_to_cookie(obj)); + drm_dev_exit(idx); + } else { + dbuf_free(&drm_info->front_info->dbuf_list, + xen_drm_front_dbuf_to_cookie(obj)); + } + + xen_drm_front_gem_free_object_unlocked(obj); +} + +static void xen_drm_drv_release(struct drm_device *dev) +{ + struct xen_drm_front_drm_info *drm_info = dev->dev_private; + struct xen_drm_front_info *front_info = drm_info->front_info; + + xen_drm_front_kms_fini(drm_info); + + drm_atomic_helper_shutdown(dev); + drm_mode_config_cleanup(dev); + + drm_dev_fini(dev); + kfree(dev); + + if (front_info->cfg.be_alloc) + xenbus_switch_state(front_info->xb_dev, + XenbusStateInitialising); + + kfree(drm_info); +} + +static const struct file_operations xen_drm_dev_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, +#ifdef CONFIG_DRM_XEN_FRONTEND_CMA + .mmap = drm_gem_cma_mmap, +#else + .mmap = xen_drm_front_gem_mmap, +#endif +}; + +static const struct vm_operations_struct xen_drm_drv_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static struct drm_driver xen_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_PRIME | DRIVER_ATOMIC, + .release = xen_drm_drv_release, + .gem_vm_ops = &xen_drm_drv_vm_ops, + .gem_free_object_unlocked = xen_drm_drv_free_object_unlocked, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import_sg_table = xen_drm_front_gem_import_sg_table, + .gem_prime_get_sg_table = xen_drm_front_gem_get_sg_table, + .dumb_create = xen_drm_drv_dumb_create, + .fops = &xen_drm_dev_fops, + .name = "xendrm-du", + .desc = "Xen PV DRM Display Unit", + .date = "20180221", + .major = 1, + .minor = 0, + +#ifdef CONFIG_DRM_XEN_FRONTEND_CMA + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, +#else + .gem_prime_vmap = xen_drm_front_gem_prime_vmap, + .gem_prime_vunmap = xen_drm_front_gem_prime_vunmap, + .gem_prime_mmap = xen_drm_front_gem_prime_mmap, +#endif +}; + +static int xen_drm_drv_init(struct xen_drm_front_info *front_info) +{ + struct device *dev = &front_info->xb_dev->dev; + struct xen_drm_front_drm_info *drm_info; + struct drm_device *drm_dev; + int ret; + + DRM_INFO("Creating %s\n", xen_drm_driver.desc); + + drm_info = kzalloc(sizeof(*drm_info), GFP_KERNEL); + if (!drm_info) { + ret = -ENOMEM; + goto fail; + } + + drm_info->front_info = front_info; + front_info->drm_info = drm_info; + + drm_dev = drm_dev_alloc(&xen_drm_driver, dev); + if (!drm_dev) { + ret = -ENOMEM; + goto fail; + } + + drm_info->drm_dev = drm_dev; + + drm_dev->dev_private = drm_info; + + ret = xen_drm_front_kms_init(drm_info); + if (ret) { + DRM_ERROR("Failed to initialize DRM/KMS, ret %d\n", ret); + goto fail_modeset; + } + + ret = drm_dev_register(drm_dev, 0); + if (ret) + goto fail_register; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + xen_drm_driver.name, xen_drm_driver.major, + xen_drm_driver.minor, xen_drm_driver.patchlevel, + xen_drm_driver.date, drm_dev->primary->index); + + return 0; + +fail_register: + drm_dev_unregister(drm_dev); +fail_modeset: + drm_kms_helper_poll_fini(drm_dev); + drm_mode_config_cleanup(drm_dev); +fail: + kfree(drm_info); + return ret; +} + +static void xen_drm_drv_fini(struct xen_drm_front_info *front_info) +{ + struct xen_drm_front_drm_info *drm_info = front_info->drm_info; + struct drm_device *dev; + + if (!drm_info) + return; + + dev = drm_info->drm_dev; + if (!dev) + return; + + /* Nothing to do if device is already unplugged */ + if (drm_dev_is_unplugged(dev)) + return; + + drm_kms_helper_poll_fini(dev); + drm_dev_unplug(dev); + + front_info->drm_info = NULL; + + xen_drm_front_evtchnl_free_all(front_info); + dbuf_free_all(&front_info->dbuf_list); + + /* + * If we are not using backend allocated buffers, then tell the + * backend we are ready to (re)initialize. Otherwise, wait for + * drm_driver.release. + */ + if (!front_info->cfg.be_alloc) + xenbus_switch_state(front_info->xb_dev, + XenbusStateInitialising); +} + +static int displback_initwait(struct xen_drm_front_info *front_info) +{ + struct xen_drm_front_cfg *cfg = &front_info->cfg; + int ret; + + cfg->front_info = front_info; + ret = xen_drm_front_cfg_card(front_info, cfg); + if (ret < 0) + return ret; + + DRM_INFO("Have %d conector(s)\n", cfg->num_connectors); + /* Create event channels for all connectors and publish */ + ret = xen_drm_front_evtchnl_create_all(front_info); + if (ret < 0) + return ret; + + return xen_drm_front_evtchnl_publish_all(front_info); +} + +static int displback_connect(struct xen_drm_front_info *front_info) +{ + xen_drm_front_evtchnl_set_state(front_info, EVTCHNL_STATE_CONNECTED); + return xen_drm_drv_init(front_info); +} + +static void displback_disconnect(struct xen_drm_front_info *front_info) +{ + if (!front_info->drm_info) + return; + + /* Tell the backend to wait until we release the DRM driver. */ + xenbus_switch_state(front_info->xb_dev, XenbusStateReconfiguring); + + xen_drm_drv_fini(front_info); +} + +static void displback_changed(struct xenbus_device *xb_dev, + enum xenbus_state backend_state) +{ + struct xen_drm_front_info *front_info = dev_get_drvdata(&xb_dev->dev); + int ret; + + DRM_DEBUG("Backend state is %s, front is %s\n", + xenbus_strstate(backend_state), + xenbus_strstate(xb_dev->state)); + + switch (backend_state) { + case XenbusStateReconfiguring: + /* fall through */ + case XenbusStateReconfigured: + /* fall through */ + case XenbusStateInitialised: + break; + + case XenbusStateInitialising: + if (xb_dev->state == XenbusStateReconfiguring) + break; + + /* recovering after backend unexpected closure */ + displback_disconnect(front_info); + break; + + case XenbusStateInitWait: + if (xb_dev->state == XenbusStateReconfiguring) + break; + + /* recovering after backend unexpected closure */ + displback_disconnect(front_info); + if (xb_dev->state != XenbusStateInitialising) + break; + + ret = displback_initwait(front_info); + if (ret < 0) + xenbus_dev_fatal(xb_dev, ret, "initializing frontend"); + else + xenbus_switch_state(xb_dev, XenbusStateInitialised); + break; + + case XenbusStateConnected: + if (xb_dev->state != XenbusStateInitialised) + break; + + ret = displback_connect(front_info); + if (ret < 0) { + displback_disconnect(front_info); + xenbus_dev_fatal(xb_dev, ret, "connecting backend"); + } else { + xenbus_switch_state(xb_dev, XenbusStateConnected); + } + break; + + case XenbusStateClosing: + /* + * in this state backend starts freeing resources, + * so let it go into closed state, so we can also + * remove ours + */ + break; + + case XenbusStateUnknown: + /* fall through */ + case XenbusStateClosed: + if (xb_dev->state == XenbusStateClosed) + break; + + displback_disconnect(front_info); + break; + } +} + +static int xen_drv_probe(struct xenbus_device *xb_dev, + const struct xenbus_device_id *id) +{ + struct xen_drm_front_info *front_info; + struct device *dev = &xb_dev->dev; + int ret; + + /* + * The device is not spawn from a device tree, so arch_setup_dma_ops + * is not called, thus leaving the device with dummy DMA ops. + * This makes the device return error on PRIME buffer import, which + * is not correct: to fix this call of_dma_configure() with a NULL + * node to set default DMA ops. + */ + dev->bus->force_dma = true; + dev->coherent_dma_mask = DMA_BIT_MASK(32); + ret = of_dma_configure(dev, NULL); + if (ret < 0) { + DRM_ERROR("Cannot setup DMA ops, ret %d", ret); + return ret; + } + + front_info = devm_kzalloc(&xb_dev->dev, + sizeof(*front_info), GFP_KERNEL); + if (!front_info) + return -ENOMEM; + + front_info->xb_dev = xb_dev; + spin_lock_init(&front_info->io_lock); + INIT_LIST_HEAD(&front_info->dbuf_list); + dev_set_drvdata(&xb_dev->dev, front_info); + + return xenbus_switch_state(xb_dev, XenbusStateInitialising); +} + +static int xen_drv_remove(struct xenbus_device *dev) +{ + struct xen_drm_front_info *front_info = dev_get_drvdata(&dev->dev); + int to = 100; + + xenbus_switch_state(dev, XenbusStateClosing); + + /* + * On driver removal it is disconnected from XenBus, + * so no backend state change events come via .otherend_changed + * callback. This prevents us from exiting gracefully, e.g. + * signaling the backend to free event channels, waiting for its + * state to change to XenbusStateClosed and cleaning at our end. + * Normally when front driver removed backend will finally go into + * XenbusStateInitWait state. + * + * Workaround: read backend's state manually and wait with time-out. + */ + while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state", + XenbusStateUnknown) != XenbusStateInitWait) && + to--) + msleep(10); + + if (!to) { + unsigned int state; + + state = xenbus_read_unsigned(front_info->xb_dev->otherend, + "state", XenbusStateUnknown); + DRM_ERROR("Backend state is %s while removing driver\n", + xenbus_strstate(state)); + } + + xen_drm_drv_fini(front_info); + xenbus_frontend_closed(dev); + return 0; +} + +static const struct xenbus_device_id xen_driver_ids[] = { + { XENDISPL_DRIVER_NAME }, + { "" } +}; + +static struct xenbus_driver xen_driver = { + .ids = xen_driver_ids, + .probe = xen_drv_probe, + .remove = xen_drv_remove, + .otherend_changed = displback_changed, +}; + +static int __init xen_drv_init(void) +{ + /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */ + if (XEN_PAGE_SIZE != PAGE_SIZE) { + DRM_ERROR(XENDISPL_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n", + XEN_PAGE_SIZE, PAGE_SIZE); + return -ENODEV; + } + + if (!xen_domain()) + return -ENODEV; + + if (!xen_has_pv_devices()) + return -ENODEV; + + DRM_INFO("Registering XEN PV " XENDISPL_DRIVER_NAME "\n"); + return xenbus_register_frontend(&xen_driver); +} + +static void __exit xen_drv_fini(void) +{ + DRM_INFO("Unregistering XEN PV " XENDISPL_DRIVER_NAME "\n"); + xenbus_unregister_driver(&xen_driver); +} + +module_init(xen_drv_init); +module_exit(xen_drv_fini); + +MODULE_DESCRIPTION("Xen para-virtualized display device frontend"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:" XENDISPL_DRIVER_NAME); diff --git a/drivers/gpu/drm/xen/xen_drm_front.h b/drivers/gpu/drm/xen/xen_drm_front.h new file mode 100644 index 0000000000000..16554b2463d8a --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual DRM device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#ifndef __XEN_DRM_FRONT_H_ +#define __XEN_DRM_FRONT_H_ + +#include +#include + +#include + +#include "xen_drm_front_cfg.h" + +/** + * DOC: Driver modes of operation in terms of display buffers used + * + * Depending on the requirements for the para-virtualized environment, namely + * requirements dictated by the accompanying DRM/(v)GPU drivers running in both + * host and guest environments, number of operating modes of para-virtualized + * display driver are supported: + * + * - display buffers can be allocated by either frontend driver or backend + * - display buffers can be allocated to be contiguous in memory or not + * + * Note! Frontend driver itself has no dependency on contiguous memory for + * its operation. + */ + +/** + * DOC: Buffers allocated by the frontend driver + * + * The below modes of operation are configured at compile-time via + * frontend driver's kernel configuration: + */ + +/** + * DOC: With GEM CMA helpers + * + * This use-case is useful when used with accompanying DRM/vGPU driver in + * guest domain which was designed to only work with contiguous buffers, + * e.g. DRM driver based on GEM CMA helpers: such drivers can only import + * contiguous PRIME buffers, thus requiring frontend driver to provide + * such. In order to implement this mode of operation para-virtualized + * frontend driver can be configured to use GEM CMA helpers. + */ + +/** + * DOC: Without GEM CMA helpers + * + * If accompanying drivers can cope with non-contiguous memory then, to + * lower pressure on CMA subsystem of the kernel, driver can allocate + * buffers from system memory. + * + * Note! If used with accompanying DRM/(v)GPU drivers this mode of operation + * may require IOMMU support on the platform, so accompanying DRM/vGPU + * hardware can still reach display buffer memory while importing PRIME + * buffers from the frontend driver. + */ + +/** + * DOC: Buffers allocated by the backend + * + * This mode of operation is run-time configured via guest domain configuration + * through XenStore entries. + * + * For systems which do not provide IOMMU support, but having specific + * requirements for display buffers it is possible to allocate such buffers + * at backend side and share those with the frontend. + * For example, if host domain is 1:1 mapped and has DRM/GPU hardware expecting + * physically contiguous memory, this allows implementing zero-copying + * use-cases. + * + * Note, while using this scenario the following should be considered: + * + * #. If guest domain dies then pages/grants received from the backend + * cannot be claimed back + * + * #. Misbehaving guest may send too many requests to the + * backend exhausting its grant references and memory + * (consider this from security POV) + */ + +/** + * DOC: Driver limitations + * + * #. Only primary plane without additional properties is supported. + * + * #. Only one video mode per connector supported which is configured + * via XenStore. + * + * #. All CRTCs operate at fixed frequency of 60Hz. + */ + +/* timeout in ms to wait for backend to respond */ +#define XEN_DRM_FRONT_WAIT_BACK_MS 3000 + +#ifndef GRANT_INVALID_REF +/* + * Note on usage of grant reference 0 as invalid grant reference: + * grant reference 0 is valid, but never exposed to a PV driver, + * because of the fact it is already in use/reserved by the PV console. + */ +#define GRANT_INVALID_REF 0 +#endif + +struct xen_drm_front_info { + struct xenbus_device *xb_dev; + struct xen_drm_front_drm_info *drm_info; + + /* to protect data between backend IO code and interrupt handler */ + spinlock_t io_lock; + + int num_evt_pairs; + struct xen_drm_front_evtchnl_pair *evt_pairs; + struct xen_drm_front_cfg cfg; + + /* display buffers */ + struct list_head dbuf_list; +}; + +struct xen_drm_front_drm_pipeline { + struct xen_drm_front_drm_info *drm_info; + + int index; + + struct drm_simple_display_pipe pipe; + + struct drm_connector conn; + /* These are only for connector mode checking */ + int width, height; + + struct drm_pending_vblank_event *pending_event; + + struct delayed_work pflip_to_worker; + + bool conn_connected; +}; + +struct xen_drm_front_drm_info { + struct xen_drm_front_info *front_info; + struct drm_device *drm_dev; + + struct xen_drm_front_drm_pipeline pipeline[XEN_DRM_FRONT_MAX_CRTCS]; +}; + +static inline u64 xen_drm_front_fb_to_cookie(struct drm_framebuffer *fb) +{ + return (u64)fb; +} + +static inline u64 xen_drm_front_dbuf_to_cookie(struct drm_gem_object *gem_obj) +{ + return (u64)gem_obj; +} + +int xen_drm_front_mode_set(struct xen_drm_front_drm_pipeline *pipeline, + u32 x, u32 y, u32 width, u32 height, + u32 bpp, u64 fb_cookie); + +int xen_drm_front_dbuf_create_from_sgt(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u32 width, u32 height, + u32 bpp, u64 size, struct sg_table *sgt); + +int xen_drm_front_dbuf_create_from_pages(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u32 width, u32 height, + u32 bpp, u64 size, struct page **pages); + +int xen_drm_front_fb_attach(struct xen_drm_front_info *front_info, + u64 dbuf_cookie, u64 fb_cookie, u32 width, + u32 height, u32 pixel_format); + +int xen_drm_front_fb_detach(struct xen_drm_front_info *front_info, + u64 fb_cookie); + +int xen_drm_front_page_flip(struct xen_drm_front_info *front_info, + int conn_idx, u64 fb_cookie); + +void xen_drm_front_on_frame_done(struct xen_drm_front_info *front_info, + int conn_idx, u64 fb_cookie); + +#endif /* __XEN_DRM_FRONT_H_ */ diff --git a/drivers/gpu/drm/xen/xen_drm_front_cfg.c b/drivers/gpu/drm/xen/xen_drm_front_cfg.c new file mode 100644 index 0000000000000..5baf2b9de93c4 --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front_cfg.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual DRM device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#include + +#include + +#include +#include + +#include "xen_drm_front.h" +#include "xen_drm_front_cfg.h" + +static int cfg_connector(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg_connector *connector, + const char *path, int index) +{ + char *connector_path; + + connector_path = devm_kasprintf(&front_info->xb_dev->dev, + GFP_KERNEL, "%s/%d", path, index); + if (!connector_path) + return -ENOMEM; + + if (xenbus_scanf(XBT_NIL, connector_path, XENDISPL_FIELD_RESOLUTION, + "%d" XENDISPL_RESOLUTION_SEPARATOR "%d", + &connector->width, &connector->height) < 0) { + /* either no entry configured or wrong resolution set */ + connector->width = 0; + connector->height = 0; + return -EINVAL; + } + + connector->xenstore_path = connector_path; + + DRM_INFO("Connector %s: resolution %dx%d\n", + connector_path, connector->width, connector->height); + return 0; +} + +int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg *cfg) +{ + struct xenbus_device *xb_dev = front_info->xb_dev; + int ret, i; + + if (xenbus_read_unsigned(front_info->xb_dev->nodename, + XENDISPL_FIELD_BE_ALLOC, 0)) { + DRM_INFO("Backend can provide display buffers\n"); + cfg->be_alloc = true; + } + + cfg->num_connectors = 0; + for (i = 0; i < ARRAY_SIZE(cfg->connectors); i++) { + ret = cfg_connector(front_info, &cfg->connectors[i], + xb_dev->nodename, i); + if (ret < 0) + break; + cfg->num_connectors++; + } + + if (!cfg->num_connectors) { + DRM_ERROR("No connector(s) configured at %s\n", + xb_dev->nodename); + return -ENODEV; + } + + return 0; +} + diff --git a/drivers/gpu/drm/xen/xen_drm_front_cfg.h b/drivers/gpu/drm/xen/xen_drm_front_cfg.h new file mode 100644 index 0000000000000..aa8490ba91469 --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front_cfg.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual DRM device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#ifndef __XEN_DRM_FRONT_CFG_H_ +#define __XEN_DRM_FRONT_CFG_H_ + +#include + +#define XEN_DRM_FRONT_MAX_CRTCS 4 + +struct xen_drm_front_cfg_connector { + int width; + int height; + char *xenstore_path; +}; + +struct xen_drm_front_cfg { + struct xen_drm_front_info *front_info; + /* number of connectors in this configuration */ + int num_connectors; + /* connector configurations */ + struct xen_drm_front_cfg_connector connectors[XEN_DRM_FRONT_MAX_CRTCS]; + /* set if dumb buffers are allocated externally on backend side */ + bool be_alloc; +}; + +int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg *cfg); + +#endif /* __XEN_DRM_FRONT_CFG_H_ */ diff --git a/drivers/gpu/drm/xen/xen_drm_front_conn.c b/drivers/gpu/drm/xen/xen_drm_front_conn.c new file mode 100644 index 0000000000000..c91ae532fa55e --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front_conn.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual DRM device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#include +#include + +#include