From dd20946516b6dc567c733cc3e4538eb9223596cf Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 12 Dec 2023 15:25:56 +0200 Subject: drm/edid: replace __attribute__((packed)) with __packed __packed is preferred over __attribute__((packed)). Signed-off-by: Jani Nikula Reviewed-by: Simon Ser Link: https://patchwork.freedesktop.org/patch/msgid/20231212132557.3777281-1-jani.nikula@intel.com --- include/drm/drm_edid.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 518d1b8106c7..54cc6f04a708 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -46,7 +46,7 @@ struct est_timings { u8 t1; u8 t2; u8 mfg_rsvd; -} __attribute__((packed)); +} __packed; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ #define EDID_TIMING_ASPECT_SHIFT 6 @@ -59,7 +59,7 @@ struct est_timings { struct std_timing { u8 hsize; /* need to multiply by 8 then add 248 */ u8 vfreq_aspect; -} __attribute__((packed)); +} __packed; #define DRM_EDID_PT_HSYNC_POSITIVE (1 << 1) #define DRM_EDID_PT_VSYNC_POSITIVE (1 << 2) @@ -85,12 +85,12 @@ struct detailed_pixel_timing { u8 hborder; u8 vborder; u8 misc; -} __attribute__((packed)); +} __packed; /* If it's not pixel timing, it'll be one of the below */ struct detailed_data_string { u8 str[13]; -} __attribute__((packed)); +} __packed; #define DRM_EDID_RANGE_OFFSET_MIN_VFREQ (1 << 0) /* 1.4 */ #define DRM_EDID_RANGE_OFFSET_MAX_VFREQ (1 << 1) /* 1.4 */ @@ -120,7 +120,7 @@ struct detailed_data_monitor_range { __le16 m; u8 k; u8 j; /* need to divide by 2 */ - } __attribute__((packed)) gtf2; + } __packed gtf2; struct { u8 version; u8 data1; /* high 6 bits: extra clock resolution */ @@ -129,27 +129,27 @@ struct detailed_data_monitor_range { u8 flags; /* preferred aspect and blanking support */ u8 supported_scalings; u8 preferred_refresh; - } __attribute__((packed)) cvt; - } __attribute__((packed)) formula; -} __attribute__((packed)); + } __packed cvt; + } __packed formula; +} __packed; struct detailed_data_wpindex { u8 white_yx_lo; /* Lower 2 bits each */ u8 white_x_hi; u8 white_y_hi; u8 gamma; /* need to divide by 100 then add 1 */ -} __attribute__((packed)); +} __packed; struct detailed_data_color_point { u8 windex1; u8 wpindex1[3]; u8 windex2; u8 wpindex2[3]; -} __attribute__((packed)); +} __packed; struct cvt_timing { u8 code[3]; -} __attribute__((packed)); +} __packed; struct detailed_non_pixel { u8 pad1; @@ -163,8 +163,8 @@ struct detailed_non_pixel { struct detailed_data_wpindex color; struct std_timing timings[6]; struct cvt_timing cvt[4]; - } __attribute__((packed)) data; -} __attribute__((packed)); + } __packed data; +} __packed; #define EDID_DETAIL_EST_TIMINGS 0xf7 #define EDID_DETAIL_CVT_3BYTE 0xf8 @@ -181,8 +181,8 @@ struct detailed_timing { union { struct detailed_pixel_timing pixel_data; struct detailed_non_pixel other_data; - } __attribute__((packed)) data; -} __attribute__((packed)); + } __packed data; +} __packed; #define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 0) #define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 1) @@ -307,7 +307,7 @@ struct edid { u8 extensions; /* Checksum */ u8 checksum; -} __attribute__((packed)); +} __packed; #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) -- cgit v1.2.3 From 216d86b9a430f3280e5b631c51e6fd1a7774cfa0 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Fri, 15 Sep 2023 18:59:18 +0800 Subject: drm/virtio: add definition for venus capset This Venus capset definition is used by Qemu, and Qemu imports the kernel protocol header file. Add Venus capset to the VirtIO-GPU protocol. Signed-off-by: Huang Rui [dmitry.osipenko@collabora.com: edit commit message] Signed-off-by: Dmitry Osipenko Link: https://patchwork.freedesktop.org/patch/msgid/20230915105918.3763061-1-ray.huang@amd.com --- include/uapi/linux/virtio_gpu.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h index f556fde07b76..0e21f3998108 100644 --- a/include/uapi/linux/virtio_gpu.h +++ b/include/uapi/linux/virtio_gpu.h @@ -309,6 +309,8 @@ struct virtio_gpu_cmd_submit { #define VIRTIO_GPU_CAPSET_VIRGL 1 #define VIRTIO_GPU_CAPSET_VIRGL2 2 +/* 3 is reserved for gfxstream */ +#define VIRTIO_GPU_CAPSET_VENUS 4 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { -- cgit v1.2.3 From 0808aef86dc2776f75c7961015ee0280b78d868d Mon Sep 17 00:00:00 2001 From: Pranjal Ramajor Asha Kanojiya Date: Fri, 8 Dec 2023 09:34:51 -0700 Subject: accel/qaic: Deprecate ->size field from attach slice IOCTL structure ->size in struct qaic_attach_slice_hdr is redundant since we have BO handle and its size can be retrieved from base BO structure. Signed-off-by: Pranjal Ramajor Asha Kanojiya Reviewed-by: Jeffrey Hugo Signed-off-by: Jeffrey Hugo Reviewed-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20231208163457.1295993-2-quic_jhugo@quicinc.com --- drivers/accel/qaic/qaic_data.c | 17 ++++------------- include/uapi/drm/qaic_accel.h | 13 +------------ 2 files changed, 5 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index cf2898eda7ae..0c6f1328df68 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -830,9 +830,6 @@ static int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_h struct sg_table *sgt; int ret; - if (obj->import_attach->dmabuf->size < hdr->size) - return -EINVAL; - sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); @@ -849,9 +846,6 @@ static int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo, { int ret; - if (bo->base.size < hdr->size) - return -EINVAL; - ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0); if (ret) return -EFAULT; @@ -952,9 +946,6 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi if (arg_size / args->hdr.count != sizeof(*slice_ent)) return -EINVAL; - if (args->hdr.size == 0) - return -EINVAL; - if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE)) return -EINVAL; @@ -994,16 +985,16 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi goto free_slice_ent; } - ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, args->hdr.size); - if (ret) - goto free_slice_ent; - obj = drm_gem_object_lookup(file_priv, args->hdr.handle); if (!obj) { ret = -ENOENT; goto free_slice_ent; } + ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, obj->size); + if (ret) + goto put_bo; + bo = to_qaic_bo(obj); ret = mutex_lock_interruptible(&bo->lock); if (ret) diff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h index 9dab32316aee..d3ca876a08e9 100644 --- a/include/uapi/drm/qaic_accel.h +++ b/include/uapi/drm/qaic_accel.h @@ -242,18 +242,7 @@ struct qaic_attach_slice_entry { * @dbc_id: In. Associate the sliced BO with this DBC. * @handle: In. GEM handle of the BO to slice. * @dir: In. Direction of data flow. 1 = DMA_TO_DEVICE, 2 = DMA_FROM_DEVICE - * @size: In. Total length of BO being used. This should not exceed base - * size of BO (struct drm_gem_object.base) - * For BOs being allocated using DRM_IOCTL_QAIC_CREATE_BO, size of - * BO requested is PAGE_SIZE aligned then allocated hence allocated - * BO size maybe bigger. This size should not exceed the new - * PAGE_SIZE aligned BO size. - * @dev_addr: In. Device address this slice pushes to or pulls from. - * @db_addr: In. Address of the doorbell to ring. - * @db_data: In. Data to write to the doorbell. - * @db_len: In. Size of the doorbell data in bits - 32, 16, or 8. 0 is for - * inactive doorbells. - * @offset: In. Start of this slice as an offset from the start of the BO. + * @size: Deprecated. This value is ignored and size of @handle is used instead. */ struct qaic_attach_slice_hdr { __u32 count; -- cgit v1.2.3 From 1c20d8b8e171b92194a4f9b45821c821606b8936 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 14 Dec 2023 11:09:12 +0100 Subject: drm/atomic: Move the drm_atomic_state field doc inline Some fields of drm_atomic_state have been documented in-line, but some were documented in the main kerneldoc block before the structure. Since the former is the preferred option in DRM, let's move all the fields to an inline documentation. Acked-by: Sui Jingfeng Link: https://lore.kernel.org/r/20231214100917.277842-1-mripard@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 50 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index cf8e1220a4ac..2a08030fcd75 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -347,24 +347,22 @@ struct __drm_private_objs_state { /** * struct drm_atomic_state - the global state object for atomic updates - * @ref: count of all references to this state (will not be freed until zero) - * @dev: parent DRM device - * @async_update: hint for asynchronous plane update - * @planes: pointer to array of structures with per-plane data - * @crtcs: pointer to array of CRTC pointers - * @num_connector: size of the @connectors and @connector_states arrays - * @connectors: pointer to array of structures with per-connector data - * @num_private_objs: size of the @private_objs array - * @private_objs: pointer to array of private object pointers - * @acquire_ctx: acquire context for this atomic modeset state update * * States are added to an atomic update by calling drm_atomic_get_crtc_state(), * drm_atomic_get_plane_state(), drm_atomic_get_connector_state(), or for * private state structures, drm_atomic_get_private_obj_state(). */ struct drm_atomic_state { + /** + * @ref: + * + * Count of all references to this update (will not be freed until zero). + */ struct kref ref; + /** + * @dev: Parent DRM Device. + */ struct drm_device *dev; /** @@ -388,7 +386,12 @@ struct drm_atomic_state { * flag are not allowed. */ bool legacy_cursor_update : 1; + + /** + * @async_update: hint for asynchronous plane update + */ bool async_update : 1; + /** * @duplicated: * @@ -398,13 +401,40 @@ struct drm_atomic_state { * states. */ bool duplicated : 1; + + /** + * @planes: pointer to array of structures with per-plane data + */ struct __drm_planes_state *planes; + + /** + * @crtcs: pointer to array of CRTC pointers + */ struct __drm_crtcs_state *crtcs; + + /** + * @num_connector: size of the @connectors and @connector_states arrays + */ int num_connector; + + /** + * @connectors: pointer to array of structures with per-connector data + */ struct __drm_connnectors_state *connectors; + + /** + * @num_private_objs: size of the @private_objs array + */ int num_private_objs; + + /** + * @private_objs: pointer to array of private object pointers + */ struct __drm_private_objs_state *private_objs; + /** + * @acquire_ctx: acquire context for this atomic modeset state update + */ struct drm_modeset_acquire_ctx *acquire_ctx; /** -- cgit v1.2.3 From 786f6c2fa4ae3913f0fdcd371e871bd97aff8481 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 14 Dec 2023 11:09:13 +0100 Subject: drm/atomic: Remove inexistent reference Commit 63e83c1dba54 ("drm: Consolidate connector arrays in drm_atomic_state") removed the connector_states field but didn't remove its mention in the num_connectors documentation. Acked-by: Sui Jingfeng Reviewed-by: Daniel Vetter Link: https://lore.kernel.org/r/20231214100917.277842-2-mripard@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 2a08030fcd75..13cecdc1257d 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -413,7 +413,7 @@ struct drm_atomic_state { struct __drm_crtcs_state *crtcs; /** - * @num_connector: size of the @connectors and @connector_states arrays + * @num_connector: size of the @connectors array */ int num_connector; -- cgit v1.2.3 From 17beda5e951aac35fee26703719c252bb216b56b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 14 Dec 2023 11:09:14 +0100 Subject: drm/atomic: Rework the object doc a bit Commits 63e83c1dba54 ("drm: Consolidate connector arrays in drm_atomic_state"), b8b5342b699b ("drm: Consolidate plane arrays in drm_atomic_state") and 5d943aa6c0d4 ("drm: Consolidate crtc arrays in drm_atomic_state") moved the object pointer and their state pointer to an intermediate structure storing both. The CRTC commit didn't update the doc of the crtcs field to reflect that, and the doc for the planes and connectors fields mention that they are pointers to an array of structures with per-$OBJECT data. The private_objs field was added later on by commit b430c27a7de3 ("drm: Add driver-private objects to atomic state") reusing the same sentence than the crtcs field, probably due to copy and paste. While these fields are indeed pointers to an array, each item of that array contain a pointer to the object structure affected by the update, and its old and new state. There's no per-object data there, and there's more than just a pointer to the objects. Let's rephrase those fields a bit to better match the current situation. Acked-by: Sui Jingfeng Reviewed-by: Daniel Vetter Link: https://lore.kernel.org/r/20231214100917.277842-3-mripard@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 13cecdc1257d..914574b58ae7 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -403,12 +403,18 @@ struct drm_atomic_state { bool duplicated : 1; /** - * @planes: pointer to array of structures with per-plane data + * @planes: + * + * Pointer to array of @drm_plane and @drm_plane_state part of this + * update. */ struct __drm_planes_state *planes; /** - * @crtcs: pointer to array of CRTC pointers + * @crtcs: + * + * Pointer to array of @drm_crtc and @drm_crtc_state part of this + * update. */ struct __drm_crtcs_state *crtcs; @@ -418,7 +424,10 @@ struct drm_atomic_state { int num_connector; /** - * @connectors: pointer to array of structures with per-connector data + * @connectors: + * + * Pointer to array of @drm_connector and @drm_connector_state part of + * this update. */ struct __drm_connnectors_state *connectors; @@ -428,7 +437,10 @@ struct drm_atomic_state { int num_private_objs; /** - * @private_objs: pointer to array of private object pointers + * @private_objs: + * + * Pointer to array of @drm_private_obj and @drm_private_obj_state part + * of this update. */ struct __drm_private_objs_state *private_objs; -- cgit v1.2.3 From ab9fabeae4e71095d29216ff14f8a56e4fdda895 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 14 Dec 2023 11:09:15 +0100 Subject: drm/atomic: Make the drm_atomic_state documentation less ambiguous The current documentation of drm_atomic_state says that it's the "global state object". This is confusing since, while it does contain all the objects affected by an update and their respective states, if an object isn't affected by this update it won't be part of it. Thus, it's not truly a "global state", unlike object state structures that do contain the entire state of a given object. Reviewed-by: Hamza Mahfooz Acked-by: Pekka Paalanen Link: https://lore.kernel.org/r/20231214100917.277842-4-mripard@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 914574b58ae7..4d7f4c5f2001 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -346,7 +346,13 @@ struct __drm_private_objs_state { }; /** - * struct drm_atomic_state - the global state object for atomic updates + * struct drm_atomic_state - Atomic commit structure + * + * This structure is the kernel counterpart of @drm_mode_atomic and represents + * an atomic commit that transitions from an old to a new display state. It + * contains all the objects affected by the atomic commit and both the new + * state structures and pointers to the old state structures for + * these. * * States are added to an atomic update by calling drm_atomic_get_crtc_state(), * drm_atomic_get_plane_state(), drm_atomic_get_connector_state(), or for -- cgit v1.2.3 From 20f5583dd7a5103427147f6ee0b29d49647f3c62 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Wed, 6 Dec 2023 22:09:47 +0100 Subject: drm/print: Add drm_dbg_ratelimited Add a function for ratelimitted debug print. Signed-off-by: Nirmoy Das Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Daniel Vetter Reviewed-by: Matthew Auld Reviewed-by: Andrzej Hajda Reviewed-by: Andi Shyti Reviewed-by: Sam Ravnborg Acked-by: Maxime Ripard Signed-off-by: Andi Shyti Link: https://patchwork.freedesktop.org/patch/msgid/20231206210948.106238-2-andi.shyti@linux.intel.com --- include/drm/drm_print.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index a93a387f8a1a..ad77ac4b6808 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -602,6 +602,9 @@ void __drm_err(const char *format, ...); drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__); \ }) +#define drm_dbg_ratelimited(drm, fmt, ...) \ + __DRM_DEFINE_DBG_RATELIMITED(DRIVER, drm, fmt, ## __VA_ARGS__) + #define drm_dbg_kms_ratelimited(drm, fmt, ...) \ __DRM_DEFINE_DBG_RATELIMITED(KMS, drm, fmt, ## __VA_ARGS__) -- cgit v1.2.3 From f031c3a7af8ea06790dd0a71872c4f0175084baa Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Tue, 26 Dec 2023 08:11:14 -0800 Subject: drm/xe/uapi: Remove DRM_XE_VM_BIND_FLAG_ASYNC comment left over MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a comment left over of commit d3d767396a02 ("drm/xe/uapi: Remove sync binds"). Fixes: d3d767396a02 ("drm/xe/uapi: Remove sync binds") Reviewed-by: Rodrigo Vivi Cc: Matthew Brost Signed-off-by: José Roberto de Souza --- include/uapi/drm/xe_drm.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 9fa3ae324731..50bbea0992d9 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -832,7 +832,6 @@ struct drm_xe_vm_destroy { * * and the @flags can be: * - %DRM_XE_VM_BIND_FLAG_READONLY - * - %DRM_XE_VM_BIND_FLAG_ASYNC * - %DRM_XE_VM_BIND_FLAG_IMMEDIATE - Valid on a faulting VM only, do the * MAP operation immediately rather than deferring the MAP to the page * fault handler. -- cgit v1.2.3 From cf8837d7204481026335461629b84ac7f4538fa5 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Wed, 8 Nov 2023 11:36:20 -0500 Subject: drm: Don't treat 0 as -1 in drm_fixp2int_ceil Unit testing this in VKMS shows that passing 0 into this function returns -1, which is highly counter- intuitive. Fix it by checking whether the input is >= 0 instead of > 0. Fixes: 64566b5e767f ("drm: Add drm_fixp_from_fraction and drm_fixp2int_ceil") Signed-off-by: Harry Wentland Reviewed-by: Simon Ser Reviewed-by: Melissa Wen Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20231108163647.106853-2-harry.wentland@amd.com --- include/drm/drm_fixed.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 6ea339d5de08..0c9f917a4d4b 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -95,7 +95,7 @@ static inline int drm_fixp2int_round(s64 a) static inline int drm_fixp2int_ceil(s64 a) { - if (a > 0) + if (a >= 0) return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE); else return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE); -- cgit v1.2.3 From e130ba220da559a8eac60eb5ff60b0774ea17009 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 4 Jan 2024 23:10:28 +0200 Subject: drm/edid: prefer forward declarations over includes in drm_edid.h There's no need to include either linux/hdmi.h or drm/drm_mode.h. They can be removed by using forward declarations. While at it, group the forward declarations together, and remove the unnecessary ones. Signed-off-by: Jani Nikula Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20240104211028.1129606-1-jani.nikula@intel.com --- include/drm/drm_edid.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 54cc6f04a708..86c1812a8034 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -24,11 +24,14 @@ #define __DRM_EDID_H__ #include -#include -#include +enum hdmi_quantization_range; +struct drm_connector; struct drm_device; +struct drm_display_mode; struct drm_edid; +struct hdmi_avi_infoframe; +struct hdmi_vendor_infoframe; struct i2c_adapter; #define EDID_LENGTH 128 @@ -319,11 +322,6 @@ struct cea_sad { u8 byte2; /* meaning depends on format */ }; -struct drm_encoder; -struct drm_connector; -struct drm_connector_state; -struct drm_display_mode; - int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads); int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb); int drm_av_sync_delay(struct drm_connector *connector, -- cgit v1.2.3 From 5343f29b3dc534be01b45cd3a3e43572996f96f8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 2 Jan 2024 13:21:58 +0100 Subject: drm: Move drm_set_preferred_mode() helper from drm_edid to drm_modes The helper is generic, it doesn't use the opaque EDID type struct drm_edid and is also used by drivers that only support non-probeable displays such as fixed panels. These drivers add a list of modes using drm_mode_probed_add() and then set a preferred mode using the drm_set_preferred_mode() helper. It seems more logical to have the helper definition in drm_modes.o instead of drm_edid.o, since the former contains modes helper while the latter has helpers to manage the EDID information. Since both drm_edid.o and drm_modes.o object files are built-in the drm.o object, there are no functional changes. But besides being a more logical place for this helper, it could also allow to eventually make drm_edid.o optional and not included in drm.o if only fixed panels must be supported in a given system. Signed-off-by: Javier Martinez Canillas Reviewed-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20240102122208.3103597-1-javierm@redhat.com --- drivers/gpu/drm/drm_edid.c | 22 ---------------------- drivers/gpu/drm/drm_modes.c | 22 ++++++++++++++++++++++ include/drm/drm_edid.h | 2 -- include/drm/drm_modes.h | 2 ++ 4 files changed, 24 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index cb4031d5dcbb..e677dc8eb7a9 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6989,28 +6989,6 @@ int drm_add_modes_noedid(struct drm_connector *connector, } EXPORT_SYMBOL(drm_add_modes_noedid); -/** - * drm_set_preferred_mode - Sets the preferred mode of a connector - * @connector: connector whose mode list should be processed - * @hpref: horizontal resolution of preferred mode - * @vpref: vertical resolution of preferred mode - * - * Marks a mode as preferred if it matches the resolution specified by @hpref - * and @vpref. - */ -void drm_set_preferred_mode(struct drm_connector *connector, - int hpref, int vpref) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &connector->probed_modes, head) { - if (mode->hdisplay == hpref && - mode->vdisplay == vpref) - mode->type |= DRM_MODE_TYPE_PREFERRED; - } -} -EXPORT_SYMBOL(drm_set_preferred_mode); - static bool is_hdmi2_sink(const struct drm_connector *connector) { /* diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index ac9a406250c5..45238ec5df1b 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -2754,3 +2754,25 @@ bool drm_mode_is_420(const struct drm_display_info *display, drm_mode_is_420_also(display, mode); } EXPORT_SYMBOL(drm_mode_is_420); + +/** + * drm_set_preferred_mode - Sets the preferred mode of a connector + * @connector: connector whose mode list should be processed + * @hpref: horizontal resolution of preferred mode + * @vpref: vertical resolution of preferred mode + * + * Marks a mode as preferred if it matches the resolution specified by @hpref + * and @vpref. + */ +void drm_set_preferred_mode(struct drm_connector *connector, + int hpref, int vpref) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->probed_modes, head) { + if (mode->hdisplay == hpref && + mode->vdisplay == vpref) + mode->type |= DRM_MODE_TYPE_PREFERRED; + } +} +EXPORT_SYMBOL(drm_set_preferred_mode); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 86c1812a8034..7923bc00dc7a 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -424,8 +424,6 @@ enum hdmi_quantization_range drm_default_rgb_quant_range(const struct drm_display_mode *mode); int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); -void drm_set_preferred_mode(struct drm_connector *connector, - int hpref, int vpref); int drm_edid_header_is_valid(const void *edid); bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index c613f0abe9dc..b9bb92e4b029 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -467,6 +467,8 @@ bool drm_mode_is_420_also(const struct drm_display_info *display, const struct drm_display_mode *mode); bool drm_mode_is_420(const struct drm_display_info *display, const struct drm_display_mode *mode); +void drm_set_preferred_mode(struct drm_connector *connector, + int hpref, int vpref); struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, enum drm_connector_tv_mode mode, -- cgit v1.2.3 From babebd1dc1279027206583a9921e05657f97da87 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 5 Jan 2024 18:57:49 +0200 Subject: drm/probe-helper: remove unused drm_connector_helper_get_modes_from_ddc() Remove the unused drm_connector_helper_get_modes_from_ddc() function. Most drivers should probably have this functionality split to detect and get modes parts, so the helper is not the best abstraction. Suggested-by: Thomas Zimmermann Signed-off-by: Jani Nikula Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/60eb6b2db16747d3f9c12604b197f33da585c16e.1704473654.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_probe_helper.c | 36 ------------------------------------ include/drm/drm_probe_helper.h | 1 - 2 files changed, 37 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 3f479483d7d8..d1e1ade66f81 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -1100,42 +1100,6 @@ enum drm_mode_status drm_crtc_helper_mode_valid_fixed(struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_crtc_helper_mode_valid_fixed); -/** - * drm_connector_helper_get_modes_from_ddc - Updates the connector's EDID - * property from the connector's - * DDC channel - * @connector: The connector - * - * Returns: - * The number of detected display modes. - * - * Uses a connector's DDC channel to retrieve EDID data and update the - * connector's EDID property and display modes. Drivers can use this - * function to implement struct &drm_connector_helper_funcs.get_modes - * for connectors with a DDC channel. - */ -int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector) -{ - struct edid *edid; - int count = 0; - - if (!connector->ddc) - return 0; - - edid = drm_get_edid(connector, connector->ddc); - - // clears property if EDID is NULL - drm_connector_update_edid_property(connector, edid); - - if (edid) { - count = drm_add_edid_modes(connector, edid); - kfree(edid); - } - - return count; -} -EXPORT_SYMBOL(drm_connector_helper_get_modes_from_ddc); - /** * drm_connector_helper_get_modes_fixed - Duplicates a display mode for a connector * @connector: the connector diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index fad3c4003b2b..62741a88796b 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -32,7 +32,6 @@ enum drm_mode_status drm_crtc_helper_mode_valid_fixed(struct drm_crtc *crtc, const struct drm_display_mode *mode, const struct drm_display_mode *fixed_mode); -int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector); int drm_connector_helper_get_modes_fixed(struct drm_connector *connector, const struct drm_display_mode *fixed_mode); int drm_connector_helper_get_modes(struct drm_connector *connector); -- cgit v1.2.3 From 251ba4583f750db2a89c464ed15682028c215688 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Mon, 25 Dec 2023 07:51:45 +0100 Subject: drm/nouveau: uapi: fix kerneldoc warnings As of commit b77fdd6a48e6 ("scripts/kernel-doc: restore warning for Excess struct/union"), we see the following warnings when running 'make htmldocs': ./include/uapi/drm/nouveau_drm.h:292: warning: Excess struct member 'DRM_NOUVEAU_VM_BIND_OP_MAP' description in 'drm_nouveau_vm_bind_op' ./include/uapi/drm/nouveau_drm.h:292: warning: Excess struct member 'DRM_NOUVEAU_VM_BIND_OP_UNMAP' description in 'drm_nouveau_vm_bind_op' ./include/uapi/drm/nouveau_drm.h:292: warning: Excess struct member 'DRM_NOUVEAU_VM_BIND_SPARSE' description in 'drm_nouveau_vm_bind_op' ./include/uapi/drm/nouveau_drm.h:336: warning: Excess struct member 'DRM_NOUVEAU_VM_BIND_RUN_ASYNC' description in 'drm_nouveau_vm_bind' The problem is that these values are #define constants, but had kerneldoc comments attached to them as if they were actual struct members. There are a number of ways we could fix this, but I chose to draw inspiration from include/uapi/drm/i915_drm.h, which pulls them into the corresponding kerneldoc comment for the struct member that they are intended to be used with. To keep the diff readable, there are a number of things I _didn't_ do in this patch, but which we should also consider: - This is pretty good documentation, but it ends up in gpu/driver-uapi, which is part of subsystem-apis/ when it really ought to display under userspace-api/ (the "Linux kernel user-space API guide" book of the documentation). - More generally, we might want a warning if include/uapi/ files are kerneldoc'd outside userspace-api/. - I'd consider it cleaner if the #defines appeared between the kerneldoc for the member and the member itself (which is something other DRM- related UAPI docs do). - The %IDENTIFIER kerneldoc syntax is intended for "constants", and is more appropriate in this context than ``IDENTIFIER`` or &IDENTIFIER. The DRM docs aren't very consistent on this. Cc: Randy Dunlap Cc: Jonathan Corbet Signed-off-by: Vegard Nossum Reviewed-by: Randy Dunlap Tested-by: Randy Dunlap Signed-off-by: Danilo Krummrich Link: https://patchwork.freedesktop.org/patch/msgid/20231225065145.3060754-1-vegard.nossum@oracle.com --- include/uapi/drm/nouveau_drm.h | 56 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/nouveau_drm.h b/include/uapi/drm/nouveau_drm.h index 0bade1592f34..c95ef8a4d94a 100644 --- a/include/uapi/drm/nouveau_drm.h +++ b/include/uapi/drm/nouveau_drm.h @@ -238,34 +238,32 @@ struct drm_nouveau_vm_init { struct drm_nouveau_vm_bind_op { /** * @op: the operation type + * + * Supported values: + * + * %DRM_NOUVEAU_VM_BIND_OP_MAP - Map a GEM object to the GPU's VA + * space. Optionally, the &DRM_NOUVEAU_VM_BIND_SPARSE flag can be + * passed to instruct the kernel to create sparse mappings for the + * given range. + * + * %DRM_NOUVEAU_VM_BIND_OP_UNMAP - Unmap an existing mapping in the + * GPU's VA space. If the region the mapping is located in is a + * sparse region, new sparse mappings are created where the unmapped + * (memory backed) mapping was mapped previously. To remove a sparse + * region the &DRM_NOUVEAU_VM_BIND_SPARSE must be set. */ __u32 op; -/** - * @DRM_NOUVEAU_VM_BIND_OP_MAP: - * - * Map a GEM object to the GPU's VA space. Optionally, the - * &DRM_NOUVEAU_VM_BIND_SPARSE flag can be passed to instruct the kernel to - * create sparse mappings for the given range. - */ #define DRM_NOUVEAU_VM_BIND_OP_MAP 0x0 -/** - * @DRM_NOUVEAU_VM_BIND_OP_UNMAP: - * - * Unmap an existing mapping in the GPU's VA space. If the region the mapping - * is located in is a sparse region, new sparse mappings are created where the - * unmapped (memory backed) mapping was mapped previously. To remove a sparse - * region the &DRM_NOUVEAU_VM_BIND_SPARSE must be set. - */ #define DRM_NOUVEAU_VM_BIND_OP_UNMAP 0x1 /** * @flags: the flags for a &drm_nouveau_vm_bind_op + * + * Supported values: + * + * %DRM_NOUVEAU_VM_BIND_SPARSE - Indicates that an allocated VA + * space region should be sparse. */ __u32 flags; -/** - * @DRM_NOUVEAU_VM_BIND_SPARSE: - * - * Indicates that an allocated VA space region should be sparse. - */ #define DRM_NOUVEAU_VM_BIND_SPARSE (1 << 8) /** * @handle: the handle of the DRM GEM object to map @@ -301,17 +299,17 @@ struct drm_nouveau_vm_bind { __u32 op_count; /** * @flags: the flags for a &drm_nouveau_vm_bind ioctl + * + * Supported values: + * + * %DRM_NOUVEAU_VM_BIND_RUN_ASYNC - Indicates that the given VM_BIND + * operation should be executed asynchronously by the kernel. + * + * If this flag is not supplied the kernel executes the associated + * operations synchronously and doesn't accept any &drm_nouveau_sync + * objects. */ __u32 flags; -/** - * @DRM_NOUVEAU_VM_BIND_RUN_ASYNC: - * - * Indicates that the given VM_BIND operation should be executed asynchronously - * by the kernel. - * - * If this flag is not supplied the kernel executes the associated operations - * synchronously and doesn't accept any &drm_nouveau_sync objects. - */ #define DRM_NOUVEAU_VM_BIND_RUN_ASYNC 0x1 /** * @wait_count: the number of wait &drm_nouveau_syncs -- cgit v1.2.3 From 9962c25ac41bf6e45bf3afeb56e10a03f0c663f7 Mon Sep 17 00:00:00 2001 From: Jouni Högander Date: Mon, 18 Dec 2023 19:49:58 +0200 Subject: drm: Add eDP 1.5 early transport definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add DP_PSR_ENABLE_SU_REGION_ET to enable panel early transport. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20231218175004.52875-2-jouni.hogander@intel.com --- include/drm/display/drm_dp.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 83d2039c018b..9f3bbbb67add 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -715,6 +715,7 @@ # define DP_PSR_SU_REGION_SCANLINE_CAPTURE BIT(4) /* eDP 1.4a */ # define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS BIT(5) /* eDP 1.4a */ # define DP_PSR_ENABLE_PSR2 BIT(6) /* eDP 1.4a */ +# define DP_PSR_ENABLE_SU_REGION_ET BIT(7) /* eDP 1.5 */ #define DP_ADAPTER_CTRL 0x1a0 # define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE (1 << 0) -- cgit v1.2.3 From 4cabb2174d2c8c9672fcec95c49a8eb257142ea3 Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Wed, 3 Jan 2024 18:13:39 -0500 Subject: drm/amdkfd: Bump KFD ioctl version This is not strictly a change in the IOCTL API. This version bump is meant to indicate to user mode the presence of a number of changes and fixes that enable the management of VA mappings in compute VMs using the GEM_VA ioctl for DMABufs exported from KFD. Signed-off-by: Felix Kuehling Reviewed-by: Xiaogang Chen Signed-off-by: Alex Deucher --- include/uapi/linux/kfd_ioctl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index f0ed68974c54..9ce46edc62a5 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -40,9 +40,10 @@ * - 1.12 - Add DMA buf export ioctl * - 1.13 - Add debugger API * - 1.14 - Update kfd_event_data + * - 1.15 - Enable managing mappings in compute VMs with GEM_VA ioctl */ #define KFD_IOCTL_MAJOR_VERSION 1 -#define KFD_IOCTL_MINOR_VERSION 14 +#define KFD_IOCTL_MINOR_VERSION 15 struct kfd_ioctl_get_version_args { __u32 major_version; /* from KFD */ -- cgit v1.2.3 From d3f452f3a01e748cc6e569df6275e95b0efdb01f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Oct 2023 14:33:15 -0400 Subject: drm/amdgpu: add new INFO IOCTL query for input power Some chips provide both average and input power. Previously we just exposed average power, add a new query for input power. Example userspace: https://github.com/Umio-Yasuno/libdrm-amdgpu-sys-rs/tree/input_power Reviewed-by: Yang Wang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 9 +++++++++ include/uapi/drm/amdgpu_drm.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index bf4f48fe438d..48496bb585c7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1114,6 +1114,15 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } ui32 >>= 8; break; + case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER: + /* get input GPU power */ + if (amdgpu_dpm_read_sensor(adev, + AMDGPU_PP_SENSOR_GPU_INPUT_POWER, + (void *)&ui32, &ui32_size)) { + return -EINVAL; + } + ui32 >>= 8; + break; case AMDGPU_INFO_SENSOR_VDDNB: /* get VDDNB in millivolts */ if (amdgpu_dpm_read_sensor(adev, diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index ad21c613fec8..96e32dafd4f0 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -865,6 +865,8 @@ struct drm_amdgpu_cs_chunk_cp_gfx_shadow { #define AMDGPU_INFO_SENSOR_PEAK_PSTATE_GFX_SCLK 0xa /* Subquery id: Query GPU peak pstate memory clock */ #define AMDGPU_INFO_SENSOR_PEAK_PSTATE_GFX_MCLK 0xb + /* Subquery id: Query input GPU power */ + #define AMDGPU_INFO_SENSOR_GPU_INPUT_POWER 0xc /* Number of VRAM page faults on CPU access. */ #define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E #define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F -- cgit v1.2.3 From a05f7279307bbee354016dc695effac043e626a5 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 4 Jan 2024 22:16:32 +0200 Subject: ASoC: hdmi-codec: drop drm/drm_edid.h include hdmi-codec.h does not appear to directly need drm/drm_edid.h for anything. Remove it. There are some files that get drm/drm_edid.h by proxy; include it where needed. v2-v4: Fix build (kernel test robot ) Cc: Rob Clark Cc: Abhinav Kumar Cc: Dmitry Baryshkov Cc: Sean Paul Cc: Marijn Suijten Cc: linux-arm-msm@vger.kernel.org Cc: freedreno@lists.freedesktop.org Cc: Andrzej Hajda Cc: Neil Armstrong Cc: Robert Foss Cc: Laurent Pinchart Cc: Jonas Karlman Cc: Jernej Skrabec Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: linux-sound@vger.kernel.org Acked-by: Maxime Ripard Reviewed-by: Andi Shyti Acked-by: Alex Deucher Reviewed-by: Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20240104201632.1100753-3-jani.nikula@intel.com --- drivers/gpu/drm/bridge/lontium-lt9611.c | 1 + drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + drivers/gpu/drm/msm/dp/dp_display.c | 1 + drivers/gpu/drm/tegra/hdmi.c | 1 + drivers/gpu/drm/vc4/vc4_hdmi.c | 1 + include/sound/hdmi-codec.h | 1 - 7 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 9663601ce098..b9205d14d943 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index e971b75e90ad..f3f130c1ef0a 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 52d91a0df85e..fa63a21bdd1c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 1b88fb52726f..766c8d01e6b3 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "msm_drv.h" #include "msm_kms.h" diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 417fb884240a..09987e372e3e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index f05e2c95a60d..34f807ed1c31 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h index 9b162ac1e08e..5e1a9eafd10f 100644 --- a/include/sound/hdmi-codec.h +++ b/include/sound/hdmi-codec.h @@ -12,7 +12,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 42d6196f6a948aaecfedf72326925dcbd054f9db Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Mon, 15 Jan 2024 18:13:47 +0100 Subject: drm/managed: Add drmm_release_action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to devres equivalent, it allows to call the "release" action directly and remove the resource from the managed resources list. Signed-off-by: Michał Winiarski Reviewed-by: Maxime Ripard Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20240115171351.504264-2-michal.winiarski@intel.com --- drivers/gpu/drm/drm_managed.c | 39 +++++++++++++++++++++++++++++++++++++++ include/drm/drm_managed.h | 4 ++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index bcd111404b12..7646f67bda4e 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action_or_reset); +/** + * drmm_release_action - release a managed action from a &drm_device + * @dev: DRM device + * @action: function which would be called when @dev is released + * @data: opaque pointer, passed to @action + * + * This function calls the @action previously added by drmm_add_action() + * immediately. + * The @action is removed from the list of cleanup actions for @dev, + * which means that it won't be called in the final drm_dev_put(). + */ +void drmm_release_action(struct drm_device *dev, + drmres_release_t action, + void *data) +{ + struct drmres *dr_match = NULL, *dr; + unsigned long flags; + + spin_lock_irqsave(&dev->managed.lock, flags); + list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) { + if (dr->node.release == action) { + if (!data || (data && *(void **)dr->data == data)) { + dr_match = dr; + del_dr(dev, dr_match); + break; + } + } + } + spin_unlock_irqrestore(&dev->managed.lock, flags); + + if (WARN_ON(!dr_match)) + return; + + action(dev, data); + + free_dr(dr_match); +} +EXPORT_SYMBOL(drmm_release_action); + /** * drmm_kmalloc - &drm_device managed kmalloc() * @dev: DRM device diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index ad08f834af40..f547b09ca023 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct drm_device *dev, drmres_release_t action, void *data, const char *name); +void drmm_release_action(struct drm_device *dev, + drmres_release_t action, + void *data); + void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; /** -- cgit v1.2.3 From bddacdf4861c0586f6d515e0c25861443a32b92a Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 8 Jan 2024 17:57:36 +0530 Subject: drm/i915: Add additional ARL PCI IDs Our existing MTL driver handling is also sufficient to handle ARL, so these IDs are simply added to the MTL ID list. Bspec: 55420 Signed-off-by: Matt Roper Signed-off-by: Haridhar Kalvala Reviewed-by: Matt Atwood Link: https://patchwork.freedesktop.org/patch/msgid/20240108122738.14399-2-haridhar.kalvala@intel.com --- include/drm/i915_pciids.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index fcf1849aa47c..07779a11758e 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -751,10 +751,13 @@ /* MTL */ #define INTEL_MTL_IDS(info) \ INTEL_VGA_DEVICE(0x7D40, info), \ + INTEL_VGA_DEVICE(0x7D41, info), \ INTEL_VGA_DEVICE(0x7D45, info), \ + INTEL_VGA_DEVICE(0x7D51, info), \ INTEL_VGA_DEVICE(0x7D55, info), \ INTEL_VGA_DEVICE(0x7D60, info), \ INTEL_VGA_DEVICE(0x7D67, info), \ + INTEL_VGA_DEVICE(0x7DD1, info), \ INTEL_VGA_DEVICE(0x7DD5, info) #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From cf41cebf9dc8143ca7bb0aabb7e0053e16f0515a Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Fri, 19 Jan 2024 10:05:57 +0100 Subject: drm/exec, drm/gpuvm: Prefer u32 over uint32_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The relatively recently introduced drm/exec utility was using uint32_t in its interface, which was then also carried over to drm/gpuvm. Prefer u32 in new code and update drm/exec and drm/gpuvm accordingly. Cc: Christian König Cc: Danilo Krummrich Signed-off-by: Thomas Hellström Reviewed-by: Christian König Reviewed-by: Danilo Krummrich Reviewed-by: Lucas De Marchi Link: https://patchwork.freedesktop.org/patch/msgid/20240119090557.6360-1-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/drm_exec.c | 2 +- include/drm/drm_exec.h | 4 ++-- include/drm/drm_gpuvm.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 5d2809de4517..20e59d88218d 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -72,7 +72,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec) * * Initialize the object and make sure that we can track locked objects. */ -void drm_exec_init(struct drm_exec *exec, uint32_t flags) +void drm_exec_init(struct drm_exec *exec, u32 flags) { exec->flags = flags; exec->objects = kmalloc(PAGE_SIZE, GFP_KERNEL); diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index b5bf0b6da791..187c3ec44606 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -18,7 +18,7 @@ struct drm_exec { /** * @flags: Flags to control locking behavior */ - uint32_t flags; + u32 flags; /** * @ticket: WW ticket used for acquiring locks @@ -135,7 +135,7 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) return !!exec->contended; } -void drm_exec_init(struct drm_exec *exec, uint32_t flags); +void drm_exec_init(struct drm_exec *exec, u32 flags); void drm_exec_fini(struct drm_exec *exec); bool drm_exec_cleanup(struct drm_exec *exec); int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj); diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h index 6258849382e1..0ee13633f1f7 100644 --- a/include/drm/drm_gpuvm.h +++ b/include/drm/drm_gpuvm.h @@ -514,7 +514,7 @@ struct drm_gpuvm_exec { /** * @flags: the flags for the struct drm_exec */ - uint32_t flags; + u32 flags; /** * @vm: the &drm_gpuvm to lock its DMA reservations -- cgit v1.2.3 From a3b6792e990d63d47c4d2622a9704a01db50c3a2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jan 2024 10:05:26 +0100 Subject: video/cmdline: Introduce CONFIG_VIDEO for video= parameter Add CONFIG_VIDEO for common code in drivers/video/. Use the option to select helpers for the video= parameter. Replaces CONFIG_VIDEO_CMDLINE. Other common code in drivers/video/ can be moved behind CONFIG_VIDEO, which will simplify the Kconfig rules. Signed-off-by: Thomas Zimmermann Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20240118090721.7995-2-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 2 +- drivers/video/Kconfig | 3 ++- drivers/video/Makefile | 2 +- drivers/video/fbdev/core/Kconfig | 2 +- include/video/cmdline.h | 7 ------- 5 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 628f90ed8a9b..da8750e1c867 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -19,7 +19,7 @@ menuconfig DRM # gallium uses SYS_kcmp for os_same_file_description() to de-duplicate # device and dmabuf fd. Let's make sure that is available for our userspace. select KCMP - select VIDEO_CMDLINE + select VIDEO select VIDEO_NOMODESET help Kernel-level support for the Direct Rendering Infrastructure (DRI) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index b694d7669d32..253b129a82dc 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -18,8 +18,9 @@ config STI_CORE STI refers to the HP "Standard Text Interface" which is a set of BIOS routines contained in a ROM chip in HP PA-RISC based machines. -config VIDEO_CMDLINE +config VIDEO bool + default n config VIDEO_NOMODESET bool diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6bbc03950899..287c198f0c82 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_APERTURE_HELPERS) += aperture.o obj-$(CONFIG_STI_CORE) += sticore.o obj-$(CONFIG_VGASTATE) += vgastate.o -obj-$(CONFIG_VIDEO_CMDLINE) += cmdline.o +obj-$(CONFIG_VIDEO) += cmdline.o obj-$(CONFIG_VIDEO_NOMODESET) += nomodeset.o obj-$(CONFIG_HDMI) += hdmi.o diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig index 21053bf00dc5..db09fe87fcd4 100644 --- a/drivers/video/fbdev/core/Kconfig +++ b/drivers/video/fbdev/core/Kconfig @@ -4,7 +4,7 @@ # config FB_CORE - select VIDEO_CMDLINE + select VIDEO tristate config FB_NOTIFY diff --git a/include/video/cmdline.h b/include/video/cmdline.h index 26b80cdaef79..77292d1d6173 100644 --- a/include/video/cmdline.h +++ b/include/video/cmdline.h @@ -5,16 +5,9 @@ #include -#if defined(CONFIG_VIDEO_CMDLINE) const char *video_get_options(const char *name); /* exported for compatibility with fbdev; don't use in new code */ bool __video_get_options(const char *name, const char **option, bool is_of); -#else -static inline const char *video_get_options(const char *name) -{ - return NULL; -} -#endif #endif -- cgit v1.2.3 From 55ea87a4f9007a034bd72bc93408d8a579d04177 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jan 2024 10:05:27 +0100 Subject: video/cmdline: Hide __video_get_options() behind CONFIG_FB_CORE The function __video_get_options() only exists for compatibility with old fbdev drivers that cannot be refactored easily. Hide it behind CONFIG_FB_CORE. v2: * support CONFIG_FB_CORE=m via IS_ENABLED() (kernel test robot) Signed-off-by: Thomas Zimmermann Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20240118090721.7995-3-tzimmermann@suse.de --- drivers/video/cmdline.c | 2 ++ include/video/cmdline.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/video/cmdline.c b/drivers/video/cmdline.c index d3d257489c3d..49ee3fb4951a 100644 --- a/drivers/video/cmdline.c +++ b/drivers/video/cmdline.c @@ -80,6 +80,7 @@ const char *video_get_options(const char *name) } EXPORT_SYMBOL(video_get_options); +#if IS_ENABLED(CONFIG_FB_CORE) bool __video_get_options(const char *name, const char **options, bool is_of) { bool enabled = true; @@ -96,6 +97,7 @@ bool __video_get_options(const char *name, const char **options, bool is_of) return enabled; } EXPORT_SYMBOL(__video_get_options); +#endif /* * Process command line options for video adapters. This function is diff --git a/include/video/cmdline.h b/include/video/cmdline.h index 77292d1d6173..76649465bb08 100644 --- a/include/video/cmdline.h +++ b/include/video/cmdline.h @@ -3,11 +3,14 @@ #ifndef VIDEO_CMDLINE_H #define VIDEO_CMDLINE_H +#include #include const char *video_get_options(const char *name); +#if IS_ENABLED(CONFIG_FB_CORE) /* exported for compatibility with fbdev; don't use in new code */ bool __video_get_options(const char *name, const char **option, bool is_of); +#endif #endif -- cgit v1.2.3 From 71fc3249f50ac22f495185872e71393cfa9d6f07 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jan 2024 10:05:28 +0100 Subject: video/nomodeset: Select nomodeset= parameter with CONFIG_VIDEO Enable support for nomodeset= parameter via CONFIG_VIDEO. Both, DRM and fbdev, already select this option. Remove the existing option CONFIG_VIDEO_NOMODESET. Simplifies the Kconfig rules. Signed-off-by: Thomas Zimmermann Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20240118090721.7995-4-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 1 - drivers/staging/sm750fb/Kconfig | 1 - drivers/video/Kconfig | 4 ---- drivers/video/Makefile | 3 +-- drivers/video/fbdev/Kconfig | 37 ------------------------------------- drivers/video/fbdev/core/fbmem.c | 2 -- drivers/video/fbdev/geode/Kconfig | 3 --- include/linux/fb.h | 7 ------- 8 files changed, 1 insertion(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index da8750e1c867..23764316efcf 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -20,7 +20,6 @@ menuconfig DRM # device and dmabuf fd. Let's make sure that is available for our userspace. select KCMP select VIDEO - select VIDEO_NOMODESET help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select diff --git a/drivers/staging/sm750fb/Kconfig b/drivers/staging/sm750fb/Kconfig index ab3d9b057d56..08bcccdd0f1c 100644 --- a/drivers/staging/sm750fb/Kconfig +++ b/drivers/staging/sm750fb/Kconfig @@ -6,7 +6,6 @@ config FB_SM750 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select VIDEO_NOMODESET help Frame buffer driver for the Silicon Motion SM750 chip with 2D acceleration and dual head support. diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 253b129a82dc..130ebccb8338 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -22,10 +22,6 @@ config VIDEO bool default n -config VIDEO_NOMODESET - bool - default n - source "drivers/auxdisplay/Kconfig" if HAS_IOMEM diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 287c198f0c82..9eb5557911de 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -3,8 +3,7 @@ obj-$(CONFIG_APERTURE_HELPERS) += aperture.o obj-$(CONFIG_STI_CORE) += sticore.o obj-$(CONFIG_VGASTATE) += vgastate.o -obj-$(CONFIG_VIDEO) += cmdline.o -obj-$(CONFIG_VIDEO_NOMODESET) += nomodeset.o +obj-$(CONFIG_VIDEO) += cmdline.o nomodeset.o obj-$(CONFIG_HDMI) += hdmi.o obj-$(CONFIG_VT) += console/ diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index d5909a9206ff..f15ba4b2f306 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -75,7 +75,6 @@ config FB_CIRRUS select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help This enables support for Cirrus Logic GD542x/543x based boards on Amiga: SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum. @@ -95,7 +94,6 @@ config FB_PM2 select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help This is the frame buffer device driver for cards based on the 3D Labs Permedia, Permedia 2 and Permedia 2V chips. @@ -179,7 +177,6 @@ config FB_CYBER2000 tristate "CyberPro 2000/2010/5000 support" depends on FB && PCI && (BROKEN || !SPARC64) select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help This enables support for the Integraphics CyberPro 20x0 and 5000 VGA chips used in the Rebel.com Netwinder and other machines. @@ -330,7 +327,6 @@ config FB_CT65550 bool "Chips 65550 display support" depends on (FB = y) && PPC32 && PCI select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help This is the frame buffer device driver for the Chips & Technologies 65550 graphics chip in PowerBooks. @@ -339,7 +335,6 @@ config FB_ASILIANT bool "Asiliant (Chips) 69000 display support" depends on (FB = y) && PCI select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help This is the frame buffer device driver for the Asiliant 69030 chipset @@ -349,7 +344,6 @@ config FB_IMSTT select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS select FB_MACMODES if PPC_PMAC - select VIDEO_NOMODESET help The IMS Twin Turbo is a PCI-based frame buffer card bundled with many Macintosh and compatible computers. @@ -414,7 +408,6 @@ config FB_TGA select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help This is the frame buffer device driver for generic TGA and SFB+ graphic cards. These include DEC ZLXp-E1, -E2 and -E3 PCI cards, @@ -591,7 +584,6 @@ config FB_XVR500 select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help This is the framebuffer device for the Sun XVR-500 and similar graphics cards based upon the 3DLABS Wildcat chipset. The driver @@ -603,7 +595,6 @@ config FB_XVR2500 bool "Sun XVR-2500 3DLABS Wildcat support" depends on (FB = y) && PCI && SPARC64 select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help This is the framebuffer device for the Sun XVR-2500 and similar graphics cards based upon the 3DLABS Wildcat chipset. The driver @@ -629,7 +620,6 @@ config FB_PVR2 select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help Say Y here if you have a PowerVR 2 card in your box. If you plan to run linux on your Dreamcast, you will have to say Y here. @@ -692,7 +682,6 @@ config FB_NVIDIA select FB_IOMEM_FOPS select BITREVERSE select VGASTATE - select VIDEO_NOMODESET help This driver supports graphics boards with the nVidia chips, TNT and newer. For very old chipsets, such as the RIVA128, then use @@ -741,7 +730,6 @@ config FB_RIVA select FB_MODE_HELPERS select BITREVERSE select VGASTATE - select VIDEO_NOMODESET help This driver supports graphics boards with the nVidia Riva/Geforce chips. @@ -784,7 +772,6 @@ config FB_I740 select FB_IOMEM_HELPERS select FB_MODE_HELPERS select VGASTATE - select VIDEO_NOMODESET select FB_DDC help This driver supports graphics cards based on Intel740 chip. @@ -795,7 +782,6 @@ config FB_I810 select FB_IOMEM_FOPS select FB_MODE_HELPERS select VGASTATE - select VIDEO_NOMODESET help This driver supports the on-board graphics built in to the Intel 810 and 815 chipsets. Say Y if you have and plan to use such a board. @@ -844,7 +830,6 @@ config FB_LE80578 depends on FB && PCI && X86 select FB_IOMEM_HELPERS select FB_MODE_HELPERS - select VIDEO_NOMODESET help This driver supports the LE80578 (Vermilion Range) chipset @@ -863,7 +848,6 @@ config FB_INTEL select FB_IOMEM_FOPS select FB_MODE_HELPERS select BOOT_VESA_SUPPORT if FB_INTEL = y - select VIDEO_NOMODESET depends on !DRM_I915 help This driver supports the on-board graphics built in to the Intel @@ -902,7 +886,6 @@ config FB_MATROX select FB_IOMEM_FOPS select FB_TILEBLITTING select FB_MACMODES if PPC_PMAC - select VIDEO_NOMODESET help Say Y here if you have a Matrox Millennium, Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox @@ -1025,7 +1008,6 @@ config FB_RADEON select FB_IOMEM_FOPS select FB_MACMODES if PPC select FB_MODE_HELPERS - select VIDEO_NOMODESET help Choose this option if you want to use an ATI Radeon graphics card as a framebuffer device. There are both PCI and AGP versions. You @@ -1063,7 +1045,6 @@ config FB_ATY128 select FB_BACKLIGHT if FB_ATY128_BACKLIGHT select FB_IOMEM_HELPERS select FB_MACMODES if PPC_PMAC - select VIDEO_NOMODESET help This driver supports graphics boards with the ATI Rage128 chips. Say Y if you have such a graphics board and read @@ -1089,7 +1070,6 @@ config FB_ATY select FB_IOMEM_FOPS select FB_MACMODES if PPC select FB_ATY_CT if SPARC64 && PCI - select VIDEO_NOMODESET help This driver supports graphics boards with the ATI Mach64 chips. Say Y if you have such a graphics board. @@ -1141,7 +1121,6 @@ config FB_S3 select FB_TILEBLITTING select FB_SVGALIB select VGASTATE - select VIDEO_NOMODESET select FONT_8x16 if FRAMEBUFFER_CONSOLE help Driver for graphics boards with S3 Trio / S3 Virge chip. @@ -1163,7 +1142,6 @@ config FB_SAVAGE select FB_IOMEM_FOPS select FB_MODE_HELPERS select VGASTATE - select VIDEO_NOMODESET help This driver supports notebooks and computers with S3 Savage PCI/AGP chips. @@ -1203,7 +1181,6 @@ config FB_SIS select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS select FB_SIS_300 if !FB_SIS_315 - select VIDEO_NOMODESET help This is the frame buffer device driver for the SiS 300, 315, 330 and 340 series as well as XGI V3XT, V5, V8, Z7 graphics chipsets. @@ -1234,7 +1211,6 @@ config FB_VIA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS select I2C_ALGOBIT - select VIDEO_NOMODESET help This is the frame buffer device driver for Graphics chips of VIA UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ @@ -1275,7 +1251,6 @@ config FB_NEOMAGIC select FB_IOMEM_FOPS select FB_MODE_HELPERS select VGASTATE - select VIDEO_NOMODESET help This driver supports notebooks with NeoMagic PCI chips. Say Y if you have such a graphics card. @@ -1287,7 +1262,6 @@ config FB_KYRO tristate "IMG Kyro support" depends on FB && PCI select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Say Y here if you have a STG4000 / Kyro / PowerVR 3 based graphics board. @@ -1303,7 +1277,6 @@ config FB_3DFX select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS select FB_MODE_HELPERS - select VIDEO_NOMODESET help This driver supports graphics boards with the 3Dfx Banshee, Voodoo3 or VSA-100 (aka Voodoo4/5) chips. Say Y if you have @@ -1332,7 +1305,6 @@ config FB_VOODOO1 depends on FB && PCI depends on FB_DEVICE select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or Voodoo2 (cvg) based graphics card. @@ -1355,7 +1327,6 @@ config FB_VT8623 select FB_TILEBLITTING select FB_SVGALIB select VGASTATE - select VIDEO_NOMODESET select FONT_8x16 if FRAMEBUFFER_CONSOLE help Driver for CastleRock integrated graphics core in the @@ -1370,7 +1341,6 @@ config FB_TRIDENT select FB_DDC select FB_IOMEM_FOPS select FB_MODE_HELPERS - select VIDEO_NOMODESET help This is the frame buffer device driver for Trident PCI/AGP chipsets. Supported chipset families are TGUI 9440/96XX, 3DImage, Blade3D @@ -1395,7 +1365,6 @@ config FB_ARK select FB_TILEBLITTING select FB_SVGALIB select VGASTATE - select VIDEO_NOMODESET select FONT_8x16 if FRAMEBUFFER_CONSOLE help Driver for PCI graphics boards with ARK 2000PV chip @@ -1408,7 +1377,6 @@ config FB_PM3 select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select VIDEO_NOMODESET help This is the frame buffer device driver for the 3DLabs Permedia3 chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & @@ -1419,7 +1387,6 @@ config FB_CARMINE tristate "Fujitsu carmine frame buffer support" depends on FB && PCI select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help This is the frame buffer device driver for the Fujitsu Carmine chip. The driver provides two independent frame buffer devices. @@ -1701,7 +1668,6 @@ config FB_IBM_GXT4500 tristate "Framebuffer support for IBM GXT4000P/4500P/6000P/6500P adaptors" depends on FB select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Say Y here to enable support for the IBM GXT4000P/6000P and GXT4500P/6500P display adaptor based on Raster Engine RC1000, @@ -1819,7 +1785,6 @@ config FB_MB862XX depends on FB depends on PCI || (OF && PPC) select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Frame buffer driver for Fujitsu Carmine/Coral-P(A)/Lime controllers. @@ -1885,7 +1850,6 @@ config FB_HYPERV depends on FB && HYPERV select DMA_CMA if HAVE_DMA_CONTIGUOUS && CMA select FB_IOMEM_HELPERS_DEFERRED - select VIDEO_NOMODESET help This framebuffer driver supports Microsoft Hyper-V Synthetic Video. @@ -1919,7 +1883,6 @@ config FB_SM712 tristate "Silicon Motion SM712 framebuffer support" depends on FB && PCI select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Frame buffer driver for the Silicon Motion SM710, SM712, SM721 and SM722 chips. diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index fc206755f5f6..48287366e0d4 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -645,7 +645,6 @@ int fb_new_modelist(struct fb_info *info) return 0; } -#if defined(CONFIG_VIDEO_NOMODESET) bool fb_modesetting_disabled(const char *drvname) { bool fwonly = video_firmware_drivers_only(); @@ -657,6 +656,5 @@ bool fb_modesetting_disabled(const char *drvname) return fwonly; } EXPORT_SYMBOL(fb_modesetting_disabled); -#endif MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/geode/Kconfig b/drivers/video/fbdev/geode/Kconfig index 9a49916e0492..3b20420cc94d 100644 --- a/drivers/video/fbdev/geode/Kconfig +++ b/drivers/video/fbdev/geode/Kconfig @@ -14,7 +14,6 @@ config FB_GEODE_LX tristate "AMD Geode LX framebuffer support" depends on FB && FB_GEODE select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Framebuffer driver for the display controller integrated into the AMD Geode LX processors. @@ -28,7 +27,6 @@ config FB_GEODE_GX tristate "AMD Geode GX framebuffer support" depends on FB && FB_GEODE select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Framebuffer driver for the display controller integrated into the AMD Geode GX processors. @@ -42,7 +40,6 @@ config FB_GEODE_GX1 tristate "AMD Geode GX1 framebuffer support" depends on FB && FB_GEODE select FB_IOMEM_HELPERS - select VIDEO_NOMODESET help Framebuffer driver for the display controller integrated into the AMD Geode GX1 processor. diff --git a/include/linux/fb.h b/include/linux/fb.h index 05dc9624897d..2ce2f5c2fca9 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -840,14 +840,7 @@ extern int fb_find_mode(struct fb_var_screeninfo *var, const struct fb_videomode *default_mode, unsigned int default_bpp); -#if defined(CONFIG_VIDEO_NOMODESET) bool fb_modesetting_disabled(const char *drvname); -#else -static inline bool fb_modesetting_disabled(const char *drvname) -{ - return false; -} -#endif /* * Convenience logging macros -- cgit v1.2.3 From 1dccdba084897443d116508a8ed71e0ac8a031a4 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 21 Nov 2023 07:32:59 +0100 Subject: drm/etnaviv: Expose a few more chipspecs to userspace These ones will be needed to make use fo the NN and TP units in the NPUs based on Vivante IP. Also fix the number of NN cores in the VIPNano-qi. Signed-off-by: Tomeu Vizoso Acked-by: Christian Gmeiner Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 20 ++++++++++++++++++++ drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 12 ++++++++++++ drivers/gpu/drm/etnaviv/etnaviv_hwdb.c | 34 ++++++++++++++++++++++++++++++++++ include/uapi/drm/etnaviv_drm.h | 5 +++++ 4 files changed, 71 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 1dcfa23db3be..18725ff5c79b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -164,6 +164,26 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) *value = gpu->identity.eco_id; break; + case ETNAVIV_PARAM_GPU_NN_CORE_COUNT: + *value = gpu->identity.nn_core_count; + break; + + case ETNAVIV_PARAM_GPU_NN_MAD_PER_CORE: + *value = gpu->identity.nn_mad_per_core; + break; + + case ETNAVIV_PARAM_GPU_TP_CORE_COUNT: + *value = gpu->identity.tp_core_count; + break; + + case ETNAVIV_PARAM_GPU_ON_CHIP_SRAM_SIZE: + *value = gpu->identity.on_chip_sram_size; + break; + + case ETNAVIV_PARAM_GPU_AXI_SRAM_SIZE: + *value = gpu->identity.axi_sram_size; + break; + default: DBG("%s: invalid param: %u", dev_name(gpu->dev), param); return -EINVAL; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index 197e0037732e..7d5e9158e13c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -54,6 +54,18 @@ struct etnaviv_chip_identity { /* Number of Neural Network cores. */ u32 nn_core_count; + /* Number of MAD units per Neural Network core. */ + u32 nn_mad_per_core; + + /* Number of Tensor Processing cores. */ + u32 tp_core_count; + + /* Size in bytes of the SRAM inside the NPU. */ + u32 on_chip_sram_size; + + /* Size in bytes of the SRAM across the AXI bus. */ + u32 axi_sram_size; + /* Size of the vertex cache. */ u32 vertex_cache_size; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c index 67201242438b..9eb8ca7c5034 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c @@ -17,6 +17,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 128, .shader_core_count = 1, .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 8, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, @@ -48,6 +52,11 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .register_max = 64, .thread_count = 256, .shader_core_count = 1, + .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 8, .vertex_output_buffer_size = 512, .pixel_pipes = 1, @@ -80,6 +89,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 512, .shader_core_count = 2, .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, @@ -112,6 +125,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 512, .shader_core_count = 2, .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, @@ -143,6 +160,11 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .register_max = 64, .thread_count = 512, .shader_core_count = 2, + .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, @@ -175,6 +197,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 1024, .shader_core_count = 4, .nn_core_count = 0, + .nn_mad_per_core = 0, + .tp_core_count = 0, + .on_chip_sram_size = 0, + .axi_sram_size = 0, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 2, @@ -207,6 +233,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 256, .shader_core_count = 1, .nn_core_count = 8, + .nn_mad_per_core = 64, + .tp_core_count = 4, + .on_chip_sram_size = 524288, + .axi_sram_size = 1048576, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, @@ -239,6 +269,10 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .thread_count = 256, .shader_core_count = 1, .nn_core_count = 6, + .nn_mad_per_core = 64, + .tp_core_count = 3, + .on_chip_sram_size = 262144, + .axi_sram_size = 0, .vertex_cache_size = 16, .vertex_output_buffer_size = 1024, .pixel_pipes = 1, diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index af024d90453d..d87410a8443a 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -77,6 +77,11 @@ struct drm_etnaviv_timespec { #define ETNAVIV_PARAM_GPU_PRODUCT_ID 0x1c #define ETNAVIV_PARAM_GPU_CUSTOMER_ID 0x1d #define ETNAVIV_PARAM_GPU_ECO_ID 0x1e +#define ETNAVIV_PARAM_GPU_NN_CORE_COUNT 0x1f +#define ETNAVIV_PARAM_GPU_NN_MAD_PER_CORE 0x20 +#define ETNAVIV_PARAM_GPU_TP_CORE_COUNT 0x21 +#define ETNAVIV_PARAM_GPU_ON_CHIP_SRAM_SIZE 0x22 +#define ETNAVIV_PARAM_GPU_AXI_SRAM_SIZE 0x23 #define ETNA_MAX_PIPES 4 -- cgit v1.2.3 From a78a8da51b36c7a0c0c16233f91d60aac03a5a49 Mon Sep 17 00:00:00 2001 From: Somalapuram Amaranath Date: Mon, 13 Nov 2023 16:18:44 +0530 Subject: drm/ttm: replace busy placement with flags v6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of a list of separate busy placement add flags which indicate that a placement should only be used when there is room or if we need to evict. v2: add missing TTM_PL_FLAG_IDLE for i915 v3: fix auto build test ERROR on drm-tip/drm-tip v4: fix some typos pointed out by checkpatch v5: cleanup some rebase problems with VMWGFX v6: implement some missing VMWGFX functionality pointed out by Zack, rename the flags as suggested by Michel, rebase on drm-tip and adjust XE as well Signed-off-by: Christian König Signed-off-by: Somalapuram Amaranath Reviewed-by: Zack Rusin Reviewed-by: Thomas Zimmermann Reviewed-by: Thomas Hellström Link: https://patchwork.freedesktop.org/patch/msgid/20240112125158.2748-4-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 6 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 11 +---- drivers/gpu/drm/drm_gem_vram_helper.c | 2 - drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 37 ++++++--------- drivers/gpu/drm/loongson/lsdc_ttm.c | 2 - drivers/gpu/drm/nouveau/nouveau_bo.c | 59 ++++++++++-------------- drivers/gpu/drm/nouveau/nouveau_bo.h | 1 - drivers/gpu/drm/qxl/qxl_object.c | 2 - drivers/gpu/drm/qxl/qxl_ttm.c | 2 - drivers/gpu/drm/radeon/radeon_object.c | 2 - drivers/gpu/drm/radeon/radeon_ttm.c | 8 +--- drivers/gpu/drm/radeon/radeon_uvd.c | 1 - drivers/gpu/drm/ttm/ttm_bo.c | 21 +++++---- drivers/gpu/drm/ttm/ttm_resource.c | 73 +++++++----------------------- drivers/gpu/drm/vmwgfx/vmwgfx_bo.c | 33 ++++++++++---- drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 4 -- include/drm/ttm/ttm_placement.h | 10 ++-- include/drm/ttm/ttm_resource.h | 8 +--- 18 files changed, 103 insertions(+), 179 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index cef920a93924..b2e9a2f81d82 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -220,9 +220,6 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain) placement->num_placement = c; placement->placement = places; - - placement->num_busy_placement = c; - placement->busy_placement = places; } /** @@ -1406,8 +1403,7 @@ vm_fault_t amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo) AMDGPU_GEM_DOMAIN_GTT); /* Avoid costly evictions; only set GTT as a busy placement */ - abo->placement.num_busy_placement = 1; - abo->placement.busy_placement = &abo->placements[1]; + abo->placements[0].flags |= TTM_PL_FLAG_DESIRED; r = ttm_bo_validate(bo, &abo->placement, &ctx); if (unlikely(r == -EBUSY || r == -ERESTARTSYS)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 05991c5c8ddb..46a24d2308aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -102,23 +102,19 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo, /* Don't handle scatter gather BOs */ if (bo->type == ttm_bo_type_sg) { placement->num_placement = 0; - placement->num_busy_placement = 0; return; } /* Object isn't an AMDGPU object so ignore */ if (!amdgpu_bo_is_amdgpu_bo(bo)) { placement->placement = &placements; - placement->busy_placement = &placements; placement->num_placement = 1; - placement->num_busy_placement = 1; return; } abo = ttm_to_amdgpu_bo(bo); if (abo->flags & AMDGPU_GEM_CREATE_DISCARDABLE) { placement->num_placement = 0; - placement->num_busy_placement = 0; return; } @@ -128,13 +124,13 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo, case AMDGPU_PL_OA: case AMDGPU_PL_DOORBELL: placement->num_placement = 0; - placement->num_busy_placement = 0; return; case TTM_PL_VRAM: if (!adev->mman.buffer_funcs_enabled) { /* Move to system memory */ amdgpu_bo_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_CPU); + } else if (!amdgpu_gmc_vram_full_visible(&adev->gmc) && !(abo->flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) && amdgpu_bo_in_cpu_visible_vram(abo)) { @@ -149,8 +145,7 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo, AMDGPU_GEM_DOMAIN_CPU); abo->placements[0].fpfn = adev->gmc.visible_vram_size >> PAGE_SHIFT; abo->placements[0].lpfn = 0; - abo->placement.busy_placement = &abo->placements[1]; - abo->placement.num_busy_placement = 1; + abo->placements[0].flags |= TTM_PL_FLAG_DESIRED; } else { /* Move to GTT memory */ amdgpu_bo_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_GTT | @@ -967,8 +962,6 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo) /* allocate GART space */ placement.num_placement = 1; placement.placement = &placements; - placement.num_busy_placement = 1; - placement.busy_placement = &placements; placements.fpfn = 0; placements.lpfn = adev->gmc.gart_size >> PAGE_SHIFT; placements.mem_type = TTM_PL_TT; diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index b67eafa55715..75f2eaf0d5b6 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -147,7 +147,6 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo, invariant_flags = TTM_PL_FLAG_TOPDOWN; gbo->placement.placement = gbo->placements; - gbo->placement.busy_placement = gbo->placements; if (pl_flag & DRM_GEM_VRAM_PL_FLAG_VRAM) { gbo->placements[c].mem_type = TTM_PL_VRAM; @@ -160,7 +159,6 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo, } gbo->placement.num_placement = c; - gbo->placement.num_busy_placement = c; for (i = 0; i < c; ++i) { gbo->placements[i].fpfn = 0; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 9227f8146a58..a6b0aaf30cbe 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -65,8 +65,6 @@ static const struct ttm_place sys_placement_flags = { static struct ttm_placement i915_sys_placement = { .num_placement = 1, .placement = &sys_placement_flags, - .num_busy_placement = 1, - .busy_placement = &sys_placement_flags, }; /** @@ -157,32 +155,28 @@ i915_ttm_place_from_region(const struct intel_memory_region *mr, static void i915_ttm_placement_from_obj(const struct drm_i915_gem_object *obj, - struct ttm_place *requested, - struct ttm_place *busy, + struct ttm_place *places, struct ttm_placement *placement) { unsigned int num_allowed = obj->mm.n_placements; unsigned int flags = obj->flags; unsigned int i; - placement->num_placement = 1; + places[0].flags |= TTM_PL_FLAG_DESIRED; i915_ttm_place_from_region(num_allowed ? obj->mm.placements[0] : - obj->mm.region, requested, obj->bo_offset, + obj->mm.region, &places[0], obj->bo_offset, obj->base.size, flags); /* Cache this on object? */ - placement->num_busy_placement = num_allowed; - for (i = 0; i < placement->num_busy_placement; ++i) - i915_ttm_place_from_region(obj->mm.placements[i], busy + i, - obj->bo_offset, obj->base.size, flags); - - if (num_allowed == 0) { - *busy = *requested; - placement->num_busy_placement = 1; + for (i = 0; i < num_allowed; ++i) { + i915_ttm_place_from_region(obj->mm.placements[i], + &places[i + 1], obj->bo_offset, + obj->base.size, flags); + places[i + 1].flags |= TTM_PL_FLAG_FALLBACK; } - placement->placement = requested; - placement->busy_placement = busy; + placement->num_placement = num_allowed + 1; + placement->placement = places; } static int i915_ttm_tt_shmem_populate(struct ttm_device *bdev, @@ -789,7 +783,8 @@ static int __i915_ttm_get_pages(struct drm_i915_gem_object *obj, int ret; /* First try only the requested placement. No eviction. */ - real_num_busy = fetch_and_zero(&placement->num_busy_placement); + real_num_busy = placement->num_placement; + placement->num_placement = 1; ret = ttm_bo_validate(bo, placement, &ctx); if (ret) { ret = i915_ttm_err_to_gem(ret); @@ -805,7 +800,7 @@ static int __i915_ttm_get_pages(struct drm_i915_gem_object *obj, * If the initial attempt fails, allow all accepted placements, * evicting if necessary. */ - placement->num_busy_placement = real_num_busy; + placement->num_placement = real_num_busy; ret = ttm_bo_validate(bo, placement, &ctx); if (ret) return i915_ttm_err_to_gem(ret); @@ -839,7 +834,7 @@ static int __i915_ttm_get_pages(struct drm_i915_gem_object *obj, static int i915_ttm_get_pages(struct drm_i915_gem_object *obj) { - struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS]; + struct ttm_place places[I915_TTM_MAX_PLACEMENTS + 1]; struct ttm_placement placement; /* restricted by sg_alloc_table */ @@ -849,7 +844,7 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj) GEM_BUG_ON(obj->mm.n_placements > I915_TTM_MAX_PLACEMENTS); /* Move to the requested placement. */ - i915_ttm_placement_from_obj(obj, &requested, busy, &placement); + i915_ttm_placement_from_obj(obj, places, &placement); return __i915_ttm_get_pages(obj, &placement); } @@ -879,9 +874,7 @@ static int __i915_ttm_migrate(struct drm_i915_gem_object *obj, i915_ttm_place_from_region(mr, &requested, obj->bo_offset, obj->base.size, flags); placement.num_placement = 1; - placement.num_busy_placement = 1; placement.placement = &requested; - placement.busy_placement = &requested; ret = __i915_ttm_get_pages(obj, &placement); if (ret) diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.c b/drivers/gpu/drm/loongson/lsdc_ttm.c index bf79dc55afa4..465f622ac05d 100644 --- a/drivers/gpu/drm/loongson/lsdc_ttm.c +++ b/drivers/gpu/drm/loongson/lsdc_ttm.c @@ -54,7 +54,6 @@ static void lsdc_bo_set_placement(struct lsdc_bo *lbo, u32 domain) pflags |= TTM_PL_FLAG_TOPDOWN; lbo->placement.placement = lbo->placements; - lbo->placement.busy_placement = lbo->placements; if (domain & LSDC_GEM_DOMAIN_VRAM) { lbo->placements[c].mem_type = TTM_PL_VRAM; @@ -77,7 +76,6 @@ static void lsdc_bo_set_placement(struct lsdc_bo *lbo, u32 domain) } lbo->placement.num_placement = c; - lbo->placement.num_busy_placement = c; for (i = 0; i < c; ++i) { lbo->placements[i].fpfn = 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index b7dda486a7ea..50ee9d730066 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -403,27 +403,6 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align, return 0; } -static void -set_placement_list(struct ttm_place *pl, unsigned *n, uint32_t domain) -{ - *n = 0; - - if (domain & NOUVEAU_GEM_DOMAIN_VRAM) { - pl[*n].mem_type = TTM_PL_VRAM; - pl[*n].flags = 0; - (*n)++; - } - if (domain & NOUVEAU_GEM_DOMAIN_GART) { - pl[*n].mem_type = TTM_PL_TT; - pl[*n].flags = 0; - (*n)++; - } - if (domain & NOUVEAU_GEM_DOMAIN_CPU) { - pl[*n].mem_type = TTM_PL_SYSTEM; - pl[(*n)++].flags = 0; - } -} - static void set_placement_range(struct nouveau_bo *nvbo, uint32_t domain) { @@ -451,10 +430,6 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t domain) nvbo->placements[i].fpfn = fpfn; nvbo->placements[i].lpfn = lpfn; } - for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { - nvbo->busy_placements[i].fpfn = fpfn; - nvbo->busy_placements[i].lpfn = lpfn; - } } } @@ -462,15 +437,32 @@ void nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t domain, uint32_t busy) { - struct ttm_placement *pl = &nvbo->placement; + unsigned int *n = &nvbo->placement.num_placement; + struct ttm_place *pl = nvbo->placements; - pl->placement = nvbo->placements; - set_placement_list(nvbo->placements, &pl->num_placement, domain); + domain |= busy; - pl->busy_placement = nvbo->busy_placements; - set_placement_list(nvbo->busy_placements, &pl->num_busy_placement, - domain | busy); + *n = 0; + if (domain & NOUVEAU_GEM_DOMAIN_VRAM) { + pl[*n].mem_type = TTM_PL_VRAM; + pl[*n].flags = busy & NOUVEAU_GEM_DOMAIN_VRAM ? + TTM_PL_FLAG_FALLBACK : 0; + (*n)++; + } + if (domain & NOUVEAU_GEM_DOMAIN_GART) { + pl[*n].mem_type = TTM_PL_TT; + pl[*n].flags = busy & NOUVEAU_GEM_DOMAIN_GART ? + TTM_PL_FLAG_FALLBACK : 0; + (*n)++; + } + if (domain & NOUVEAU_GEM_DOMAIN_CPU) { + pl[*n].mem_type = TTM_PL_SYSTEM; + pl[*n].flags = busy & NOUVEAU_GEM_DOMAIN_CPU ? + TTM_PL_FLAG_FALLBACK : 0; + (*n)++; + } + nvbo->placement.placement = nvbo->placements; set_placement_range(nvbo, domain); } @@ -1313,11 +1305,6 @@ vm_fault_t nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) nvbo->placements[i].lpfn = mappable; } - for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { - nvbo->busy_placements[i].fpfn = 0; - nvbo->busy_placements[i].lpfn = mappable; - } - nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, 0); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 70c551921a9e..e9dfab6a8156 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -15,7 +15,6 @@ struct nouveau_bo { struct ttm_placement placement; u32 valid_domains; struct ttm_place placements[3]; - struct ttm_place busy_placements[3]; bool force_coherent; struct ttm_bo_kmap_obj kmap; struct list_head head; diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index 06a58dad5f5c..1e46b0a6e478 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -66,7 +66,6 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain) pflag |= TTM_PL_FLAG_TOPDOWN; qbo->placement.placement = qbo->placements; - qbo->placement.busy_placement = qbo->placements; if (domain == QXL_GEM_DOMAIN_VRAM) { qbo->placements[c].mem_type = TTM_PL_VRAM; qbo->placements[c++].flags = pflag; @@ -86,7 +85,6 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain) qbo->placements[c++].flags = 0; } qbo->placement.num_placement = c; - qbo->placement.num_busy_placement = c; for (i = 0; i < c; ++i) { qbo->placements[i].fpfn = 0; qbo->placements[i].lpfn = 0; diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 1a82629bce3f..765a144cea14 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -60,9 +60,7 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo, if (!qxl_ttm_bo_is_qxl_bo(bo)) { placement->placement = &placements; - placement->busy_placement = &placements; placement->num_placement = 1; - placement->num_busy_placement = 1; return; } qbo = to_qxl_bo(bo); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 10c0fbd9d2b4..a955f8a2f7fe 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -78,7 +78,6 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) u32 c = 0, i; rbo->placement.placement = rbo->placements; - rbo->placement.busy_placement = rbo->placements; if (domain & RADEON_GEM_DOMAIN_VRAM) { /* Try placing BOs which don't need CPU access outside of the * CPU accessible part of VRAM @@ -114,7 +113,6 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) } rbo->placement.num_placement = c; - rbo->placement.num_busy_placement = c; for (i = 0; i < c; ++i) { if ((rbo->flags & RADEON_GEM_CPU_ACCESS) && diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index de4e6d78f1e1..2078b0000e22 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -92,9 +92,7 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo, if (!radeon_ttm_bo_is_radeon_bo(bo)) { placement->placement = &placements; - placement->busy_placement = &placements; placement->num_placement = 1; - placement->num_busy_placement = 1; return; } rbo = container_of(bo, struct radeon_bo, tbo); @@ -114,15 +112,11 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo, */ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM | RADEON_GEM_DOMAIN_GTT); - rbo->placement.num_busy_placement = 0; for (i = 0; i < rbo->placement.num_placement; i++) { if (rbo->placements[i].mem_type == TTM_PL_VRAM) { if (rbo->placements[i].fpfn < fpfn) rbo->placements[i].fpfn = fpfn; - } else { - rbo->placement.busy_placement = - &rbo->placements[i]; - rbo->placement.num_busy_placement = 1; + rbo->placements[0].flags |= TTM_PL_FLAG_DESIRED; } } } else diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index a2cda184b2b2..058a1c8451b2 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -324,7 +324,6 @@ void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo, rbo->placements[1].fpfn += (256 * 1024 * 1024) >> PAGE_SHIFT; rbo->placements[1].lpfn += (256 * 1024 * 1024) >> PAGE_SHIFT; rbo->placement.num_placement++; - rbo->placement.num_busy_placement++; } void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index f95b0406ca99..ba3f09e2d7e6 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -410,8 +410,8 @@ static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, struct ttm_resource *hop_mem; int ret; - hop_placement.num_placement = hop_placement.num_busy_placement = 1; - hop_placement.placement = hop_placement.busy_placement = hop; + hop_placement.num_placement = 1; + hop_placement.placement = hop; /* find space in the bounce domain */ ret = ttm_bo_mem_space(bo, &hop_placement, &hop_mem, ctx); @@ -440,10 +440,9 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, dma_resv_assert_held(bo->base.resv); placement.num_placement = 0; - placement.num_busy_placement = 0; bdev->funcs->evict_flags(bo, &placement); - if (!placement.num_placement && !placement.num_busy_placement) { + if (!placement.num_placement) { ret = ttm_bo_wait_ctx(bo, ctx); if (ret) return ret; @@ -791,6 +790,9 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, const struct ttm_place *place = &placement->placement[i]; struct ttm_resource_manager *man; + if (place->flags & TTM_PL_FLAG_FALLBACK) + continue; + man = ttm_manager_type(bdev, place->mem_type); if (!man || !ttm_resource_manager_used(man)) continue; @@ -813,10 +815,13 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, return 0; } - for (i = 0; i < placement->num_busy_placement; ++i) { - const struct ttm_place *place = &placement->busy_placement[i]; + for (i = 0; i < placement->num_placement; ++i) { + const struct ttm_place *place = &placement->placement[i]; struct ttm_resource_manager *man; + if (place->flags & TTM_PL_FLAG_DESIRED) + continue; + man = ttm_manager_type(bdev, place->mem_type); if (!man || !ttm_resource_manager_used(man)) continue; @@ -904,11 +909,11 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, /* * Remove the backing store if no placement is given. */ - if (!placement->num_placement && !placement->num_busy_placement) + if (!placement->num_placement) return ttm_bo_pipeline_gutting(bo); /* Check whether we need to move buffer. */ - if (bo->resource && ttm_resource_compat(bo->resource, placement)) + if (bo->resource && ttm_resource_compatible(bo->resource, placement)) return 0; /* Moving of pinned BOs is forbidden */ diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 02b96d23fdb9..fb14f7716cf8 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -291,37 +291,15 @@ bool ttm_resource_intersects(struct ttm_device *bdev, } /** - * ttm_resource_compatible - test for compatibility + * ttm_resource_compatible - check if resource is compatible with placement * - * @bdev: TTM device structure - * @res: The resource to test - * @place: The placement to test - * @size: How many bytes the new allocation needs. - * - * Test if @res compatible with @place and @size. + * @res: the resource to check + * @placement: the placement to check against * - * Returns true if the res placement compatible with @place and @size. + * Returns true if the placement is compatible. */ -bool ttm_resource_compatible(struct ttm_device *bdev, - struct ttm_resource *res, - const struct ttm_place *place, - size_t size) -{ - struct ttm_resource_manager *man; - - if (!res || !place) - return false; - - man = ttm_manager_type(bdev, res->mem_type); - if (!man->func->compatible) - return true; - - return man->func->compatible(man, res, place, size); -} - -static bool ttm_resource_places_compat(struct ttm_resource *res, - const struct ttm_place *places, - unsigned num_placement) +bool ttm_resource_compatible(struct ttm_resource *res, + struct ttm_placement *placement) { struct ttm_buffer_object *bo = res->bo; struct ttm_device *bdev = bo->bdev; @@ -330,44 +308,25 @@ static bool ttm_resource_places_compat(struct ttm_resource *res, if (res->placement & TTM_PL_FLAG_TEMPORARY) return false; - for (i = 0; i < num_placement; i++) { - const struct ttm_place *heap = &places[i]; + for (i = 0; i < placement->num_placement; i++) { + const struct ttm_place *place = &placement->placement[i]; + struct ttm_resource_manager *man; - if (!ttm_resource_compatible(bdev, res, heap, bo->base.size)) + if (res->mem_type != place->mem_type) + continue; + + man = ttm_manager_type(bdev, res->mem_type); + if (man->func->compatible && + !man->func->compatible(man, res, place, bo->base.size)) continue; - if ((res->mem_type == heap->mem_type) && - (!(heap->flags & TTM_PL_FLAG_CONTIGUOUS) || + if ((!(place->flags & TTM_PL_FLAG_CONTIGUOUS) || (res->placement & TTM_PL_FLAG_CONTIGUOUS))) return true; } return false; } -/** - * ttm_resource_compat - check if resource is compatible with placement - * - * @res: the resource to check - * @placement: the placement to check against - * - * Returns true if the placement is compatible. - */ -bool ttm_resource_compat(struct ttm_resource *res, - struct ttm_placement *placement) -{ - if (ttm_resource_places_compat(res, placement->placement, - placement->num_placement)) - return true; - - if ((placement->busy_placement != placement->placement || - placement->num_busy_placement > placement->num_placement) && - ttm_resource_places_compat(res, placement->busy_placement, - placement->num_busy_placement)) - return true; - - return false; -} - void ttm_resource_set_bo(struct ttm_resource *res, struct ttm_buffer_object *bo) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index 2bfac3aad7b7..bfd41ce3c8f4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -742,9 +742,21 @@ void vmw_bo_move_notify(struct ttm_buffer_object *bo, vmw_resource_unbind_list(vbo); } +static u32 placement_flags(u32 domain, u32 desired, u32 fallback) +{ + if (desired & fallback & domain) + return 0; + + if (desired & domain) + return TTM_PL_FLAG_DESIRED; + + return TTM_PL_FLAG_FALLBACK; +} + static u32 -set_placement_list(struct ttm_place *pl, u32 domain) +set_placement_list(struct ttm_place *pl, u32 desired, u32 fallback) { + u32 domain = desired | fallback; u32 n = 0; /* @@ -752,35 +764,40 @@ set_placement_list(struct ttm_place *pl, u32 domain) */ if (domain & VMW_BO_DOMAIN_MOB) { pl[n].mem_type = VMW_PL_MOB; - pl[n].flags = 0; + pl[n].flags = placement_flags(VMW_BO_DOMAIN_MOB, desired, + fallback); pl[n].fpfn = 0; pl[n].lpfn = 0; n++; } if (domain & VMW_BO_DOMAIN_GMR) { pl[n].mem_type = VMW_PL_GMR; - pl[n].flags = 0; + pl[n].flags = placement_flags(VMW_BO_DOMAIN_GMR, desired, + fallback); pl[n].fpfn = 0; pl[n].lpfn = 0; n++; } if (domain & VMW_BO_DOMAIN_VRAM) { pl[n].mem_type = TTM_PL_VRAM; - pl[n].flags = 0; + pl[n].flags = placement_flags(VMW_BO_DOMAIN_VRAM, desired, + fallback); pl[n].fpfn = 0; pl[n].lpfn = 0; n++; } if (domain & VMW_BO_DOMAIN_WAITABLE_SYS) { pl[n].mem_type = VMW_PL_SYSTEM; - pl[n].flags = 0; + pl[n].flags = placement_flags(VMW_BO_DOMAIN_WAITABLE_SYS, + desired, fallback); pl[n].fpfn = 0; pl[n].lpfn = 0; n++; } if (domain & VMW_BO_DOMAIN_SYS) { pl[n].mem_type = TTM_PL_SYSTEM; - pl[n].flags = 0; + pl[n].flags = placement_flags(VMW_BO_DOMAIN_SYS, desired, + fallback); pl[n].fpfn = 0; pl[n].lpfn = 0; n++; @@ -806,7 +823,7 @@ void vmw_bo_placement_set(struct vmw_bo *bo, u32 domain, u32 busy_domain) u32 i; pl->placement = bo->places; - pl->num_placement = set_placement_list(bo->places, domain); + pl->num_placement = set_placement_list(bo->places, domain, busy_domain); if (drm_debug_enabled(DRM_UT_DRIVER) && bo->tbo.resource) { for (i = 0; i < pl->num_placement; ++i) { @@ -821,8 +838,6 @@ void vmw_bo_placement_set(struct vmw_bo *bo, u32 domain, u32 busy_domain) __func__, bo->tbo.resource->mem_type, domain); } - pl->busy_placement = bo->busy_places; - pl->num_busy_placement = set_placement_list(bo->busy_places, busy_domain); } void vmw_bo_placement_set_default_accelerated(struct vmw_bo *bo) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index a84fffcef8e1..4d23d0a70bcb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -46,15 +46,11 @@ static const struct ttm_place sys_placement_flags = { struct ttm_placement vmw_vram_placement = { .num_placement = 1, .placement = &vram_placement_flags, - .num_busy_placement = 1, - .busy_placement = &vram_placement_flags }; struct ttm_placement vmw_sys_placement = { .num_placement = 1, .placement = &sys_placement_flags, - .num_busy_placement = 1, - .busy_placement = &sys_placement_flags }; const size_t vmw_tt_size = sizeof(struct vmw_ttm_tt); diff --git a/include/drm/ttm/ttm_placement.h b/include/drm/ttm/ttm_placement.h index 8074d0f6cae5..b510a4812609 100644 --- a/include/drm/ttm/ttm_placement.h +++ b/include/drm/ttm/ttm_placement.h @@ -64,6 +64,12 @@ /* For multihop handling */ #define TTM_PL_FLAG_TEMPORARY (1 << 2) +/* Placement is never used during eviction */ +#define TTM_PL_FLAG_DESIRED (1 << 3) + +/* Placement is only used during eviction */ +#define TTM_PL_FLAG_FALLBACK (1 << 4) + /** * struct ttm_place * @@ -86,16 +92,12 @@ struct ttm_place { * * @num_placement: number of preferred placements * @placement: preferred placements - * @num_busy_placement: number of preferred placements when need to evict buffer - * @busy_placement: preferred placements when need to evict buffer * * Structure indicating the placement you request for an object. */ struct ttm_placement { unsigned num_placement; const struct ttm_place *placement; - unsigned num_busy_placement; - const struct ttm_place *busy_placement; }; #endif diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 78a226eba953..1afa13f0c22b 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -365,12 +365,8 @@ bool ttm_resource_intersects(struct ttm_device *bdev, struct ttm_resource *res, const struct ttm_place *place, size_t size); -bool ttm_resource_compatible(struct ttm_device *bdev, - struct ttm_resource *res, - const struct ttm_place *place, - size_t size); -bool ttm_resource_compat(struct ttm_resource *res, - struct ttm_placement *placement); +bool ttm_resource_compatible(struct ttm_resource *res, + struct ttm_placement *placement); void ttm_resource_set_bo(struct ttm_resource *res, struct ttm_buffer_object *bo); -- cgit v1.2.3 From 71ce046327cfd3aef3f93d1c44e091395eb03f8f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Fri, 5 Jan 2024 08:51:05 -0500 Subject: drm/ttm: Make sure the mapped tt pages are decrypted when needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some drivers require the mapped tt pages to be decrypted. In an ideal world this would have been handled by the dma layer, but the TTM page fault handling would have to be rewritten to able to do that. A side-effect of the TTM page fault handling is using a dma allocation per order (via ttm_pool_alloc_page) which makes it impossible to just trivially use dma_mmap_attrs. As a result ttm has to be very careful about trying to make its pgprot for the mapped tt pages match what the dma layer thinks it is. At the ttm layer it's possible to deduce the requirement to have tt pages decrypted by checking whether coherent dma allocations have been requested and the system is running with confidential computing technologies. This approach isn't ideal but keeping TTM matching DMAs expectations for the page properties is in general fragile, unfortunately proper fix would require a rewrite of TTM's page fault handling. Fixes vmwgfx with SEV enabled. v2: Explicitly include cc_platform.h v3: Use CC_ATTR_GUEST_MEM_ENCRYPT instead of CC_ATTR_MEM_ENCRYPT to limit the scope to guests and log when memory decryption is enabled. Signed-off-by: Zack Rusin Fixes: 3bf3710e3718 ("drm/ttm: Add a generic TTM memcpy move for page-based iomem") Reviewed-by: Thomas Hellström Acked-by: Christian König Cc: Huang Rui Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: # v5.14+ Link: https://patchwork.freedesktop.org/patch/msgid/20230926040359.3040017-1-zack@kde.org --- drivers/gpu/drm/ttm/ttm_bo_util.c | 13 +++++++++++-- drivers/gpu/drm/ttm/ttm_tt.c | 12 ++++++++++++ include/drm/ttm/ttm_tt.h | 9 ++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index fd9fd3d15101..0b3f4267130c 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -294,7 +294,13 @@ pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, enum ttm_caching caching; man = ttm_manager_type(bo->bdev, res->mem_type); - caching = man->use_tt ? bo->ttm->caching : res->bus.caching; + if (man->use_tt) { + caching = bo->ttm->caching; + if (bo->ttm->page_flags & TTM_TT_FLAG_DECRYPTED) + tmp = pgprot_decrypted(tmp); + } else { + caching = res->bus.caching; + } return ttm_prot_from_caching(caching, tmp); } @@ -337,6 +343,8 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, .no_wait_gpu = false }; struct ttm_tt *ttm = bo->ttm; + struct ttm_resource_manager *man = + ttm_manager_type(bo->bdev, bo->resource->mem_type); pgprot_t prot; int ret; @@ -346,7 +354,8 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, if (ret) return ret; - if (num_pages == 1 && ttm->caching == ttm_cached) { + if (num_pages == 1 && ttm->caching == ttm_cached && + !(man->use_tt && (ttm->page_flags & TTM_TT_FLAG_DECRYPTED))) { /* * We're mapping a single page, and the desired * page protection is consistent with the bo. diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index d978dc539a9b..578a7c37f00b 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -31,11 +31,13 @@ #define pr_fmt(fmt) "[TTM] " fmt +#include #include #include #include #include #include +#include #include #include #include @@ -61,6 +63,7 @@ static atomic_long_t ttm_dma32_pages_allocated; int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc) { struct ttm_device *bdev = bo->bdev; + struct drm_device *ddev = bo->base.dev; uint32_t page_flags = 0; dma_resv_assert_held(bo->base.resv); @@ -82,6 +85,15 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc) pr_err("Illegal buffer object type\n"); return -EINVAL; } + /* + * When using dma_alloc_coherent with memory encryption the + * mapped TT pages need to be decrypted or otherwise the drivers + * will end up sending encrypted mem to the gpu. + */ + if (bdev->pool.use_dma_alloc && cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { + page_flags |= TTM_TT_FLAG_DECRYPTED; + drm_info(ddev, "TT memory decryption enabled."); + } bo->ttm = bdev->funcs->ttm_tt_create(bo, page_flags); if (unlikely(bo->ttm == NULL)) diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h index a4eff85b1f44..2b9d856ff388 100644 --- a/include/drm/ttm/ttm_tt.h +++ b/include/drm/ttm/ttm_tt.h @@ -79,6 +79,12 @@ struct ttm_tt { * page_flags = TTM_TT_FLAG_EXTERNAL | * TTM_TT_FLAG_EXTERNAL_MAPPABLE; * + * TTM_TT_FLAG_DECRYPTED: The mapped ttm pages should be marked as + * not encrypted. The framework will try to match what the dma layer + * is doing, but note that it is a little fragile because ttm page + * fault handling abuses the DMA api a bit and dma_map_attrs can't be + * used to assure pgprot always matches. + * * TTM_TT_FLAG_PRIV_POPULATED: TTM internal only. DO NOT USE. This is * set by TTM after ttm_tt_populate() has successfully returned, and is * then unset when TTM calls ttm_tt_unpopulate(). @@ -87,8 +93,9 @@ struct ttm_tt { #define TTM_TT_FLAG_ZERO_ALLOC BIT(1) #define TTM_TT_FLAG_EXTERNAL BIT(2) #define TTM_TT_FLAG_EXTERNAL_MAPPABLE BIT(3) +#define TTM_TT_FLAG_DECRYPTED BIT(4) -#define TTM_TT_FLAG_PRIV_POPULATED BIT(4) +#define TTM_TT_FLAG_PRIV_POPULATED BIT(5) uint32_t page_flags; /** @num_pages: Number of pages in the page array. */ uint32_t num_pages; -- cgit v1.2.3 From 9840d28f25143da23e0e7ecb1a3b8109987406ee Mon Sep 17 00:00:00 2001 From: Maaz Mombasawala Date: Fri, 26 Jan 2024 15:08:01 -0500 Subject: drm/vmwgfx: Make all surfaces shareable There is no real need to have a separate pool for shareable and non-shareable surfaces. Make all surfaces shareable, regardless of whether the drm_vmw_surface_flag_shareable has been specified. Signed-off-by: Maaz Mombasawala Reviewed-by: Martin Krastev Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240126200804.732454-3-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/ttm_object.c | 6 +++--- drivers/gpu/drm/vmwgfx/ttm_object.h | 3 +-- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 17 ++++++----------- include/uapi/drm/vmwgfx_drm.h | 5 +++-- 4 files changed, 13 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c index ddf8373c1d77..6806c05e57f6 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.c +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /************************************************************************** * - * Copyright (c) 2009-2022 VMware, Inc., Palo Alto, CA., USA + * Copyright (c) 2009-2023 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -648,7 +648,6 @@ out_unref: * @tfile: struct ttm_object_file identifying the caller * @size: The size of the dma_bufs we export. * @prime: The object to be initialized. - * @shareable: See ttm_base_object_init * @type: See ttm_base_object_init * @refcount_release: See ttm_base_object_init * @@ -656,10 +655,11 @@ out_unref: * for data sharing between processes and devices. */ int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, - struct ttm_prime_object *prime, bool shareable, + struct ttm_prime_object *prime, enum ttm_object_type type, void (*refcount_release) (struct ttm_base_object **)) { + bool shareable = !!(type == VMW_RES_SURFACE); mutex_init(&prime->mutex); prime->size = PAGE_ALIGN(size); prime->real_type = type; diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.h b/drivers/gpu/drm/vmwgfx/ttm_object.h index e6b77ee33e55..573e038c0fab 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.h +++ b/drivers/gpu/drm/vmwgfx/ttm_object.h @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright (c) 2006-2022 VMware, Inc., Palo Alto, CA., USA + * Copyright (c) 2006-2023 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -288,7 +288,6 @@ extern void ttm_object_device_release(struct ttm_object_device **p_tdev); extern int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, struct ttm_prime_object *prime, - bool shareable, enum ttm_object_type type, void (*refcount_release) (struct ttm_base_object **)); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 10498725034c..e7a744dfcecf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -832,8 +832,6 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->snooper.image = NULL; } - user_srf->prime.base.shareable = false; - user_srf->prime.base.tfile = NULL; if (drm_is_primary_client(file_priv)) user_srf->master = drm_file_get_master(file_priv); @@ -847,10 +845,10 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, goto out_unlock; /* - * A gb-aware client referencing a shared surface will - * expect a backup buffer to be present. + * A gb-aware client referencing a surface will expect a backup + * buffer to be present. */ - if (dev_priv->has_mob && req->shareable) { + if (dev_priv->has_mob) { struct vmw_bo_params params = { .domain = VMW_BO_DOMAIN_SYS, .busy_domain = VMW_BO_DOMAIN_SYS, @@ -869,8 +867,9 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, } tmp = vmw_resource_reference(&srf->res); - ret = ttm_prime_object_init(tfile, res->guest_memory_size, &user_srf->prime, - req->shareable, VMW_RES_SURFACE, + ret = ttm_prime_object_init(tfile, res->guest_memory_size, + &user_srf->prime, + VMW_RES_SURFACE, &vmw_user_surface_base_release); if (unlikely(ret != 0)) { @@ -1549,8 +1548,6 @@ vmw_gb_surface_define_internal(struct drm_device *dev, tmp = vmw_resource_reference(res); ret = ttm_prime_object_init(tfile, res->guest_memory_size, &user_srf->prime, - req->base.drm_surface_flags & - drm_vmw_surface_flag_shareable, VMW_RES_SURFACE, &vmw_user_surface_base_release); @@ -2052,8 +2049,6 @@ int vmw_gb_surface_define(struct vmw_private *dev_priv, } *srf_out = &user_srf->srf; - user_srf->prime.base.shareable = false; - user_srf->prime.base.tfile = NULL; srf = &user_srf->srf; srf->metadata = *req; diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index 26549c86a91f..26d96fecb902 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright © 2009-2022 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2009-2023 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -902,7 +902,8 @@ struct drm_vmw_shader_arg { /** * enum drm_vmw_surface_flags * - * @drm_vmw_surface_flag_shareable: Whether the surface is shareable + * @drm_vmw_surface_flag_shareable: Deprecated - all userspace surfaces are + * shareable. * @drm_vmw_surface_flag_scanout: Whether the surface is a scanout * surface. * @drm_vmw_surface_flag_create_buffer: Create a backup buffer if none is -- cgit v1.2.3 From 0c10a15d21222ef22b41d11e423d7b17ed3d4a5d Mon Sep 17 00:00:00 2001 From: Maaz Mombasawala Date: Fri, 26 Jan 2024 15:08:02 -0500 Subject: drm/vmwgfx: Add SPDX header to vmwgfx_drm.h Update vmwgfx_drm.h with SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT Signed-off-by: Maaz Mombasawala Reviewed-by: Martin Krastev Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240126200804.732454-4-zack.rusin@broadcom.com --- include/uapi/drm/vmwgfx_drm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index 26d96fecb902..7d786a0cc835 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */ /************************************************************************** * * Copyright © 2009-2023 VMware, Inc., Palo Alto, CA., USA -- cgit v1.2.3 From 311d0fad2a2751dc707696063eb29ca427c0139d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 4 Feb 2024 21:41:01 -0800 Subject: drm/rect: fix kernel-doc typos Correct typos of "translated". Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Reviewed-by: Simon Ser Signed-off-by: Randy Dunlap Signed-off-by: Simon Ser Link: https://patchwork.freedesktop.org/patch/msgid/20240205054101.27929-1-rdunlap@infradead.org --- include/drm/drm_rect.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index e8d94fca2703..73fcb899a01d 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -129,7 +129,7 @@ static inline void drm_rect_adjust_size(struct drm_rect *r, int dw, int dh) /** * drm_rect_translate - translate the rectangle - * @r: rectangle to be tranlated + * @r: rectangle to be translated * @dx: horizontal translation * @dy: vertical translation * @@ -146,7 +146,7 @@ static inline void drm_rect_translate(struct drm_rect *r, int dx, int dy) /** * drm_rect_translate_to - translate the rectangle to an absolute position - * @r: rectangle to be tranlated + * @r: rectangle to be translated * @x: horizontal position * @y: vertical position * -- cgit v1.2.3 From d807ad80d811ba0c22adfd871e2a46491f80d6e2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 23 Jan 2024 21:37:07 +0200 Subject: drm/bridge: add ->edid_read hook and drm_bridge_edid_read() Add new struct drm_edid based ->edid_read hook and drm_bridge_edid_read() function to call the hook. v2: Include drm/drm_edid.h Signed-off-by: Jani Nikula Reviewed-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/9d08d22eaffcb9c59a2b677e45d7e61fc689bc2f.1706038510.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_bridge.c | 46 +++++++++++++++++++++++++++++++++++++++++++- include/drm/drm_bridge.h | 33 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index cee3188adf3d..4f6f8c662d3f 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -27,8 +27,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -1206,6 +1207,47 @@ int drm_bridge_get_modes(struct drm_bridge *bridge, } EXPORT_SYMBOL_GPL(drm_bridge_get_modes); +/** + * drm_bridge_edid_read - read the EDID data of the connected display + * @bridge: bridge control structure + * @connector: the connector to read EDID for + * + * If the bridge supports output EDID retrieval, as reported by the + * DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.edid_read to get + * the EDID and return it. Otherwise return NULL. + * + * If &drm_bridge_funcs.edid_read is not set, fall back to using + * drm_bridge_get_edid() and wrapping it in struct drm_edid. + * + * RETURNS: + * The retrieved EDID on success, or NULL otherwise. + */ +const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_EDID)) + return NULL; + + /* Transitional: Fall back to ->get_edid. */ + if (!bridge->funcs->edid_read) { + const struct drm_edid *drm_edid; + struct edid *edid; + + edid = drm_bridge_get_edid(bridge, connector); + if (!edid) + return NULL; + + drm_edid = drm_edid_alloc(edid, (edid->extensions + 1) * EDID_LENGTH); + + kfree(edid); + + return drm_edid; + } + + return bridge->funcs->edid_read(bridge, connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_edid_read); + /** * drm_bridge_get_edid - get the EDID data of the connected display * @bridge: bridge control structure @@ -1215,6 +1257,8 @@ EXPORT_SYMBOL_GPL(drm_bridge_get_modes); * DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.get_edid to * get the EDID and return it. Otherwise return NULL. * + * Deprecated. Prefer using drm_bridge_edid_read(). + * * RETURNS: * The retrieved EDID on success, or NULL otherwise. */ diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index e39da5807ba7..b7aed3ead705 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -557,6 +557,37 @@ struct drm_bridge_funcs { int (*get_modes)(struct drm_bridge *bridge, struct drm_connector *connector); + /** + * @edid_read: + * + * Read the EDID data of the connected display. + * + * The @edid_read callback is the preferred way of reporting mode + * information for a display connected to the bridge output. Bridges + * that support reading EDID shall implement this callback and leave + * the @get_modes callback unimplemented. + * + * The caller of this operation shall first verify the output + * connection status and refrain from reading EDID from a disconnected + * output. + * + * This callback is optional. Bridges that implement it shall set the + * DRM_BRIDGE_OP_EDID flag in their &drm_bridge->ops. + * + * The connector parameter shall be used for the sole purpose of EDID + * retrieval, and shall not be stored internally by bridge drivers for + * future usage. + * + * RETURNS: + * + * An edid structure newly allocated with drm_edid_alloc() or returned + * from drm_edid_read() family of functions on success, or NULL + * otherwise. The caller is responsible for freeing the returned edid + * structure with drm_edid_free(). + */ + const struct drm_edid *(*edid_read)(struct drm_bridge *bridge, + struct drm_connector *connector); + /** * @get_edid: * @@ -888,6 +919,8 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge); int drm_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector); +const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector); struct edid *drm_bridge_get_edid(struct drm_bridge *bridge, struct drm_connector *connector); void drm_bridge_hpd_enable(struct drm_bridge *bridge, -- cgit v1.2.3 From 3ce7384048fa1793db0eae013fa377d89256b76f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 23 Jan 2024 21:37:19 +0200 Subject: drm/bridge: remove drm_bridge_get_edid() in favour of drm_bridge_edid_read() All users of drm_bridge_get_edid() have been converted to use drm_bridge_edid_read(). Remove drm_bridge_get_edid(). Signed-off-by: Jani Nikula Reviewed-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/c51d50edddbe8816eaa63e6ccafa9f2354b506ba.1706038510.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_bridge.c | 28 ++-------------------------- include/drm/drm_bridge.h | 2 -- 2 files changed, 2 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 4f6f8c662d3f..a3065d4aa3d6 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1217,7 +1217,7 @@ EXPORT_SYMBOL_GPL(drm_bridge_get_modes); * the EDID and return it. Otherwise return NULL. * * If &drm_bridge_funcs.edid_read is not set, fall back to using - * drm_bridge_get_edid() and wrapping it in struct drm_edid. + * &drm_bridge_funcs.get_edid and wrapping it in struct drm_edid. * * RETURNS: * The retrieved EDID on success, or NULL otherwise. @@ -1233,7 +1233,7 @@ const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, const struct drm_edid *drm_edid; struct edid *edid; - edid = drm_bridge_get_edid(bridge, connector); + edid = bridge->funcs->get_edid(bridge, connector); if (!edid) return NULL; @@ -1248,30 +1248,6 @@ const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, } EXPORT_SYMBOL_GPL(drm_bridge_edid_read); -/** - * drm_bridge_get_edid - get the EDID data of the connected display - * @bridge: bridge control structure - * @connector: the connector to read EDID for - * - * If the bridge supports output EDID retrieval, as reported by the - * DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.get_edid to - * get the EDID and return it. Otherwise return NULL. - * - * Deprecated. Prefer using drm_bridge_edid_read(). - * - * RETURNS: - * The retrieved EDID on success, or NULL otherwise. - */ -struct edid *drm_bridge_get_edid(struct drm_bridge *bridge, - struct drm_connector *connector) -{ - if (!(bridge->ops & DRM_BRIDGE_OP_EDID)) - return NULL; - - return bridge->funcs->get_edid(bridge, connector); -} -EXPORT_SYMBOL_GPL(drm_bridge_get_edid); - /** * drm_bridge_hpd_enable - enable hot plug detection for the bridge * @bridge: bridge control structure diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index b7aed3ead705..ee12f829aaf7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -921,8 +921,6 @@ int drm_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector); const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector); -struct edid *drm_bridge_get_edid(struct drm_bridge *bridge, - struct drm_connector *connector); void drm_bridge_hpd_enable(struct drm_bridge *bridge, void (*cb)(void *data, enum drm_connector_status status), -- cgit v1.2.3 From 27b8f91c08d99d267ac6096d4733203161274cbb Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 23 Jan 2024 21:37:45 +0200 Subject: drm/bridge: remove ->get_edid callback There are no more users of the ->get_edid callback left. They've all been converted to ->edid_read. Remove the callback, and the fallback in drm_bridge_edid_read(). Signed-off-by: Jani Nikula Reviewed-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/34407a355ec6848fc44f8c30d245fcbc5687195e.1706038510.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_bridge.c | 19 ------------------- include/drm/drm_bridge.h | 30 ------------------------------ 2 files changed, 49 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index a3065d4aa3d6..521a71c61b16 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1216,9 +1216,6 @@ EXPORT_SYMBOL_GPL(drm_bridge_get_modes); * DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.edid_read to get * the EDID and return it. Otherwise return NULL. * - * If &drm_bridge_funcs.edid_read is not set, fall back to using - * &drm_bridge_funcs.get_edid and wrapping it in struct drm_edid. - * * RETURNS: * The retrieved EDID on success, or NULL otherwise. */ @@ -1228,22 +1225,6 @@ const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, if (!(bridge->ops & DRM_BRIDGE_OP_EDID)) return NULL; - /* Transitional: Fall back to ->get_edid. */ - if (!bridge->funcs->edid_read) { - const struct drm_edid *drm_edid; - struct edid *edid; - - edid = bridge->funcs->get_edid(bridge, connector); - if (!edid) - return NULL; - - drm_edid = drm_edid_alloc(edid, (edid->extensions + 1) * EDID_LENGTH); - - kfree(edid); - - return drm_edid; - } - return bridge->funcs->edid_read(bridge, connector); } EXPORT_SYMBOL_GPL(drm_bridge_edid_read); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index ee12f829aaf7..7293c02e17c5 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -588,36 +588,6 @@ struct drm_bridge_funcs { const struct drm_edid *(*edid_read)(struct drm_bridge *bridge, struct drm_connector *connector); - /** - * @get_edid: - * - * Read and parse the EDID data of the connected display. - * - * The @get_edid callback is the preferred way of reporting mode - * information for a display connected to the bridge output. Bridges - * that support reading EDID shall implement this callback and leave - * the @get_modes callback unimplemented. - * - * The caller of this operation shall first verify the output - * connection status and refrain from reading EDID from a disconnected - * output. - * - * This callback is optional. Bridges that implement it shall set the - * DRM_BRIDGE_OP_EDID flag in their &drm_bridge->ops. - * - * The connector parameter shall be used for the sole purpose of EDID - * retrieval and parsing, and shall not be stored internally by bridge - * drivers for future usage. - * - * RETURNS: - * - * An edid structure newly allocated with kmalloc() (or similar) on - * success, or NULL otherwise. The caller is responsible for freeing - * the returned edid structure with kfree(). - */ - struct edid *(*get_edid)(struct drm_bridge *bridge, - struct drm_connector *connector); - /** * @hpd_notify: * -- cgit v1.2.3 From 5e0c04c8c40b69ab165d52964433859d8b666376 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 16 Jan 2024 15:07:26 +0200 Subject: drm/print: make drm_err_printer() device specific by using drm_err() With few users for drm_err_printer(), it's still feasible to convert it to be device specific. Use drm_err() under the hood. While at it, make the prefix optional. Signed-off-by: Jani Nikula Reviewed-by: Luca Coelho Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/2a9cdcfc1df44568078f7c131e2e7e0f7c94e97e.1705410327.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_print.c | 7 ++++++- drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c | 4 ++-- drivers/gpu/drm/i915/selftests/i915_active.c | 4 ++-- include/drm/drm_print.h | 11 ++++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index 5b93c11895bb..91dbcdeaad3f 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -191,7 +191,12 @@ EXPORT_SYMBOL(__drm_printfn_debug); void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf) { - pr_err("*ERROR* %s %pV", p->prefix, vaf); + struct drm_device *drm = p->arg; + + if (p->prefix) + drm_err(drm, "%s %pV", p->prefix, vaf); + else + drm_err(drm, "%pV", vaf); } EXPORT_SYMBOL(__drm_printfn_err); diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c index bc441ce7b380..be827318275c 100644 --- a/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c @@ -122,7 +122,7 @@ static int __live_idle_pulse(struct intel_engine_cs *engine, GEM_BUG_ON(!llist_empty(&engine->barrier_tasks)); if (engine_sync_barrier(engine)) { - struct drm_printer m = drm_err_printer("pulse"); + struct drm_printer m = drm_err_printer(&engine->i915->drm, "pulse"); pr_err("%s: no heartbeat pulse?\n", engine->name); intel_engine_dump(engine, &m, "%s", engine->name); @@ -136,7 +136,7 @@ static int __live_idle_pulse(struct intel_engine_cs *engine, pulse_unlock_wait(p); /* synchronize with the retirement callback */ if (!i915_active_is_idle(&p->active)) { - struct drm_printer m = drm_err_printer("pulse"); + struct drm_printer m = drm_err_printer(&engine->i915->drm, "pulse"); pr_err("%s: heartbeat pulse did not flush idle tasks\n", engine->name); diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c index b61fe850e924..8886752ade63 100644 --- a/drivers/gpu/drm/i915/selftests/i915_active.c +++ b/drivers/gpu/drm/i915/selftests/i915_active.c @@ -156,7 +156,7 @@ static int live_active_wait(void *arg) __i915_active_wait(&active->base, TASK_UNINTERRUPTIBLE); if (!READ_ONCE(active->retired)) { - struct drm_printer p = drm_err_printer(__func__); + struct drm_printer p = drm_err_printer(&i915->drm, __func__); pr_err("i915_active not retired after waiting!\n"); i915_active_print(&active->base, &p); @@ -189,7 +189,7 @@ static int live_active_retire(void *arg) err = -EIO; if (!READ_ONCE(active->retired)) { - struct drm_printer p = drm_err_printer(__func__); + struct drm_printer p = drm_err_printer(&i915->drm, __func__); pr_err("i915_active not retired after flushing!\n"); i915_active_print(&active->base, &p); diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index dd4883df876a..3d899fb0793c 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -35,6 +35,8 @@ #include +struct drm_device; + /* Do *not* use outside of drm_print.[ch]! */ extern unsigned long __drm_debug; @@ -235,16 +237,19 @@ static inline struct drm_printer drm_debug_printer(const char *prefix) } /** - * drm_err_printer - construct a &drm_printer that outputs to pr_err() - * @prefix: debug output prefix + * drm_err_printer - construct a &drm_printer that outputs to drm_err() + * @drm: the &struct drm_device pointer + * @prefix: debug output prefix, or NULL for no prefix * * RETURNS: * The &drm_printer object */ -static inline struct drm_printer drm_err_printer(const char *prefix) +static inline struct drm_printer drm_err_printer(struct drm_device *drm, + const char *prefix) { struct drm_printer p = { .printfn = __drm_printfn_err, + .arg = drm, .prefix = prefix }; return p; -- cgit v1.2.3 From 82195d48b77c71b5084a6d6a03d0c574a9fc6749 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 16 Jan 2024 15:07:27 +0200 Subject: drm/print: move enum drm_debug_category etc. earlier in drm_print.h Avoid forward declarations in subsequent changes, but separate this movement to an independent change. Signed-off-by: Jani Nikula Reviewed-by: Luca Coelho Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/9d105014e3c90af13a874745d768212347f68283.1705410327.git.jani.nikula@intel.com --- include/drm/drm_print.h | 190 ++++++++++++++++++++++++------------------------ 1 file changed, 95 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 3d899fb0793c..2d57939429a9 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -69,6 +69,101 @@ extern unsigned long __drm_debug; * } */ +/** + * enum drm_debug_category - The DRM debug categories + * + * Each of the DRM debug logging macros use a specific category, and the logging + * is filtered by the drm.debug module parameter. This enum specifies the values + * for the interface. + * + * Each DRM_DEBUG_ macro logs to DRM_UT_ category, except + * DRM_DEBUG() logs to DRM_UT_CORE. + * + * Enabling verbose debug messages is done through the drm.debug parameter, each + * category being enabled by a bit: + * + * - drm.debug=0x1 will enable CORE messages + * - drm.debug=0x2 will enable DRIVER messages + * - drm.debug=0x3 will enable CORE and DRIVER messages + * - ... + * - drm.debug=0x1ff will enable all messages + * + * An interesting feature is that it's possible to enable verbose logging at + * run-time by echoing the debug value in its sysfs node:: + * + * # echo 0xf > /sys/module/drm/parameters/debug + * + */ +enum drm_debug_category { + /* These names must match those in DYNAMIC_DEBUG_CLASSBITS */ + /** + * @DRM_UT_CORE: Used in the generic drm code: drm_ioctl.c, drm_mm.c, + * drm_memory.c, ... + */ + DRM_UT_CORE, + /** + * @DRM_UT_DRIVER: Used in the vendor specific part of the driver: i915, + * radeon, ... macro. + */ + DRM_UT_DRIVER, + /** + * @DRM_UT_KMS: Used in the modesetting code. + */ + DRM_UT_KMS, + /** + * @DRM_UT_PRIME: Used in the prime code. + */ + DRM_UT_PRIME, + /** + * @DRM_UT_ATOMIC: Used in the atomic code. + */ + DRM_UT_ATOMIC, + /** + * @DRM_UT_VBL: Used for verbose debug message in the vblank code. + */ + DRM_UT_VBL, + /** + * @DRM_UT_STATE: Used for verbose atomic state debugging. + */ + DRM_UT_STATE, + /** + * @DRM_UT_LEASE: Used in the lease code. + */ + DRM_UT_LEASE, + /** + * @DRM_UT_DP: Used in the DP code. + */ + DRM_UT_DP, + /** + * @DRM_UT_DRMRES: Used in the drm managed resources code. + */ + DRM_UT_DRMRES +}; + +static inline bool drm_debug_enabled_raw(enum drm_debug_category category) +{ + return unlikely(__drm_debug & BIT(category)); +} + +#define drm_debug_enabled_instrumented(category) \ + ({ \ + pr_debug("todo: is this frequent enough to optimize ?\n"); \ + drm_debug_enabled_raw(category); \ + }) + +#if defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) +/* + * the drm.debug API uses dyndbg, so each drm_*dbg macro/callsite gets + * a descriptor, and only enabled callsites are reachable. They use + * the private macro to avoid re-testing the enable-bit. + */ +#define __drm_debug_enabled(category) true +#define drm_debug_enabled(category) drm_debug_enabled_instrumented(category) +#else +#define __drm_debug_enabled(category) drm_debug_enabled_raw(category) +#define drm_debug_enabled(category) drm_debug_enabled_raw(category) +#endif + /** * struct drm_printer - drm output "stream" * @@ -255,101 +350,6 @@ static inline struct drm_printer drm_err_printer(struct drm_device *drm, return p; } -/** - * enum drm_debug_category - The DRM debug categories - * - * Each of the DRM debug logging macros use a specific category, and the logging - * is filtered by the drm.debug module parameter. This enum specifies the values - * for the interface. - * - * Each DRM_DEBUG_ macro logs to DRM_UT_ category, except - * DRM_DEBUG() logs to DRM_UT_CORE. - * - * Enabling verbose debug messages is done through the drm.debug parameter, each - * category being enabled by a bit: - * - * - drm.debug=0x1 will enable CORE messages - * - drm.debug=0x2 will enable DRIVER messages - * - drm.debug=0x3 will enable CORE and DRIVER messages - * - ... - * - drm.debug=0x1ff will enable all messages - * - * An interesting feature is that it's possible to enable verbose logging at - * run-time by echoing the debug value in its sysfs node:: - * - * # echo 0xf > /sys/module/drm/parameters/debug - * - */ -enum drm_debug_category { - /* These names must match those in DYNAMIC_DEBUG_CLASSBITS */ - /** - * @DRM_UT_CORE: Used in the generic drm code: drm_ioctl.c, drm_mm.c, - * drm_memory.c, ... - */ - DRM_UT_CORE, - /** - * @DRM_UT_DRIVER: Used in the vendor specific part of the driver: i915, - * radeon, ... macro. - */ - DRM_UT_DRIVER, - /** - * @DRM_UT_KMS: Used in the modesetting code. - */ - DRM_UT_KMS, - /** - * @DRM_UT_PRIME: Used in the prime code. - */ - DRM_UT_PRIME, - /** - * @DRM_UT_ATOMIC: Used in the atomic code. - */ - DRM_UT_ATOMIC, - /** - * @DRM_UT_VBL: Used for verbose debug message in the vblank code. - */ - DRM_UT_VBL, - /** - * @DRM_UT_STATE: Used for verbose atomic state debugging. - */ - DRM_UT_STATE, - /** - * @DRM_UT_LEASE: Used in the lease code. - */ - DRM_UT_LEASE, - /** - * @DRM_UT_DP: Used in the DP code. - */ - DRM_UT_DP, - /** - * @DRM_UT_DRMRES: Used in the drm managed resources code. - */ - DRM_UT_DRMRES -}; - -static inline bool drm_debug_enabled_raw(enum drm_debug_category category) -{ - return unlikely(__drm_debug & BIT(category)); -} - -#define drm_debug_enabled_instrumented(category) \ - ({ \ - pr_debug("todo: is this frequent enough to optimize ?\n"); \ - drm_debug_enabled_raw(category); \ - }) - -#if defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) -/* - * the drm.debug API uses dyndbg, so each drm_*dbg macro/callsite gets - * a descriptor, and only enabled callsites are reachable. They use - * the private macro to avoid re-testing the enable-bit. - */ -#define __drm_debug_enabled(category) true -#define drm_debug_enabled(category) drm_debug_enabled_instrumented(category) -#else -#define __drm_debug_enabled(category) drm_debug_enabled_raw(category) -#define drm_debug_enabled(category) drm_debug_enabled_raw(category) -#endif - /* * struct device based logging * -- cgit v1.2.3 From 9fd6f61a297e944fa045a1cb6cd53916d0fea454 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 16 Jan 2024 15:07:28 +0200 Subject: drm/print: add drm_dbg_printer() for drm device specific printer We've lacked a device specific debug printer. Add one. Take category into account too. __builtin_return_address(0) is inaccurate here, so don't use it. If necessary, we can later pass __func__ to drm_dbg_printer() by wrapping it inside a macro. Signed-off-by: Jani Nikula Reviewed-by: Luca Coelho Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/48607d58e5cdf8341ffdd522257542fa2ce41a19.1705410327.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_print.c | 21 +++++++++++++++++++++ include/drm/drm_print.h | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index 91dbcdeaad3f..673b29c732ea 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -189,6 +189,27 @@ void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf) } EXPORT_SYMBOL(__drm_printfn_debug); +void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf) +{ + const struct drm_device *drm = p->arg; + const struct device *dev = drm ? drm->dev : NULL; + enum drm_debug_category category = p->category; + const char *prefix = p->prefix ?: ""; + const char *prefix_pad = p->prefix ? " " : ""; + + if (!__drm_debug_enabled(category)) + return; + + /* Note: __builtin_return_address(0) is useless here. */ + if (dev) + dev_printk(KERN_DEBUG, dev, "[" DRM_NAME "]%s%s %pV", + prefix_pad, prefix, vaf); + else + printk(KERN_DEBUG "[" DRM_NAME "]%s%s %pV", + prefix_pad, prefix, vaf); +} +EXPORT_SYMBOL(__drm_printfn_dbg); + void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf) { struct drm_device *drm = p->arg; diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 2d57939429a9..c6a7a7fecccc 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -176,6 +176,7 @@ struct drm_printer { void (*puts)(struct drm_printer *p, const char *str); void *arg; const char *prefix; + enum drm_debug_category category; }; void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf); @@ -184,6 +185,7 @@ void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf); void __drm_puts_seq_file(struct drm_printer *p, const char *str); void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf); void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf); +void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf); void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf); __printf(2, 3) @@ -331,6 +333,28 @@ static inline struct drm_printer drm_debug_printer(const char *prefix) return p; } +/** + * drm_dbg_printer - construct a &drm_printer for drm device specific output + * @drm: the &struct drm_device pointer, or NULL + * @category: the debug category to use + * @prefix: debug output prefix, or NULL for no prefix + * + * RETURNS: + * The &drm_printer object + */ +static inline struct drm_printer drm_dbg_printer(struct drm_device *drm, + enum drm_debug_category category, + const char *prefix) +{ + struct drm_printer p = { + .printfn = __drm_printfn_dbg, + .arg = drm, + .prefix = prefix, + .category = category, + }; + return p; +} + /** * drm_err_printer - construct a &drm_printer that outputs to drm_err() * @drm: the &struct drm_device pointer -- cgit v1.2.3 From 2e61504fd1c36ac87e2127f02f9a61a3586bb1ec Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 16 Jan 2024 15:07:31 +0200 Subject: drm/dp: switch drm_dp_vsc_sdp_log() to struct drm_printer Use the existing drm printer infrastructure instead of local macros. Signed-off-by: Jani Nikula Reviewed-by: Luca Coelho Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/cdf8faf272d345de215feb6ececba384ecaecdb4.1705410327.git.jani.nikula@intel.com --- drivers/gpu/drm/display/drm_dp_helper.c | 17 ++++++-------- .../gpu/drm/i915/display/intel_crtc_state_dump.c | 5 ++-- drivers/gpu/drm/i915/display/intel_display.c | 27 +++++++++++----------- include/drm/display/drm_dp_helper.h | 3 +-- 4 files changed, 23 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index b1ca3a1100da..8d6ce46471ae 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2897,22 +2897,19 @@ static const char *dp_content_type_get_name(enum dp_content_type content_type) } } -void drm_dp_vsc_sdp_log(const char *level, struct device *dev, - const struct drm_dp_vsc_sdp *vsc) +void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc) { -#define DP_SDP_LOG(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) - DP_SDP_LOG("DP SDP: %s, revision %u, length %u\n", "VSC", + drm_printf(p, "DP SDP: VSC, revision %u, length %u\n", vsc->revision, vsc->length); - DP_SDP_LOG(" pixelformat: %s\n", + drm_printf(p, " pixelformat: %s\n", dp_pixelformat_get_name(vsc->pixelformat)); - DP_SDP_LOG(" colorimetry: %s\n", + drm_printf(p, " colorimetry: %s\n", dp_colorimetry_get_name(vsc->pixelformat, vsc->colorimetry)); - DP_SDP_LOG(" bpc: %u\n", vsc->bpc); - DP_SDP_LOG(" dynamic range: %s\n", + drm_printf(p, " bpc: %u\n", vsc->bpc); + drm_printf(p, " dynamic range: %s\n", dp_dynamic_range_get_name(vsc->dynamic_range)); - DP_SDP_LOG(" content type: %s\n", + drm_printf(p, " content type: %s\n", dp_content_type_get_name(vsc->content_type)); -#undef DP_SDP_LOG } EXPORT_SYMBOL(drm_dp_vsc_sdp_log); diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c index 49fd100ec98a..4bcf446c75f4 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c +++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c @@ -55,10 +55,9 @@ static void intel_dump_dp_vsc_sdp(struct drm_i915_private *i915, const struct drm_dp_vsc_sdp *vsc) { - if (!drm_debug_enabled(DRM_UT_KMS)) - return; + struct drm_printer p = drm_dbg_printer(&i915->drm, DRM_UT_KMS, NULL); - drm_dp_vsc_sdp_log(KERN_DEBUG, i915->drm.dev, vsc); + drm_dp_vsc_sdp_log(&p, vsc); } static void diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index b10aad15a63d..57f78492f6ea 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4799,28 +4799,27 @@ pipe_config_infoframe_mismatch(struct drm_i915_private *dev_priv, } static void -pipe_config_dp_vsc_sdp_mismatch(struct drm_i915_private *dev_priv, +pipe_config_dp_vsc_sdp_mismatch(struct drm_i915_private *i915, bool fastset, const char *name, const struct drm_dp_vsc_sdp *a, const struct drm_dp_vsc_sdp *b) { + struct drm_printer p; + if (fastset) { - if (!drm_debug_enabled(DRM_UT_KMS)) - return; + p = drm_dbg_printer(&i915->drm, DRM_UT_KMS, NULL); - drm_dbg_kms(&dev_priv->drm, - "fastset requirement not met in %s dp sdp\n", name); - drm_dbg_kms(&dev_priv->drm, "expected:\n"); - drm_dp_vsc_sdp_log(KERN_DEBUG, dev_priv->drm.dev, a); - drm_dbg_kms(&dev_priv->drm, "found:\n"); - drm_dp_vsc_sdp_log(KERN_DEBUG, dev_priv->drm.dev, b); + drm_printf(&p, "fastset requirement not met in %s dp sdp\n", name); } else { - drm_err(&dev_priv->drm, "mismatch in %s dp sdp\n", name); - drm_err(&dev_priv->drm, "expected:\n"); - drm_dp_vsc_sdp_log(KERN_ERR, dev_priv->drm.dev, a); - drm_err(&dev_priv->drm, "found:\n"); - drm_dp_vsc_sdp_log(KERN_ERR, dev_priv->drm.dev, b); + p = drm_err_printer(&i915->drm, NULL); + + drm_printf(&p, "mismatch in %s dp sdp\n", name); } + + drm_printf(&p, "expected:\n"); + drm_dp_vsc_sdp_log(&p, a); + drm_printf(&p, "found:\n"); + drm_dp_vsc_sdp_log(&p, b); } /* Returns the length up to and including the last differing byte */ diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 863b2e7add29..d02014a87f12 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -98,8 +98,7 @@ struct drm_dp_vsc_sdp { enum dp_content_type content_type; }; -void drm_dp_vsc_sdp_log(const char *level, struct device *dev, - const struct drm_dp_vsc_sdp *vsc); +void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc); int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]); -- cgit v1.2.3 From e154c4fc7bf2d5c3f86d07628ab1cb03e8085c25 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 16 Jan 2024 15:07:35 +0200 Subject: drm: remove drm_debug_printer in favor of drm_dbg_printer Convert the remaining drm_debug_printer users over to drm_dbg_printer, as it can handle the cases without struct drm_device pointer, and also provides drm debug category and prefix support. Remove drm_debug_printer altogether. Signed-off-by: Jani Nikula Reviewed-by: Luca Coelho Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/18b5b91e62d071675a651f6f91c58f05ad74134a.1705410327.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_modeset_lock.c | 2 +- drivers/gpu/drm/drm_print.c | 7 ------- drivers/gpu/drm/ttm/ttm_bo.c | 2 +- include/drm/drm_print.h | 17 ----------------- 4 files changed, 2 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 918065982db4..7694b85e75e3 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -91,7 +91,7 @@ static noinline depot_stack_handle_t __drm_stack_depot_save(void) static void __drm_stack_depot_print(depot_stack_handle_t stack_depot) { - struct drm_printer p = drm_debug_printer("drm_modeset_lock"); + struct drm_printer p = drm_dbg_printer(NULL, DRM_UT_KMS, "drm_modeset_lock"); unsigned long *entries; unsigned int nr_entries; char *buf; diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index 673b29c732ea..699b7dbffd7b 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -182,13 +182,6 @@ void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf) } EXPORT_SYMBOL(__drm_printfn_info); -void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf) -{ - /* pr_debug callsite decorations are unhelpful here */ - printk(KERN_DEBUG "%s %pV", p->prefix, vaf); -} -EXPORT_SYMBOL(__drm_printfn_debug); - void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf) { const struct drm_device *drm = p->arg; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index ba3f09e2d7e6..96a724e8f3ff 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -49,7 +49,7 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, struct ttm_placement *placement) { - struct drm_printer p = drm_debug_printer(TTM_PFX); + struct drm_printer p = drm_dbg_printer(NULL, DRM_UT_CORE, TTM_PFX); struct ttm_resource_manager *man; int i, mem_type; diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index c6a7a7fecccc..39ea79b5c0f0 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -184,7 +184,6 @@ void __drm_puts_coredump(struct drm_printer *p, const char *str); void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf); void __drm_puts_seq_file(struct drm_printer *p, const char *str); void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf); -void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf); void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf); void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf); @@ -317,22 +316,6 @@ static inline struct drm_printer drm_info_printer(struct device *dev) return p; } -/** - * drm_debug_printer - construct a &drm_printer that outputs to pr_debug() - * @prefix: debug output prefix - * - * RETURNS: - * The &drm_printer object - */ -static inline struct drm_printer drm_debug_printer(const char *prefix) -{ - struct drm_printer p = { - .printfn = __drm_printfn_debug, - .prefix = prefix - }; - return p; -} - /** * drm_dbg_printer - construct a &drm_printer for drm device specific output * @drm: the &struct drm_device pointer, or NULL -- cgit v1.2.3 From 9bc36e58d162466236a38489e4b41f38a8848c94 Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Thu, 8 Feb 2024 10:35:38 -0800 Subject: drm/xe: Add uAPI to query GuC firmware submission version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to a bug in GuC firmware, Mesa can't enable by default the usage of compute engines in DG2 and newer. A new GuC firmware fixed the issue but until now there was no way for Mesa to know if KMD was running with the fixed GuC version or not, so this uAPI is required. It may be expanded in future to query other firmware versions too. This is querying XE_UC_FW_VER_COMPATIBILITY/submission version because that is also supported by VFs, while XE_UC_FW_VER_RELEASE don't. i915 uAPI: https://patchwork.freedesktop.org/series/129627/ Mesa usage: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25233 v2: - fixed drm_xe_query_uc_fw_version documentation - moved branch_ver as the first version number Cc: John Harrison Cc: Francois Dugast Cc: Lucas De Marchi Signed-off-by: José Roberto de Souza Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240208183539.185095-1-jose.souza@intel.com --- drivers/gpu/drm/xe/xe_query.c | 44 +++++++++++++++++++++++++++++++++++++++++++ include/uapi/drm/xe_drm.h | 31 ++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 4f1ab91dbec5..92bb06c0586e 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -516,6 +516,49 @@ static int query_gt_topology(struct xe_device *xe, return 0; } +static int +query_uc_fw_version(struct xe_device *xe, struct drm_xe_device_query *query) +{ + struct drm_xe_query_uc_fw_version __user *query_ptr = u64_to_user_ptr(query->data); + size_t size = sizeof(struct drm_xe_query_uc_fw_version); + struct drm_xe_query_uc_fw_version resp; + struct xe_uc_fw_version *version = NULL; + + if (query->size == 0) { + query->size = size; + return 0; + } else if (XE_IOCTL_DBG(xe, query->size != size)) { + return -EINVAL; + } + + if (copy_from_user(&resp, query_ptr, size)) + return -EFAULT; + + if (XE_IOCTL_DBG(xe, resp.pad || resp.pad2 || resp.reserved)) + return -EINVAL; + + switch (resp.uc_type) { + case XE_QUERY_UC_TYPE_GUC_SUBMISSION: { + struct xe_guc *guc = &xe->tiles[0].primary_gt->uc.guc; + + version = &guc->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY]; + break; + } + default: + return -EINVAL; + } + + resp.branch_ver = 0; + resp.major_ver = version->major; + resp.minor_ver = version->minor; + resp.patch_ver = version->patch; + + if (copy_to_user(query_ptr, &resp, size)) + return -EFAULT; + + return 0; +} + static int (* const xe_query_funcs[])(struct xe_device *xe, struct drm_xe_device_query *query) = { query_engines, @@ -525,6 +568,7 @@ static int (* const xe_query_funcs[])(struct xe_device *xe, query_hwconfig, query_gt_topology, query_engine_cycles, + query_uc_fw_version, }; int xe_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 50bbea0992d9..0819959a5222 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -574,6 +574,36 @@ struct drm_xe_query_engine_cycles { __u64 cpu_delta; }; +/** + * struct drm_xe_query_uc_fw_version - query a micro-controller firmware version + * + * Given a uc_type this will return the branch, major, minor and patch version + * of the micro-controller firmware. + */ +struct drm_xe_query_uc_fw_version { + /** @uc_type: The micro-controller type to query firmware version */ +#define XE_QUERY_UC_TYPE_GUC_SUBMISSION 0 + __u16 uc_type; + + /** @pad: MBZ */ + __u16 pad; + + /** @branch_ver: branch uc fw version */ + __u32 branch_ver; + /** @major_ver: major uc fw version */ + __u32 major_ver; + /** @minor_ver: minor uc fw version */ + __u32 minor_ver; + /** @patch_ver: patch uc fw version */ + __u32 patch_ver; + + /** @pad2: MBZ */ + __u32 pad2; + + /** @reserved: Reserved */ + __u64 reserved; +}; + /** * struct drm_xe_device_query - Input of &DRM_IOCTL_XE_DEVICE_QUERY - main * structure to query device information @@ -643,6 +673,7 @@ struct drm_xe_device_query { #define DRM_XE_DEVICE_QUERY_HWCONFIG 4 #define DRM_XE_DEVICE_QUERY_GT_TOPOLOGY 5 #define DRM_XE_DEVICE_QUERY_ENGINE_CYCLES 6 +#define DRM_XE_DEVICE_QUERY_UC_FW_VERSION 7 /** @query: The type of data to query */ __u32 query; -- cgit v1.2.3 From 75fa9b7e375e35739663cde0252d31e586c6314a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 12 Feb 2024 10:06:09 +0100 Subject: video: Add helpers for decoding screen_info The plain values as stored in struct screen_info need to be decoded before being used. Add helpers that decode the type of video output and the framebuffer I/O aperture. Old or non-x86 systems may not set the type of video directly, but only indicate the presence by storing 0x01 in orig_video_isVGA. The decoding logic in screen_info_video_type() takes this into account. It then follows similar code in vgacon's vgacon_startup() to detect the video type from the given values. A call to screen_info_resources() returns all known resources of the given screen_info. The resources' values have been taken from existing code in vgacon and vga16fb. These drivers can later be converted to use the new interfaces. v2: * return ssize_t from screen_info_resources() * don't call __screen_info_has_lfb() unnecessarily Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-2-tzimmermann@suse.de --- drivers/firmware/Kconfig | 1 + drivers/video/Kconfig | 4 + drivers/video/Makefile | 3 + drivers/video/screen_info_generic.c | 146 ++++++++++++++++++++++++++++++++++++ include/linux/screen_info.h | 100 ++++++++++++++++++++++++ 5 files changed, 254 insertions(+) create mode 100644 drivers/video/screen_info_generic.c (limited to 'include') diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index afd38539b92e..71d8b26c4103 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -182,6 +182,7 @@ config MTK_ADSP_IPC config SYSFB bool select BOOT_VESA_SUPPORT + select SCREEN_INFO config SYSFB_SIMPLEFB bool "Mark VGA/VBE/EFI FB as generic system framebuffer" diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 130ebccb8338..44c9ef1435a2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -11,6 +11,10 @@ config APERTURE_HELPERS Support tracking and hand-over of aperture ownership. Required by graphics drivers for firmware-provided framebuffers. +config SCREEN_INFO + bool + default n + config STI_CORE bool depends on PARISC diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9eb5557911de..117cbdbb58c2 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,11 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_APERTURE_HELPERS) += aperture.o +obj-$(CONFIG_SCREEN_INFO) += screen_info.o obj-$(CONFIG_STI_CORE) += sticore.o obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_VIDEO) += cmdline.o nomodeset.o obj-$(CONFIG_HDMI) += hdmi.o +screen_info-y := screen_info_generic.o + obj-$(CONFIG_VT) += console/ obj-$(CONFIG_FB_STI) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/screen_info_generic.c b/drivers/video/screen_info_generic.c new file mode 100644 index 000000000000..64117c6367ab --- /dev/null +++ b/drivers/video/screen_info_generic.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +static void resource_init_named(struct resource *r, + resource_size_t start, resource_size_t size, + const char *name, unsigned int flags) +{ + memset(r, 0, sizeof(*r)); + + r->start = start; + r->end = start + size - 1; + r->name = name; + r->flags = flags; +} + +static void resource_init_io_named(struct resource *r, + resource_size_t start, resource_size_t size, + const char *name) +{ + resource_init_named(r, start, size, name, IORESOURCE_IO); +} + +static void resource_init_mem_named(struct resource *r, + resource_size_t start, resource_size_t size, + const char *name) +{ + resource_init_named(r, start, size, name, IORESOURCE_MEM); +} + +static inline bool __screen_info_has_ega_gfx(unsigned int mode) +{ + switch (mode) { + case 0x0d: /* 320x200-4 */ + case 0x0e: /* 640x200-4 */ + case 0x0f: /* 640x350-1 */ + case 0x10: /* 640x350-4 */ + return true; + default: + return false; + } +} + +static inline bool __screen_info_has_vga_gfx(unsigned int mode) +{ + switch (mode) { + case 0x10: /* 640x480-1 */ + case 0x12: /* 640x480-4 */ + case 0x13: /* 320-200-8 */ + case 0x6a: /* 800x600-4 (VESA) */ + return true; + default: + return __screen_info_has_ega_gfx(mode); + } +} + +/** + * screen_info_resources() - Get resources from screen_info structure + * @si: the screen_info + * @r: pointer to an array of resource structures + * @num: number of elements in @r: + * + * Returns: + * The number of resources stored in @r on success, or a negative errno code otherwise. + * + * A call to screen_info_resources() returns the resources consumed by the + * screen_info's device or framebuffer. The result is stored in the caller-supplied + * array @r with up to @num elements. The function returns the number of + * initialized elements. + */ +ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num) +{ + struct resource *pos = r; + unsigned int type = screen_info_video_type(si); + u64 base, size; + + switch (type) { + case VIDEO_TYPE_MDA: + if (num > 0) + resource_init_io_named(pos++, 0x3b0, 12, "mda"); + if (num > 1) + resource_init_io_named(pos++, 0x3bf, 0x01, "mda"); + if (num > 2) + resource_init_mem_named(pos++, 0xb0000, 0x2000, "mda"); + break; + case VIDEO_TYPE_CGA: + if (num > 0) + resource_init_io_named(pos++, 0x3d4, 0x02, "cga"); + if (num > 1) + resource_init_mem_named(pos++, 0xb8000, 0x2000, "cga"); + break; + case VIDEO_TYPE_EGAM: + if (num > 0) + resource_init_io_named(pos++, 0x3bf, 0x10, "ega"); + if (num > 1) + resource_init_mem_named(pos++, 0xb0000, 0x8000, "ega"); + break; + case VIDEO_TYPE_EGAC: + if (num > 0) + resource_init_io_named(pos++, 0x3c0, 0x20, "ega"); + if (num > 1) { + if (__screen_info_has_ega_gfx(si->orig_video_mode)) + resource_init_mem_named(pos++, 0xa0000, 0x10000, "ega"); + else + resource_init_mem_named(pos++, 0xb8000, 0x8000, "ega"); + } + break; + case VIDEO_TYPE_VGAC: + if (num > 0) + resource_init_io_named(pos++, 0x3c0, 0x20, "vga+"); + if (num > 1) { + if (__screen_info_has_vga_gfx(si->orig_video_mode)) + resource_init_mem_named(pos++, 0xa0000, 0x10000, "vga+"); + else + resource_init_mem_named(pos++, 0xb8000, 0x8000, "vga+"); + } + break; + case VIDEO_TYPE_VLFB: + case VIDEO_TYPE_EFI: + base = __screen_info_lfb_base(si); + if (!base) + break; + size = __screen_info_lfb_size(si, type); + if (!size) + break; + if (num > 0) + resource_init_mem_named(pos++, base, size, "lfb"); + break; + case VIDEO_TYPE_PICA_S3: + case VIDEO_TYPE_MIPS_G364: + case VIDEO_TYPE_SGI: + case VIDEO_TYPE_TGAC: + case VIDEO_TYPE_SUN: + case VIDEO_TYPE_SUNPCI: + case VIDEO_TYPE_PMAC: + default: + /* not supported */ + return -EINVAL; + } + + return pos - r; +} +EXPORT_SYMBOL(screen_info_resources); diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h index eab7081392d5..e7a02c5609d1 100644 --- a/include/linux/screen_info.h +++ b/include/linux/screen_info.h @@ -4,6 +4,106 @@ #include +/** + * SCREEN_INFO_MAX_RESOURCES - maximum number of resources per screen_info + */ +#define SCREEN_INFO_MAX_RESOURCES 3 + +struct resource; + +static inline bool __screen_info_has_lfb(unsigned int type) +{ + return (type == VIDEO_TYPE_VLFB) || (type == VIDEO_TYPE_EFI); +} + +static inline u64 __screen_info_lfb_base(const struct screen_info *si) +{ + u64 lfb_base = si->lfb_base; + + if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE) + lfb_base |= (u64)si->ext_lfb_base << 32; + + return lfb_base; +} + +static inline u64 __screen_info_lfb_size(const struct screen_info *si, unsigned int type) +{ + u64 lfb_size = si->lfb_size; + + if (type == VIDEO_TYPE_VLFB) + lfb_size <<= 16; + return lfb_size; +} + +static inline unsigned int __screen_info_video_type(unsigned int type) +{ + switch (type) { + case VIDEO_TYPE_MDA: + case VIDEO_TYPE_CGA: + case VIDEO_TYPE_EGAM: + case VIDEO_TYPE_EGAC: + case VIDEO_TYPE_VGAC: + case VIDEO_TYPE_VLFB: + case VIDEO_TYPE_PICA_S3: + case VIDEO_TYPE_MIPS_G364: + case VIDEO_TYPE_SGI: + case VIDEO_TYPE_TGAC: + case VIDEO_TYPE_SUN: + case VIDEO_TYPE_SUNPCI: + case VIDEO_TYPE_PMAC: + case VIDEO_TYPE_EFI: + return type; + default: + return 0; + } +} + +/** + * screen_info_video_type() - Decodes the video type from struct screen_info + * @si: an instance of struct screen_info + * + * Returns: + * A VIDEO_TYPE_ constant representing si's type of video display, or 0 otherwise. + */ +static inline unsigned int screen_info_video_type(const struct screen_info *si) +{ + unsigned int type; + + // check if display output is on + if (!si->orig_video_isVGA) + return 0; + + // check for a known VIDEO_TYPE_ constant + type = __screen_info_video_type(si->orig_video_isVGA); + if (type) + return si->orig_video_isVGA; + + // check if text mode has been initialized + if (!si->orig_video_lines || !si->orig_video_cols) + return 0; + + // 80x25 text, mono + if (si->orig_video_mode == 0x07) { + if ((si->orig_video_ega_bx & 0xff) != 0x10) + return VIDEO_TYPE_EGAM; + else + return VIDEO_TYPE_MDA; + } + + // EGA/VGA, 16 colors + if ((si->orig_video_ega_bx & 0xff) != 0x10) { + if (si->orig_video_isVGA) + return VIDEO_TYPE_VGAC; + else + return VIDEO_TYPE_EGAC; + } + + // the rest... + return VIDEO_TYPE_CGA; +} + +ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num); + extern struct screen_info screen_info; #endif /* _SCREEN_INFO_H */ -- cgit v1.2.3 From 036105e3a776b6fc2fe0d262896a23ff2cc2e6b1 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 12 Feb 2024 10:06:10 +0100 Subject: video: Provide screen_info_get_pci_dev() to find screen_info's PCI device Add screen_info_get_pci_dev() to find the PCI device of an instance of screen_info. Does nothing on systems without PCI bus. v3: * search PCI device with pci_get_base_class() (Sui) v2: * remove ret from screen_info_pci_dev() (Javier) Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-3-tzimmermann@suse.de --- drivers/video/Makefile | 1 + drivers/video/screen_info_pci.c | 48 +++++++++++++++++++++++++++++++++++++++++ include/linux/screen_info.h | 10 +++++++++ 3 files changed, 59 insertions(+) create mode 100644 drivers/video/screen_info_pci.c (limited to 'include') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 117cbdbb58c2..ffbac4387c67 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO) += cmdline.o nomodeset.o obj-$(CONFIG_HDMI) += hdmi.o screen_info-y := screen_info_generic.o +screen_info-$(CONFIG_PCI) += screen_info_pci.o obj-$(CONFIG_VT) += console/ obj-$(CONFIG_FB_STI) += console/ diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c new file mode 100644 index 000000000000..d8985a54ce71 --- /dev/null +++ b/drivers/video/screen_info_pci.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +static struct pci_dev *__screen_info_pci_dev(struct resource *res) +{ + struct pci_dev *pdev = NULL; + const struct resource *r = NULL; + + if (!(res->flags & IORESOURCE_MEM)) + return NULL; + + while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) { + r = pci_find_resource(pdev, res); + } + + return pdev; +} + +/** + * screen_info_pci_dev() - Return PCI parent device that contains screen_info's framebuffer + * @si: the screen_info + * + * Returns: + * The screen_info's parent device or NULL on success, or a pointer-encoded + * errno value otherwise. The value NULL is not an error. It signals that no + * PCI device has been found. + */ +struct pci_dev *screen_info_pci_dev(const struct screen_info *si) +{ + struct resource res[SCREEN_INFO_MAX_RESOURCES]; + ssize_t i, numres; + + numres = screen_info_resources(si, res, ARRAY_SIZE(res)); + if (numres < 0) + return ERR_PTR(numres); + + for (i = 0; i < numres; ++i) { + struct pci_dev *pdev = __screen_info_pci_dev(&res[i]); + + if (pdev) + return pdev; + } + + return NULL; +} +EXPORT_SYMBOL(screen_info_pci_dev); diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h index e7a02c5609d1..0eae08e3c6f9 100644 --- a/include/linux/screen_info.h +++ b/include/linux/screen_info.h @@ -9,6 +9,7 @@ */ #define SCREEN_INFO_MAX_RESOURCES 3 +struct pci_dev; struct resource; static inline bool __screen_info_has_lfb(unsigned int type) @@ -104,6 +105,15 @@ static inline unsigned int screen_info_video_type(const struct screen_info *si) ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num); +#if defined(CONFIG_PCI) +struct pci_dev *screen_info_pci_dev(const struct screen_info *si); +#else +static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si) +{ + return NULL; +} +#endif + extern struct screen_info screen_info; #endif /* _SCREEN_INFO_H */ -- cgit v1.2.3 From 9eac534db0013aff9b9124985dab114600df9081 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 12 Feb 2024 10:06:11 +0100 Subject: firmware/sysfb: Set firmware-framebuffer parent device Set the firmware framebuffer's parent device, which usually is the graphics hardware's physical device. Integrates the framebuffer in the Linux device hierarchy and lets Linux handle dependencies among devices. For example, the graphics hardware won't be suspended while the firmware device is still active. v4: * fix build for CONFIG_SYSFB_SIMPLEFB=n, again v3: * fix build for CONFIG_SYSFB_SIMPLEFB=n (Sui) * test result of screen_info_pci_dev() for errors (Sui) v2: * detect parent device in sysfb_parent_dev() Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-4-tzimmermann@suse.de --- drivers/firmware/sysfb.c | 21 ++++++++++++++++++++- drivers/firmware/sysfb_simplefb.c | 5 ++++- include/linux/sysfb.h | 6 ++++-- 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 3c197db42c9d..4e104f3de4b9 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -69,9 +70,23 @@ void sysfb_disable(void) } EXPORT_SYMBOL_GPL(sysfb_disable); +static __init struct device *sysfb_parent_dev(const struct screen_info *si) +{ + struct pci_dev *pdev; + + pdev = screen_info_pci_dev(si); + if (IS_ERR(pdev)) + return ERR_CAST(pdev); + else if (pdev) + return &pdev->dev; + + return NULL; +} + static __init int sysfb_init(void) { struct screen_info *si = &screen_info; + struct device *parent; struct simplefb_platform_data mode; const char *name; bool compatible; @@ -83,10 +98,12 @@ static __init int sysfb_init(void) sysfb_apply_efi_quirks(); + parent = sysfb_parent_dev(si); + /* try to create a simple-framebuffer device */ compatible = sysfb_parse_mode(si, &mode); if (compatible) { - pd = sysfb_create_simplefb(si, &mode); + pd = sysfb_create_simplefb(si, &mode, parent); if (!IS_ERR(pd)) goto unlock_mutex; } @@ -109,6 +126,8 @@ static __init int sysfb_init(void) goto unlock_mutex; } + pd->dev.parent = parent; + sysfb_set_efifb_fwnode(pd); ret = platform_device_add_data(pd, si, sizeof(*si)); diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c index 74363ed7501f..75a186bf8f8e 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -91,7 +91,8 @@ __init bool sysfb_parse_mode(const struct screen_info *si, } __init struct platform_device *sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode) + const struct simplefb_platform_data *mode, + struct device *parent) { struct platform_device *pd; struct resource res; @@ -143,6 +144,8 @@ __init struct platform_device *sysfb_create_simplefb(const struct screen_info *s if (!pd) return ERR_PTR(-ENOMEM); + pd->dev.parent = parent; + sysfb_set_efifb_fwnode(pd); ret = platform_device_add_resources(pd, &res, 1); diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index 19cb803dd5ec..c9cb657dad08 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -91,7 +91,8 @@ static inline void sysfb_set_efifb_fwnode(struct platform_device *pd) bool sysfb_parse_mode(const struct screen_info *si, struct simplefb_platform_data *mode); struct platform_device *sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode); + const struct simplefb_platform_data *mode, + struct device *parent); #else /* CONFIG_SYSFB_SIMPLE */ @@ -102,7 +103,8 @@ static inline bool sysfb_parse_mode(const struct screen_info *si, } static inline struct platform_device *sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode) + const struct simplefb_platform_data *mode, + struct device *parent) { return ERR_PTR(-EINVAL); } -- cgit v1.2.3 From 78aa89d1dfba1e3cf4a2e053afa3b4c4ec622371 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 12 Feb 2024 10:06:15 +0100 Subject: firmware/sysfb: Update screen_info for relocated EFI framebuffers On ARM PCI systems, the PCI hierarchy might be reconfigured during boot and the firmware framebuffer might move as a result of that. The values in screen_info will then be invalid. Work around this problem by tracking the framebuffer's initial location before it get relocated; then fix the screen_info state between reloaction and creating the firmware framebuffer's device. This functionality has been lifted from efifb. See the commit message of commit 55d728a40d36 ("efi/fb: Avoid reconfiguration of BAR that covers the framebuffer") for more information. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-8-tzimmermann@suse.de --- drivers/firmware/sysfb.c | 2 + drivers/video/screen_info_pci.c | 88 +++++++++++++++++++++++++++++++++++++++++ include/linux/screen_info.h | 16 ++++++++ 3 files changed, 106 insertions(+) (limited to 'include') diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 170b7cd0cfcb..a6b48703dc9e 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -118,6 +118,8 @@ static __init int sysfb_init(void) bool compatible; int ret = 0; + screen_info_apply_fixups(); + mutex_lock(&disable_lock); if (disabled) goto unlock_mutex; diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c index d8985a54ce71..6c5833517141 100644 --- a/drivers/video/screen_info_pci.c +++ b/drivers/video/screen_info_pci.c @@ -1,7 +1,95 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include +#include + +static struct pci_dev *screen_info_lfb_pdev; +static size_t screen_info_lfb_bar; +static resource_size_t screen_info_lfb_offset; +static struct resource screen_info_lfb_res = DEFINE_RES_MEM(0, 0); + +static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr) +{ + u64 size = __screen_info_lfb_size(si, screen_info_video_type(si)); + + if (screen_info_lfb_offset > resource_size(pr)) + return false; + if (size > resource_size(pr)) + return false; + if (resource_size(pr) - size < screen_info_lfb_offset) + return false; + + return true; +} + +void screen_info_apply_fixups(void) +{ + struct screen_info *si = &screen_info; + + if (screen_info_lfb_pdev) { + struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar]; + + if (pr->start != screen_info_lfb_res.start) { + if (__screen_info_relocation_is_valid(si, pr)) { + /* + * Only update base if we have an actual + * relocation to a valid I/O range. + */ + __screen_info_set_lfb_base(si, pr->start + screen_info_lfb_offset); + pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n", + &screen_info_lfb_offset, pr); + } else { + pr_warn("Invalid relocating, disabling firmware framebuffer\n"); + } + } + } +} + +static void screen_info_fixup_lfb(struct pci_dev *pdev) +{ + unsigned int type; + struct resource res[SCREEN_INFO_MAX_RESOURCES]; + size_t i, numres; + int ret; + const struct screen_info *si = &screen_info; + + if (screen_info_lfb_pdev) + return; // already found + + type = screen_info_video_type(si); + if (type != VIDEO_TYPE_EFI) + return; // only applies to EFI + + ret = screen_info_resources(si, res, ARRAY_SIZE(res)); + if (ret < 0) + return; + numres = ret; + + for (i = 0; i < numres; ++i) { + struct resource *r = &res[i]; + const struct resource *pr; + + if (!(r->flags & IORESOURCE_MEM)) + continue; + pr = pci_find_resource(pdev, r); + if (!pr) + continue; + + /* + * We've found a PCI device with the framebuffer + * resource. Store away the parameters to track + * relocation of the framebuffer aperture. + */ + screen_info_lfb_pdev = pdev; + screen_info_lfb_bar = pr - pdev->resource; + screen_info_lfb_offset = r->start - pr->start; + memcpy(&screen_info_lfb_res, r, sizeof(screen_info_lfb_res)); + } +} +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16, + screen_info_fixup_lfb); static struct pci_dev *__screen_info_pci_dev(struct resource *res) { diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h index 0eae08e3c6f9..75303c126285 100644 --- a/include/linux/screen_info.h +++ b/include/linux/screen_info.h @@ -4,6 +4,8 @@ #include +#include + /** * SCREEN_INFO_MAX_RESOURCES - maximum number of resources per screen_info */ @@ -27,6 +29,17 @@ static inline u64 __screen_info_lfb_base(const struct screen_info *si) return lfb_base; } +static inline void __screen_info_set_lfb_base(struct screen_info *si, u64 lfb_base) +{ + si->lfb_base = lfb_base & GENMASK_ULL(31, 0); + si->ext_lfb_base = (lfb_base & GENMASK_ULL(63, 32)) >> 32; + + if (si->ext_lfb_base) + si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; + else + si->capabilities &= ~VIDEO_CAPABILITY_64BIT_BASE; +} + static inline u64 __screen_info_lfb_size(const struct screen_info *si, unsigned int type) { u64 lfb_size = si->lfb_size; @@ -106,8 +119,11 @@ static inline unsigned int screen_info_video_type(const struct screen_info *si) ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num); #if defined(CONFIG_PCI) +void screen_info_apply_fixups(void); struct pci_dev *screen_info_pci_dev(const struct screen_info *si); #else +static inline void screen_info_apply_fixups(void) +{ } static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si) { return NULL; -- cgit v1.2.3 From 4276d28e1da689e0cad3e88521c2cddbea42eb6e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 13 Feb 2024 14:42:19 -0800 Subject: iosys-map: fix typo Correct a spello/typo in comments. Signed-off-by: Randy Dunlap Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20240213224219.10644-1-rdunlap@infradead.org --- include/linux/iosys-map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index e3649a6563dd..4696abfd311c 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -34,7 +34,7 @@ * the same driver for allocation, read and write operations. * * Open-coding access to :c:type:`struct iosys_map ` is considered - * bad style. Rather then accessing its fields directly, use one of the provided + * bad style. Rather than accessing its fields directly, use one of the provided * helper functions, or implement your own. For example, instances of * :c:type:`struct iosys_map ` can be initialized statically with * IOSYS_MAP_INIT_VADDR(), or at runtime with iosys_map_set_vaddr(). These -- cgit v1.2.3 From b112364867499e1327801da200868a6c506465fa Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 8 Feb 2024 08:25:10 +0000 Subject: drm/i915: Add GuC submission interface version query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new query to the GuC submission interface version. Mesa intends to use this information to check for old firmware versions with a known bug where using the render and compute command streamers simultaneously can cause GPU hangs due issues in firmware scheduling. Based on patches from Vivaik and Joonas. Compile tested only. v2: * Added branch version. Signed-off-by: Tvrtko Ursulin Cc: Kenneth Graunke Cc: Jose Souza Cc: Sagar Ghuge Cc: Paulo Zanoni Cc: John Harrison Cc: Rodrigo Vivi Cc: Jani Nikula Cc: Tvrtko Ursulin Cc: Vivaik Balasubrawmanian Cc: Joonas Lahtinen Reviewed-by: José Roberto de Souza Tested-by: José Roberto de Souza Signed-off-by: José Roberto de Souza Link: https://patchwork.freedesktop.org/patch/msgid/20240208082510.1363268-1-tvrtko.ursulin@linux.intel.com --- drivers/gpu/drm/i915/i915_query.c | 33 +++++++++++++++++++++++++++++++++ include/uapi/drm/i915_drm.h | 12 ++++++++++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 00871ef99792..d4dba1240b40 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -551,6 +551,38 @@ static int query_hwconfig_blob(struct drm_i915_private *i915, return hwconfig->size; } +static int +query_guc_submission_version(struct drm_i915_private *i915, + struct drm_i915_query_item *query) +{ + struct drm_i915_query_guc_submission_version __user *query_ptr = + u64_to_user_ptr(query->data_ptr); + struct drm_i915_query_guc_submission_version ver; + struct intel_guc *guc = &to_gt(i915)->uc.guc; + const size_t size = sizeof(ver); + int ret; + + if (!intel_uc_uses_guc_submission(&to_gt(i915)->uc)) + return -ENODEV; + + ret = copy_query_item(&ver, size, size, query); + if (ret != 0) + return ret; + + if (ver.branch || ver.major || ver.minor || ver.patch) + return -EINVAL; + + ver.branch = 0; + ver.major = guc->submission_version.major; + ver.minor = guc->submission_version.minor; + ver.patch = guc->submission_version.patch; + + if (copy_to_user(query_ptr, &ver, size)) + return -EFAULT; + + return 0; +} + static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv, struct drm_i915_query_item *query_item) = { query_topology_info, @@ -559,6 +591,7 @@ static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv, query_memregion_info, query_hwconfig_blob, query_geometry_subslices, + query_guc_submission_version, }; int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index fd4f9574d177..bd87386a8243 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -3013,6 +3013,7 @@ struct drm_i915_query_item { * - %DRM_I915_QUERY_MEMORY_REGIONS (see struct drm_i915_query_memory_regions) * - %DRM_I915_QUERY_HWCONFIG_BLOB (see `GuC HWCONFIG blob uAPI`) * - %DRM_I915_QUERY_GEOMETRY_SUBSLICES (see struct drm_i915_query_topology_info) + * - %DRM_I915_QUERY_GUC_SUBMISSION_VERSION (see struct drm_i915_query_guc_submission_version) */ __u64 query_id; #define DRM_I915_QUERY_TOPOLOGY_INFO 1 @@ -3021,6 +3022,7 @@ struct drm_i915_query_item { #define DRM_I915_QUERY_MEMORY_REGIONS 4 #define DRM_I915_QUERY_HWCONFIG_BLOB 5 #define DRM_I915_QUERY_GEOMETRY_SUBSLICES 6 +#define DRM_I915_QUERY_GUC_SUBMISSION_VERSION 7 /* Must be kept compact -- no holes and well documented */ /** @@ -3566,6 +3568,16 @@ struct drm_i915_query_memory_regions { struct drm_i915_memory_region_info regions[]; }; +/** + * struct drm_i915_query_guc_submission_version - query GuC submission interface version + */ +struct drm_i915_query_guc_submission_version { + __u32 branch; + __u32 major; + __u32 minor; + __u32 patch; +}; + /** * DOC: GuC HWCONFIG blob uAPI * -- cgit v1.2.3 From 425b463859eda4f4c071e517267acdd1c0d731bd Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Wed, 14 Feb 2024 11:46:30 -0300 Subject: drm/i915: Update ADL-N PCI IDs Extend the list of ADL-N PCI IDs to contain two new entries. Bspec: 68397 Signed-off-by: Gustavo Sousa Reviewed-by: Dnyaneshwar Bhadane Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20240214144629.106702-2-gustavo.sousa@intel.com --- include/drm/i915_pciids.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 07779a11758e..28a96aa1e08f 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -672,7 +672,9 @@ #define INTEL_ADLN_IDS(info) \ INTEL_VGA_DEVICE(0x46D0, info), \ INTEL_VGA_DEVICE(0x46D1, info), \ - INTEL_VGA_DEVICE(0x46D2, info) + INTEL_VGA_DEVICE(0x46D2, info), \ + INTEL_VGA_DEVICE(0x46D3, info), \ + INTEL_VGA_DEVICE(0x46D4, info) /* RPL-S */ #define INTEL_RPLS_IDS(info) \ -- cgit v1.2.3 From c4c96d1417fdb5559b45f5fefa90520c0d29a095 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 15 Feb 2024 14:24:15 +0100 Subject: drm: Spelling s/hardward/hardware/g Fix misspellings of "hardware". Signed-off-by: Geert Uytterhoeven Reviewed-by: Neil Armstrong Reviewed-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/94c9b76ee906d1b790dfcc435f4221b1197df586.1708003402.git.geert+renesas@glider.be --- include/drm/drm_bridge.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 7293c02e17c5..3606e1a7f965 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -107,7 +107,7 @@ struct drm_bridge_funcs { * Since this function is both called from the check phase of an atomic * commit, and the mode validation in the probe paths it is not allowed * to look at anything else but the passed-in mode, and validate it - * against configuration-invariant hardward constraints. Any further + * against configuration-invariant hardware constraints. Any further * limits which depend upon the configuration can only be checked in * @mode_fixup. * -- cgit v1.2.3 From b31f5eba32ae8cc28e7cfa5a55ec8670d8c718e2 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 12 Feb 2024 16:04:24 -0500 Subject: drm: add drm_gem_object_is_shared_for_memory_stats() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper so that drm drivers can consistently report shared status via the fdinfo shared memory stats interface. In addition to handle count, show buffers as shared if they are shared via dma-buf as well (e.g., shared with v4l or some other subsystem). v2: switch to inline function Link: https://lore.kernel.org/all/20231207180225.439482-1-alexander.deucher@amd.com/ Reviewed-by: Tvrtko Ursulin (v1) Signed-off-by: Alex Deucher Reviewed-by: Christian König Signed-off-by: Christian König --- include/drm/drm_gem.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 369505447acd..2ebec3984cd4 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -553,6 +553,19 @@ unsigned long drm_gem_lru_scan(struct drm_gem_lru *lru, int drm_gem_evict(struct drm_gem_object *obj); +/** + * drm_gem_object_is_shared_for_memory_stats - helper for shared memory stats + * + * This helper should only be used for fdinfo shared memory stats to determine + * if a GEM object is shared. + * + * @obj: obj in question + */ +static inline bool drm_gem_object_is_shared_for_memory_stats(struct drm_gem_object *obj) +{ + return (obj->handle_count > 1) || obj->dma_buf; +} + #ifdef CONFIG_LOCKDEP /** * drm_gem_gpuva_set_lock() - Set the lock protecting accesses to the gpuva list. -- cgit v1.2.3 From f1a9abc0cf311375695bede1590364864c05976d Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Fri, 9 Feb 2024 12:34:44 +0100 Subject: drm/xe/uapi: Remove support for persistent exec_queues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persistent exec_queues delays explicit destruction of exec_queues until they are done executing, but destruction on process exit is still immediate. It turns out no UMD is relying on this functionality, so remove it. If there turns out to be a use-case in the future, let's re-add. Persistent exec_queues were never used for LR VMs v2: - Don't add an "UNUSED" define for the missing property (Lucas, Rodrigo) v3: - Remove the remaining struct xe_exec_queue::persistent state (Niranjana, Lucas) Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: Rodrigo Vivi Cc: Matthew Brost Cc: David Airlie Cc: Daniel Vetter Cc: Lucas De Marchi Cc: Francois Dugast Signed-off-by: Thomas Hellström Reviewed-by: Lucas De Marchi Acked-by: José Roberto de Souza Link: https://patchwork.freedesktop.org/patch/msgid/20240209113444.8396-1-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/xe/xe_device.c | 39 -------------------------------- drivers/gpu/drm/xe/xe_device.h | 4 ---- drivers/gpu/drm/xe/xe_device_types.h | 8 ------- drivers/gpu/drm/xe/xe_exec_queue.c | 36 +++++------------------------ drivers/gpu/drm/xe/xe_exec_queue_types.h | 10 -------- drivers/gpu/drm/xe/xe_execlist.c | 2 -- drivers/gpu/drm/xe/xe_guc_submit.c | 2 -- include/uapi/drm/xe_drm.h | 1 - 8 files changed, 6 insertions(+), 96 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 5b84d7305520..1cf7561c8b4d 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -86,9 +86,6 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file) return 0; } -static void device_kill_persistent_exec_queues(struct xe_device *xe, - struct xe_file *xef); - static void xe_file_close(struct drm_device *dev, struct drm_file *file) { struct xe_device *xe = to_xe_device(dev); @@ -105,8 +102,6 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file) mutex_unlock(&xef->exec_queue.lock); xa_destroy(&xef->exec_queue.xa); mutex_destroy(&xef->exec_queue.lock); - device_kill_persistent_exec_queues(xe, xef); - mutex_lock(&xef->vm.lock); xa_for_each(&xef->vm.xa, idx, vm) xe_vm_close_and_put(vm); @@ -258,9 +253,6 @@ struct xe_device *xe_device_create(struct pci_dev *pdev, xa_erase(&xe->usm.asid_to_vm, asid); } - drmm_mutex_init(&xe->drm, &xe->persistent_engines.lock); - INIT_LIST_HEAD(&xe->persistent_engines.list); - spin_lock_init(&xe->pinned.lock); INIT_LIST_HEAD(&xe->pinned.kernel_bo_present); INIT_LIST_HEAD(&xe->pinned.external_vram); @@ -599,37 +591,6 @@ void xe_device_shutdown(struct xe_device *xe) { } -void xe_device_add_persistent_exec_queues(struct xe_device *xe, struct xe_exec_queue *q) -{ - mutex_lock(&xe->persistent_engines.lock); - list_add_tail(&q->persistent.link, &xe->persistent_engines.list); - mutex_unlock(&xe->persistent_engines.lock); -} - -void xe_device_remove_persistent_exec_queues(struct xe_device *xe, - struct xe_exec_queue *q) -{ - mutex_lock(&xe->persistent_engines.lock); - if (!list_empty(&q->persistent.link)) - list_del(&q->persistent.link); - mutex_unlock(&xe->persistent_engines.lock); -} - -static void device_kill_persistent_exec_queues(struct xe_device *xe, - struct xe_file *xef) -{ - struct xe_exec_queue *q, *next; - - mutex_lock(&xe->persistent_engines.lock); - list_for_each_entry_safe(q, next, &xe->persistent_engines.list, - persistent.link) - if (q->persistent.xef == xef) { - xe_exec_queue_kill(q); - list_del_init(&q->persistent.link); - } - mutex_unlock(&xe->persistent_engines.lock); -} - void xe_device_wmb(struct xe_device *xe) { struct xe_gt *gt = xe_root_mmio_gt(xe); diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index 462f59e902b1..14be34d9f543 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -42,10 +42,6 @@ int xe_device_probe(struct xe_device *xe); void xe_device_remove(struct xe_device *xe); void xe_device_shutdown(struct xe_device *xe); -void xe_device_add_persistent_exec_queues(struct xe_device *xe, struct xe_exec_queue *q); -void xe_device_remove_persistent_exec_queues(struct xe_device *xe, - struct xe_exec_queue *q); - void xe_device_wmb(struct xe_device *xe); static inline struct xe_file *to_xe_file(const struct drm_file *file) diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index eb2b806a1d23..9785eef2e5a4 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -348,14 +348,6 @@ struct xe_device { struct mutex lock; } usm; - /** @persistent_engines: engines that are closed but still running */ - struct { - /** @persistent_engines.lock: protects persistent engines */ - struct mutex lock; - /** @persistent_engines.list: list of persistent engines */ - struct list_head list; - } persistent_engines; - /** @pinned: pinned BO state */ struct { /** @pinned.lock: protected pinned BO list state */ diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index da84ac93a559..dda90f0c9890 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -60,7 +60,6 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe, q->fence_irq = >->fence_irq[hwe->class]; q->ring_ops = gt->ring_ops[hwe->class]; q->ops = gt->exec_queue_ops; - INIT_LIST_HEAD(&q->persistent.link); INIT_LIST_HEAD(&q->compute.link); INIT_LIST_HEAD(&q->multi_gt_link); @@ -375,23 +374,6 @@ static int exec_queue_set_preemption_timeout(struct xe_device *xe, return 0; } -static int exec_queue_set_persistence(struct xe_device *xe, struct xe_exec_queue *q, - u64 value, bool create) -{ - if (XE_IOCTL_DBG(xe, !create)) - return -EINVAL; - - if (XE_IOCTL_DBG(xe, xe_vm_in_preempt_fence_mode(q->vm))) - return -EINVAL; - - if (value) - q->flags |= EXEC_QUEUE_FLAG_PERSISTENT; - else - q->flags &= ~EXEC_QUEUE_FLAG_PERSISTENT; - - return 0; -} - static int exec_queue_set_job_timeout(struct xe_device *xe, struct xe_exec_queue *q, u64 value, bool create) { @@ -465,7 +447,6 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = { [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PREEMPTION_TIMEOUT] = exec_queue_set_preemption_timeout, - [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PERSISTENCE] = exec_queue_set_persistence, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_JOB_TIMEOUT] = exec_queue_set_job_timeout, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_ACC_TRIGGER] = exec_queue_set_acc_trigger, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_ACC_NOTIFY] = exec_queue_set_acc_notify, @@ -492,6 +473,9 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe, return -EINVAL; idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs)); + if (!exec_queue_set_property_funcs[idx]) + return -EINVAL; + return exec_queue_set_property_funcs[idx](xe, q, ext.value, create); } @@ -703,8 +687,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, /* The migration vm doesn't hold rpm ref */ xe_device_mem_access_get(xe); - flags = EXEC_QUEUE_FLAG_PERSISTENT | EXEC_QUEUE_FLAG_VM | - (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0); + flags = EXEC_QUEUE_FLAG_VM | (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0); migrate_vm = xe_migrate_get_vm(gt_to_tile(gt)->migrate); new = xe_exec_queue_create(xe, migrate_vm, logical_mask, @@ -755,9 +738,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, } q = xe_exec_queue_create(xe, vm, logical_mask, - args->width, hwe, - xe_vm_in_lr_mode(vm) ? 0 : - EXEC_QUEUE_FLAG_PERSISTENT, + args->width, hwe, 0, args->extensions); up_read(&vm->lock); xe_vm_put(vm); @@ -774,8 +755,6 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, } } - q->persistent.xef = xef; - mutex_lock(&xef->exec_queue.lock); err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL); mutex_unlock(&xef->exec_queue.lock); @@ -918,10 +897,7 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data, if (XE_IOCTL_DBG(xe, !q)) return -ENOENT; - if (!(q->flags & EXEC_QUEUE_FLAG_PERSISTENT)) - xe_exec_queue_kill(q); - else - xe_device_add_persistent_exec_queues(xe, q); + xe_exec_queue_kill(q); trace_xe_exec_queue_close(q); xe_exec_queue_put(q); diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 3df8571e4a07..c40240e88068 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -105,16 +105,6 @@ struct xe_exec_queue { struct xe_guc_exec_queue *guc; }; - /** - * @persistent: persistent exec queue state - */ - struct { - /** @persistent.xef: file which this exec queue belongs to */ - struct xe_file *xef; - /** @persisiten.link: link in list of persistent exec queues */ - struct list_head link; - } persistent; - /** * @parallel: parallel submission state */ diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c index 58dfe6a78ffe..1788e78caf5c 100644 --- a/drivers/gpu/drm/xe/xe_execlist.c +++ b/drivers/gpu/drm/xe/xe_execlist.c @@ -378,8 +378,6 @@ static void execlist_exec_queue_fini_async(struct work_struct *w) list_del(&exl->active_link); spin_unlock_irqrestore(&exl->port->lock, flags); - if (q->flags & EXEC_QUEUE_FLAG_PERSISTENT) - xe_device_remove_persistent_exec_queues(xe, q); drm_sched_entity_fini(&exl->entity); drm_sched_fini(&exl->sched); kfree(exl); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 4744668ef60a..efee08680aea 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1031,8 +1031,6 @@ static void __guc_exec_queue_fini_async(struct work_struct *w) if (xe_exec_queue_is_lr(q)) cancel_work_sync(&ge->lr_tdr); - if (q->flags & EXEC_QUEUE_FLAG_PERSISTENT) - xe_device_remove_persistent_exec_queues(gt_to_xe(q->gt), q); release_guc_id(guc, q); xe_sched_entity_fini(&ge->entity); xe_sched_fini(&ge->sched); diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 0819959a5222..dce06de592d0 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -1076,7 +1076,6 @@ struct drm_xe_exec_queue_create { #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY 0 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE 1 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PREEMPTION_TIMEOUT 2 -#define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PERSISTENCE 3 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_JOB_TIMEOUT 4 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_ACC_TRIGGER 5 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_ACC_NOTIFY 6 -- cgit v1.2.3 From 5cf0fbf7637410aea88819e64a4bd5ea14ccbbfc Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 19 Feb 2024 13:25:17 +0000 Subject: drm/i915: Add some boring kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tooling appears very strict so lets pacify it by adding some comments, even if fields are completely self-explanatory. Signed-off-by: Tvrtko Ursulin Fixes: b11236486749 ("drm/i915: Add GuC submission interface version query") Reported-by: Stephen Rothwell Cc: Jose Souza Reviewed-by: José Roberto de Souza Link: https://patchwork.freedesktop.org/patch/msgid/20240219132517.1868604-1-tvrtko.ursulin@linux.intel.com --- include/uapi/drm/i915_drm.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index bd87386a8243..2ee338860b7e 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -3572,9 +3572,13 @@ struct drm_i915_query_memory_regions { * struct drm_i915_query_guc_submission_version - query GuC submission interface version */ struct drm_i915_query_guc_submission_version { + /** @branch: Firmware branch version. */ __u32 branch; + /** @major: Firmware major version. */ __u32 major; + /** @minor: Firmware minor version. */ __u32 minor; + /** @patch: Firmware patch version. */ __u32 patch; }; -- cgit v1.2.3 From 76a86b58d2b3de31e88acb487ebfa0c3cc7c41d2 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 21 Feb 2024 14:30:18 +0100 Subject: drm/xe: Add uapi for dumpable bos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the flag XE_VM_BIND_FLAG_DUMPABLE to notify devcoredump that this mapping should be dumped. This is not hooked up, but the uapi should be ready before merging. It's likely easier to dump the contents of the bo's at devcoredump readout time, so it's better if the bos will stay unmodified after a hang. The NEEDS_CPU_MAPPING flag is removed as requirement. Signed-off-by: Maarten Lankhorst Reviewed-by: José Roberto de Souza Link: https://patchwork.freedesktop.org/patch/msgid/20240221133024.898315-3-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/xe/xe_vm.c | 3 ++- include/uapi/drm/xe_drm.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 23a44ef85aa4..08462f80f000 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -2722,7 +2722,8 @@ static int vm_bind_ioctl_ops_execute(struct xe_vm *vm, #define SUPPORTED_FLAGS \ (DRM_XE_VM_BIND_FLAG_READONLY | \ - DRM_XE_VM_BIND_FLAG_IMMEDIATE | DRM_XE_VM_BIND_FLAG_NULL) + DRM_XE_VM_BIND_FLAG_IMMEDIATE | DRM_XE_VM_BIND_FLAG_NULL | \ + DRM_XE_VM_BIND_FLAG_DUMPABLE) #define XE_64K_PAGE_MASK 0xffffull #define ALL_DRM_XE_SYNCS_FLAGS (DRM_XE_SYNCS_FLAG_WAIT_FOR_OP) diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index dce06de592d0..2fefec9c0e94 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -961,6 +961,7 @@ struct drm_xe_vm_bind_op { #define DRM_XE_VM_BIND_FLAG_READONLY (1 << 0) #define DRM_XE_VM_BIND_FLAG_IMMEDIATE (1 << 1) #define DRM_XE_VM_BIND_FLAG_NULL (1 << 2) +#define DRM_XE_VM_BIND_FLAG_DUMPABLE (1 << 3) /** @flags: Bind flags */ __u32 flags; -- cgit v1.2.3 From 47f419e07111acecab3b529d4ae31a28985f5b61 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 20 Feb 2024 11:53:46 -0800 Subject: drm/dp: move intel_dp_vsc_sdp_pack() to generic helper intel_dp_vsc_sdp_pack() can be re-used by other DRM drivers as well. Lets move this to drm_dp_helper to achieve this. changes in v2: - rebased on top of drm-tip Acked-by: Dmitry Baryshkov Signed-off-by: Abhinav Kumar Acked-by: Jani Nikula Signed-off-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240220195348.1270854-1-quic_abhinavk@quicinc.com --- drivers/gpu/drm/display/drm_dp_helper.c | 78 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_dp.c | 71 +----------------------------- include/drm/display/drm_dp_helper.h | 3 ++ 3 files changed, 83 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 8d6ce46471ae..6c91f400ecb1 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2913,6 +2913,84 @@ void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc) } EXPORT_SYMBOL(drm_dp_vsc_sdp_log); +/** + * drm_dp_vsc_sdp_pack() - pack a given vsc sdp into generic dp_sdp + * @vsc: vsc sdp initialized according to its purpose as defined in + * table 2-118 - table 2-120 in DP 1.4a specification + * @sdp: valid handle to the generic dp_sdp which will be packed + * @size: valid size of the passed sdp handle + * + * Returns length of sdp on success and error code on failure + */ +ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, + struct dp_sdp *sdp, size_t size) +{ + size_t length = sizeof(struct dp_sdp); + + if (size < length) + return -ENOSPC; + + memset(sdp, 0, size); + + /* + * Prepare VSC Header for SU as per DP 1.4a spec, Table 2-119 + * VSC SDP Header Bytes + */ + sdp->sdp_header.HB0 = 0; /* Secondary-Data Packet ID = 0 */ + sdp->sdp_header.HB1 = vsc->sdp_type; /* Secondary-data Packet Type */ + sdp->sdp_header.HB2 = vsc->revision; /* Revision Number */ + sdp->sdp_header.HB3 = vsc->length; /* Number of Valid Data Bytes */ + + if (vsc->revision == 0x6) { + sdp->db[0] = 1; + sdp->db[3] = 1; + } + + /* + * Revision 0x5 and revision 0x7 supports Pixel Encoding/Colorimetry + * Format as per DP 1.4a spec and DP 2.0 respectively. + */ + if (!(vsc->revision == 0x5 || vsc->revision == 0x7)) + goto out; + + /* VSC SDP Payload for DB16 through DB18 */ + /* Pixel Encoding and Colorimetry Formats */ + sdp->db[16] = (vsc->pixelformat & 0xf) << 4; /* DB16[7:4] */ + sdp->db[16] |= vsc->colorimetry & 0xf; /* DB16[3:0] */ + + switch (vsc->bpc) { + case 6: + /* 6bpc: 0x0 */ + break; + case 8: + sdp->db[17] = 0x1; /* DB17[3:0] */ + break; + case 10: + sdp->db[17] = 0x2; + break; + case 12: + sdp->db[17] = 0x3; + break; + case 16: + sdp->db[17] = 0x4; + break; + default: + WARN(1, "Missing case %d\n", vsc->bpc); + return -EINVAL; + } + + /* Dynamic Range and Component Bit Depth */ + if (vsc->dynamic_range == DP_DYNAMIC_RANGE_CTA) + sdp->db[17] |= 0x80; /* DB17[7] */ + + /* Content Type */ + sdp->db[18] = vsc->content_type & 0x7; + +out: + return length; +} +EXPORT_SYMBOL(drm_dp_vsc_sdp_pack); + /** * drm_dp_get_pcon_max_frl_bw() - maximum frl supported by PCON * @dpcd: DisplayPort configuration data diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index f5ef95da5534..a18ee12e88cd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4110,73 +4110,6 @@ intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, return false; } -static ssize_t intel_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, - struct dp_sdp *sdp, size_t size) -{ - size_t length = sizeof(struct dp_sdp); - - if (size < length) - return -ENOSPC; - - memset(sdp, 0, size); - - /* - * Prepare VSC Header for SU as per DP 1.4a spec, Table 2-119 - * VSC SDP Header Bytes - */ - sdp->sdp_header.HB0 = 0; /* Secondary-Data Packet ID = 0 */ - sdp->sdp_header.HB1 = vsc->sdp_type; /* Secondary-data Packet Type */ - sdp->sdp_header.HB2 = vsc->revision; /* Revision Number */ - sdp->sdp_header.HB3 = vsc->length; /* Number of Valid Data Bytes */ - - if (vsc->revision == 0x6) { - sdp->db[0] = 1; - sdp->db[3] = 1; - } - - /* - * Revision 0x5 and revision 0x7 supports Pixel Encoding/Colorimetry - * Format as per DP 1.4a spec and DP 2.0 respectively. - */ - if (!(vsc->revision == 0x5 || vsc->revision == 0x7)) - goto out; - - /* VSC SDP Payload for DB16 through DB18 */ - /* Pixel Encoding and Colorimetry Formats */ - sdp->db[16] = (vsc->pixelformat & 0xf) << 4; /* DB16[7:4] */ - sdp->db[16] |= vsc->colorimetry & 0xf; /* DB16[3:0] */ - - switch (vsc->bpc) { - case 6: - /* 6bpc: 0x0 */ - break; - case 8: - sdp->db[17] = 0x1; /* DB17[3:0] */ - break; - case 10: - sdp->db[17] = 0x2; - break; - case 12: - sdp->db[17] = 0x3; - break; - case 16: - sdp->db[17] = 0x4; - break; - default: - MISSING_CASE(vsc->bpc); - break; - } - /* Dynamic Range and Component Bit Depth */ - if (vsc->dynamic_range == DP_DYNAMIC_RANGE_CTA) - sdp->db[17] |= 0x80; /* DB17[7] */ - - /* Content Type */ - sdp->db[18] = vsc->content_type & 0x7; - -out: - return length; -} - static ssize_t intel_dp_hdr_metadata_infoframe_sdp_pack(struct drm_i915_private *i915, const struct hdmi_drm_infoframe *drm_infoframe, @@ -4269,8 +4202,8 @@ static void intel_write_dp_sdp(struct intel_encoder *encoder, switch (type) { case DP_SDP_VSC: - len = intel_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp, - sizeof(sdp)); + len = drm_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp, + sizeof(sdp)); break; case HDMI_PACKET_TYPE_GAMUT_METADATA: len = intel_dp_hdr_metadata_infoframe_sdp_pack(dev_priv, diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index d02014a87f12..8474504d4c88 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -812,4 +812,7 @@ int drm_dp_bw_overhead(int lane_count, int hactive, int bpp_x16, unsigned long flags); int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); +ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, + struct dp_sdp *sdp, size_t size); + #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3 From b55b88d86fec1d3edf60489b25ed998a3f0848cb Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 20 Feb 2024 11:53:47 -0800 Subject: drm/dp: drop the size parameter from drm_dp_vsc_sdp_pack() Currently the size parameter of drm_dp_vsc_sdp_pack() is always the size of struct dp_sdp. Hence lets drop this parameter and use sizeof() directly. Suggested-by: Dmitry Baryshkov Signed-off-by: Abhinav Kumar Acked-by: Dmitry Baryshkov Acked-by: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Signed-off-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240220195348.1270854-2-quic_abhinavk@quicinc.com --- drivers/gpu/drm/display/drm_dp_helper.c | 8 ++------ drivers/gpu/drm/i915/display/intel_dp.c | 3 +-- include/drm/display/drm_dp_helper.h | 3 +-- 3 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 6c91f400ecb1..10ee82e34de7 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2918,19 +2918,15 @@ EXPORT_SYMBOL(drm_dp_vsc_sdp_log); * @vsc: vsc sdp initialized according to its purpose as defined in * table 2-118 - table 2-120 in DP 1.4a specification * @sdp: valid handle to the generic dp_sdp which will be packed - * @size: valid size of the passed sdp handle * * Returns length of sdp on success and error code on failure */ ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, - struct dp_sdp *sdp, size_t size) + struct dp_sdp *sdp) { size_t length = sizeof(struct dp_sdp); - if (size < length) - return -ENOSPC; - - memset(sdp, 0, size); + memset(sdp, 0, sizeof(struct dp_sdp)); /* * Prepare VSC Header for SU as per DP 1.4a spec, Table 2-119 diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index a18ee12e88cd..756c15791a3c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4202,8 +4202,7 @@ static void intel_write_dp_sdp(struct intel_encoder *encoder, switch (type) { case DP_SDP_VSC: - len = drm_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp, - sizeof(sdp)); + len = drm_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp); break; case HDMI_PACKET_TYPE_GAMUT_METADATA: len = intel_dp_hdr_metadata_infoframe_sdp_pack(dev_priv, diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 8474504d4c88..1f41994796d3 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -812,7 +812,6 @@ int drm_dp_bw_overhead(int lane_count, int hactive, int bpp_x16, unsigned long flags); int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); -ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, - struct dp_sdp *sdp, size_t size); +ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, struct dp_sdp *sdp); #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3 From 0d024974014f39207c5f52e770059b5bac35ea6d Mon Sep 17 00:00:00 2001 From: Paloma Arellano Date: Thu, 15 Feb 2024 11:15:56 -0800 Subject: drm/dp: add an API to indicate if sink supports VSC SDP YUV420 format is supported only in the VSC SDP packet and not through MSA. Hence add an API which indicates the sink support which can be used by the rest of the DP programming. changes in v5: - rebased on top of drm-tip changes in v4: - bail out early if dpcd rev check fails changes in v3: - fix the commit title prefix to drm/dp - get rid of redundant !! - break out this change from series [1] to get acks from drm core maintainers Changes in v2: - Move VSC SDP support check API from dp_panel.c to drm_dp_helper.c [1]: https://patchwork.freedesktop.org/series/129180/ Reviewed-by: Dmitry Baryshkov Signed-off-by: Paloma Arellano Signed-off-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240215191556.3227259-1-quic_abhinavk@quicinc.com --- drivers/gpu/drm/display/drm_dp_helper.c | 23 +++++++++++++++++++++++ include/drm/display/drm_dp_helper.h | 2 ++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 10ee82e34de7..9ac52cf5d4d8 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2913,6 +2913,29 @@ void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc) } EXPORT_SYMBOL(drm_dp_vsc_sdp_log); +/** + * drm_dp_vsc_sdp_supported() - check if vsc sdp is supported + * @aux: DisplayPort AUX channel + * @dpcd: DisplayPort configuration data + * + * Returns true if vsc sdp is supported, else returns false + */ +bool drm_dp_vsc_sdp_supported(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + u8 rx_feature; + + if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_13) + return false; + + if (drm_dp_dpcd_readb(aux, DP_DPRX_FEATURE_ENUMERATION_LIST, &rx_feature) != 1) { + drm_dbg_dp(aux->drm_dev, "failed to read DP_DPRX_FEATURE_ENUMERATION_LIST\n"); + return false; + } + + return (rx_feature & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED); +} +EXPORT_SYMBOL(drm_dp_vsc_sdp_supported); + /** * drm_dp_vsc_sdp_pack() - pack a given vsc sdp into generic dp_sdp * @vsc: vsc sdp initialized according to its purpose as defined in diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 1f41994796d3..0c1a4021e098 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -100,6 +100,8 @@ struct drm_dp_vsc_sdp { void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc); +bool drm_dp_vsc_sdp_supported(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); + int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]); static inline int -- cgit v1.2.3 From 1e59ab501abac4fd664de143485be99b341bc78f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:43 +0200 Subject: drm/dp: Add drm_dp_max_dprx_data_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy intel_dp_max_data_rate() to DRM core. It will be needed by a follow-up DP tunnel patch, checking the maximum rate the DPRX (sink) supports. Accordingly use the drm_dp_max_dprx_data_rate() name for clarity. This patchset will also switch calling the new DRM function in i915 instead of intel_dp_max_data_rate(). While at it simplify the function documentation/comments, removing parts described already by drm_dp_bw_channel_coding_efficiency(). v2: (Ville) - Remove max_link_rate_kbps. - Simplify the function documentation. v3: - Rebased on latest drm-tip. Cc: Ville Syrjälä Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-1-imre.deak@intel.com --- drivers/gpu/drm/display/drm_dp_helper.c | 30 ++++++++++++++++++++++++++++++ include/drm/display/drm_dp_helper.h | 1 + 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index d72b6f9a352c..d9f8581a5a5f 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -4059,3 +4059,33 @@ int drm_dp_bw_channel_coding_efficiency(bool is_uhbr) return 800000; } EXPORT_SYMBOL(drm_dp_bw_channel_coding_efficiency); + +/** + * drm_dp_max_dprx_data_rate - Get the max data bandwidth of a DPRX sink + * @max_link_rate: max DPRX link rate in 10kbps units + * @max_lanes: max DPRX lane count + * + * Given a link rate and lanes, get the data bandwidth. + * + * Data bandwidth is the actual payload rate, which depends on the data + * bandwidth efficiency and the link rate. + * + * Note that protocol layers above the DPRX link level considered here can + * further limit the maximum data rate. Such layers are the MST topology (with + * limits on the link between the source and first branch device as well as on + * the whole MST path until the DPRX link) and (Thunderbolt) DP tunnels - + * which in turn can encapsulate an MST link with its own limit - with each + * SST or MST encapsulated tunnel sharing the BW of a tunnel group. + * + * Returns the maximum data rate in kBps units. + */ +int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes) +{ + int ch_coding_efficiency = + drm_dp_bw_channel_coding_efficiency(drm_dp_is_uhbr_rate(max_link_rate)); + + return DIV_ROUND_DOWN_ULL(mul_u32_u32(max_link_rate * 10 * max_lanes, + ch_coding_efficiency), + 1000000 * 8); +} +EXPORT_SYMBOL(drm_dp_max_dprx_data_rate); diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 863b2e7add29..aff7f02971db 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -812,5 +812,6 @@ int drm_dp_bw_overhead(int lane_count, int hactive, int dsc_slice_count, int bpp_x16, unsigned long flags); int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); +int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes); #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3 From 295654f7e554a9f089bdab0b2bb9a9aad7c402c0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:44 +0200 Subject: drm/dp: Add support for DP tunneling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Display Port tunneling. For now this includes the support for Bandwidth Allocation Mode (BWA), leaving adding Panel Replay support for later. BWA allows using displays that share the same (Thunderbolt) link with their maximum resolution. Atm, this may not be possible due to the coarse granularity of partitioning the link BW among the displays on the link: the BW allocation policy is in a SW/FW/HW component on the link (on Thunderbolt it's the SW or FW Connection Manager), independent of the driver. This policy will set the DPRX maximum rate and lane count DPCD registers the GFX driver will see (0x00000, 0x00001, 0x02200, 0x02201) based on the available link BW. The granularity of the current BW allocation policy is coarse, based on the required link rate in the 1.62Gbs..8.1Gbps range and it may prevent using higher resolutions all together: the display connected first will get a share of the link BW which corresponds to its full DPRX capability (regardless of the actual mode it uses). A subsequent display connected will only get the remaining BW, which could be well below its full capability. BWA solves the above coarse granularity (reducing it to a 250Mbs..1Gps range) and first-come/first-served issues by letting the driver request the BW for each display on a link which reflects the actual modes the displays use. This patch adds the DRM core helper functions, while a follow-up change in the patchset takes them into use in the i915 driver. v2: - Fix prepare_to_wait vs. wake-up cond check order in allocate_tunnel_bw(). (Ville) - Move tunnel==NULL checks from callers in drivers to here. (Ville) - Avoid var inits in declaration blocks that can fail or have side-effects. (Ville) - Use u8 for driver and group IDs. (Ville) - Simplify API removing drm_dp_tunnel_get/put_untracked(). (Ville) - Reuse str_yes_no() instead of a local yes_no_chr(). (Ville) - s/drm_dp_tunnel_atomic_clear_state()/free_tunnel_state() and unexport the function. (Ville) - s/clear_tunnel_group_state()/free_group_state() and move kfree() to this function. (Ville) - Add separate group_free_bw() helper and describe what the tunnel estimated BW includes. (Ville) - Improve help text for CONFIG_DRM_DISPLAY_DP_TUNNEL. (Ville) - Add code comment explaining the purpose of DPCD reg read helpers. (Ville) - Add code comment describing the tunnel group name prefix format. (Ville) - Report the allocated BW as undetermined until the first allocation request. - Skip allocation requests matching the previous request. - Clear any stale BW request status flags before a new request. - Add missing error return check of drm_dp_tunnel_atomic_get_group_state() in drm_dp_tunnel_atomic_set_stream_bw(). - Add drm_dp_tunnel_get_allocated_bw(). - s/drm_dp_tunnel_atomic_get_tunnel_bw/drm_dp_tunnel_atomic_get_required_bw - Fix return value description in function doc of drm_dp_tunnel_detect(). - Add function documentation to all exported functions. v3: - Improve grouping of fields in drm_dp_tunnel_group struct. (Uma) - Fix validating the BW granularity DPCD reg value. (Uma) - Document return value of check_and_clear_status_change(). (Uma) - Fix resetting drm_dp_tunnel_ref::tunnel in drm_dp_tunnel_ref_put(). (Ville) - Allow for ALLOCATED_BW to change after a BWA enable/disable sequence. Cc: Ville Syrjälä Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-2-imre.deak@intel.com --- drivers/gpu/drm/display/Kconfig | 21 + drivers/gpu/drm/display/Makefile | 2 + drivers/gpu/drm/display/drm_dp_tunnel.c | 1949 +++++++++++++++++++++++++++++++ include/drm/display/drm_dp.h | 60 + include/drm/display/drm_dp_tunnel.h | 248 ++++ 5 files changed, 2280 insertions(+) create mode 100644 drivers/gpu/drm/display/drm_dp_tunnel.c create mode 100644 include/drm/display/drm_dp_tunnel.h (limited to 'include') diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 09712b88a5b8..c0f56888c328 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -17,6 +17,27 @@ config DRM_DISPLAY_DP_HELPER help DRM display helpers for DisplayPort. +config DRM_DISPLAY_DP_TUNNEL + bool + select DRM_DISPLAY_DP_HELPER + help + Enable support for DisplayPort tunnels. This allows drivers to use + DP tunnel features like the Bandwidth Allocation mode to maximize the + BW utilization for display streams on Thunderbolt links. + +config DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + bool "Enable debugging the DP tunnel state" + depends on REF_TRACKER + depends on DRM_DISPLAY_DP_TUNNEL + depends on DEBUG_KERNEL + depends on EXPERT + help + Enables debugging the DP tunnel manager's state, including the + consistency of all managed tunnels' reference counting and the state of + streams contained in tunnels. + + If in doubt, say "N". + config DRM_DISPLAY_HDCP_HELPER bool depends on DRM_DISPLAY_HELPER diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile index 17ac4a1006a8..7ca61333c669 100644 --- a/drivers/gpu/drm/display/Makefile +++ b/drivers/gpu/drm/display/Makefile @@ -8,6 +8,8 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_HELPER) += \ drm_dp_helper.o \ drm_dp_mst_topology.o \ drm_dsc_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_TUNNEL) += \ + drm_dp_tunnel.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \ drm_hdmi_helper.o \ diff --git a/drivers/gpu/drm/display/drm_dp_tunnel.c b/drivers/gpu/drm/display/drm_dp_tunnel.c new file mode 100644 index 000000000000..120e0de674c1 --- /dev/null +++ b/drivers/gpu/drm/display/drm_dp_tunnel.c @@ -0,0 +1,1949 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define to_group(__private_obj) \ + container_of(__private_obj, struct drm_dp_tunnel_group, base) + +#define to_group_state(__private_state) \ + container_of(__private_state, struct drm_dp_tunnel_group_state, base) + +#define is_dp_tunnel_private_obj(__obj) \ + ((__obj)->funcs == &tunnel_group_funcs) + +#define for_each_new_group_in_state(__state, __new_group_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_private_objs; \ + (__i)++) \ + for_each_if ((__state)->private_objs[__i].ptr && \ + is_dp_tunnel_private_obj((__state)->private_objs[__i].ptr) && \ + ((__new_group_state) = \ + to_group_state((__state)->private_objs[__i].new_state), 1)) + +#define for_each_old_group_in_state(__state, __old_group_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_private_objs; \ + (__i)++) \ + for_each_if ((__state)->private_objs[__i].ptr && \ + is_dp_tunnel_private_obj((__state)->private_objs[__i].ptr) && \ + ((__old_group_state) = \ + to_group_state((__state)->private_objs[__i].old_state), 1)) + +#define for_each_tunnel_in_group(__group, __tunnel) \ + list_for_each_entry(__tunnel, &(__group)->tunnels, node) + +#define for_each_tunnel_state(__group_state, __tunnel_state) \ + list_for_each_entry(__tunnel_state, &(__group_state)->tunnel_states, node) + +#define for_each_tunnel_state_safe(__group_state, __tunnel_state, __tunnel_state_tmp) \ + list_for_each_entry_safe(__tunnel_state, __tunnel_state_tmp, \ + &(__group_state)->tunnel_states, node) + +#define kbytes_to_mbits(__kbytes) \ + DIV_ROUND_UP((__kbytes) * 8, 1000) + +#define DPTUN_BW_ARG(__bw) ((__bw) < 0 ? (__bw) : kbytes_to_mbits(__bw)) + +#define __tun_prn(__tunnel, __level, __type, __fmt, ...) \ + drm_##__level##__type((__tunnel)->group->mgr->dev, \ + "[DPTUN %s][%s] " __fmt, \ + drm_dp_tunnel_name(__tunnel), \ + (__tunnel)->aux->name, ## \ + __VA_ARGS__) + +#define tun_dbg(__tunnel, __fmt, ...) \ + __tun_prn(__tunnel, dbg, _kms, __fmt, ## __VA_ARGS__) + +#define tun_dbg_stat(__tunnel, __err, __fmt, ...) do { \ + if (__err) \ + __tun_prn(__tunnel, dbg, _kms, __fmt " (Failed, err: %pe)\n", \ + ## __VA_ARGS__, ERR_PTR(__err)); \ + else \ + __tun_prn(__tunnel, dbg, _kms, __fmt " (Ok)\n", \ + ## __VA_ARGS__); \ +} while (0) + +#define tun_dbg_atomic(__tunnel, __fmt, ...) \ + __tun_prn(__tunnel, dbg, _atomic, __fmt, ## __VA_ARGS__) + +#define tun_grp_dbg(__group, __fmt, ...) \ + drm_dbg_kms((__group)->mgr->dev, \ + "[DPTUN %s] " __fmt, \ + drm_dp_tunnel_group_name(__group), ## \ + __VA_ARGS__) + +#define DP_TUNNELING_BASE DP_TUNNELING_OUI + +#define __DPTUN_REG_RANGE(__start, __size) \ + GENMASK_ULL((__start) + (__size) - 1, (__start)) + +#define DPTUN_REG_RANGE(__addr, __size) \ + __DPTUN_REG_RANGE((__addr) - DP_TUNNELING_BASE, (__size)) + +#define DPTUN_REG(__addr) DPTUN_REG_RANGE(__addr, 1) + +#define DPTUN_INFO_REG_MASK ( \ + DPTUN_REG_RANGE(DP_TUNNELING_OUI, DP_TUNNELING_OUI_BYTES) | \ + DPTUN_REG_RANGE(DP_TUNNELING_DEV_ID, DP_TUNNELING_DEV_ID_BYTES) | \ + DPTUN_REG(DP_TUNNELING_HW_REV) | \ + DPTUN_REG(DP_TUNNELING_SW_REV_MAJOR) | \ + DPTUN_REG(DP_TUNNELING_SW_REV_MINOR) | \ + DPTUN_REG(DP_TUNNELING_CAPABILITIES) | \ + DPTUN_REG(DP_IN_ADAPTER_INFO) | \ + DPTUN_REG(DP_USB4_DRIVER_ID) | \ + DPTUN_REG(DP_USB4_DRIVER_BW_CAPABILITY) | \ + DPTUN_REG(DP_IN_ADAPTER_TUNNEL_INFORMATION) | \ + DPTUN_REG(DP_BW_GRANULARITY) | \ + DPTUN_REG(DP_ESTIMATED_BW) | \ + DPTUN_REG(DP_ALLOCATED_BW) | \ + DPTUN_REG(DP_TUNNELING_MAX_LINK_RATE) | \ + DPTUN_REG(DP_TUNNELING_MAX_LANE_COUNT) | \ + DPTUN_REG(DP_DPTX_BW_ALLOCATION_MODE_CONTROL)) + +static const DECLARE_BITMAP(dptun_info_regs, 64) = { + DPTUN_INFO_REG_MASK & -1UL, +#if BITS_PER_LONG == 32 + DPTUN_INFO_REG_MASK >> 32, +#endif +}; + +struct drm_dp_tunnel_regs { + u8 buf[HWEIGHT64(DPTUN_INFO_REG_MASK)]; +}; + +struct drm_dp_tunnel_group; + +struct drm_dp_tunnel { + struct drm_dp_tunnel_group *group; + + struct list_head node; + + struct kref kref; + struct ref_tracker *tracker; + struct drm_dp_aux *aux; + char name[8]; + + int bw_granularity; + int estimated_bw; + int allocated_bw; + + int max_dprx_rate; + u8 max_dprx_lane_count; + + u8 adapter_id; + + bool bw_alloc_supported:1; + bool bw_alloc_enabled:1; + bool has_io_error:1; + bool destroyed:1; +}; + +struct drm_dp_tunnel_group_state; + +struct drm_dp_tunnel_state { + struct drm_dp_tunnel_group_state *group_state; + + struct drm_dp_tunnel_ref tunnel_ref; + + struct list_head node; + + u32 stream_mask; + int *stream_bw; +}; + +struct drm_dp_tunnel_group_state { + struct drm_private_state base; + + struct list_head tunnel_states; +}; + +struct drm_dp_tunnel_group { + struct drm_private_obj base; + struct drm_dp_tunnel_mgr *mgr; + + struct list_head tunnels; + + /* available BW including the allocated_bw of all tunnels in the group */ + int available_bw; + + u8 drv_group_id; + char name[8]; + + bool active:1; +}; + +struct drm_dp_tunnel_mgr { + struct drm_device *dev; + + int group_count; + struct drm_dp_tunnel_group *groups; + wait_queue_head_t bw_req_queue; + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + struct ref_tracker_dir ref_tracker; +#endif +}; + +/* + * The following helpers provide a way to read out the tunneling DPCD + * registers with a minimal amount of AUX transfers (1 transfer per contiguous + * range, as permitted by the 16 byte per transfer AUX limit), not accessing + * other registers to avoid any read side-effects. + */ +static int next_reg_area(int *offset) +{ + *offset = find_next_bit(dptun_info_regs, 64, *offset); + + return find_next_zero_bit(dptun_info_regs, 64, *offset + 1) - *offset; +} + +#define tunnel_reg_ptr(__regs, __address) ({ \ + WARN_ON(!test_bit((__address) - DP_TUNNELING_BASE, dptun_info_regs)); \ + &(__regs)->buf[bitmap_weight(dptun_info_regs, (__address) - DP_TUNNELING_BASE)]; \ +}) + +static int read_tunnel_regs(struct drm_dp_aux *aux, struct drm_dp_tunnel_regs *regs) +{ + int offset = 0; + int len; + + while ((len = next_reg_area(&offset))) { + int address = DP_TUNNELING_BASE + offset; + + if (drm_dp_dpcd_read(aux, address, tunnel_reg_ptr(regs, address), len) < 0) + return -EIO; + + offset += len; + } + + return 0; +} + +static u8 tunnel_reg(const struct drm_dp_tunnel_regs *regs, int address) +{ + return *tunnel_reg_ptr(regs, address); +} + +static u8 tunnel_reg_drv_group_id(const struct drm_dp_tunnel_regs *regs) +{ + u8 drv_id = tunnel_reg(regs, DP_USB4_DRIVER_ID) & DP_USB4_DRIVER_ID_MASK; + u8 group_id = tunnel_reg(regs, DP_IN_ADAPTER_TUNNEL_INFORMATION) & DP_GROUP_ID_MASK; + + if (!group_id) + return 0; + + return (drv_id << DP_GROUP_ID_BITS) | group_id; +} + +/* Return granularity in kB/s units */ +static int tunnel_reg_bw_granularity(const struct drm_dp_tunnel_regs *regs) +{ + int gr = tunnel_reg(regs, DP_BW_GRANULARITY) & DP_BW_GRANULARITY_MASK; + + if (gr > 2) + return -1; + + return (250000 << gr) / 8; +} + +static int tunnel_reg_max_dprx_rate(const struct drm_dp_tunnel_regs *regs) +{ + u8 bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE); + + return drm_dp_bw_code_to_link_rate(bw_code); +} + +static int tunnel_reg_max_dprx_lane_count(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_TUNNELING_MAX_LANE_COUNT) & + DP_TUNNELING_MAX_LANE_COUNT_MASK; +} + +static bool tunnel_reg_bw_alloc_supported(const struct drm_dp_tunnel_regs *regs) +{ + u8 cap_mask = DP_TUNNELING_SUPPORT | DP_IN_BW_ALLOCATION_MODE_SUPPORT; + + if ((tunnel_reg(regs, DP_TUNNELING_CAPABILITIES) & cap_mask) != cap_mask) + return false; + + return tunnel_reg(regs, DP_USB4_DRIVER_BW_CAPABILITY) & + DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT; +} + +static bool tunnel_reg_bw_alloc_enabled(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_DPTX_BW_ALLOCATION_MODE_CONTROL) & + DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE; +} + +static u8 tunnel_group_drv_id(u8 drv_group_id) +{ + return drv_group_id >> DP_GROUP_ID_BITS; +} + +static u8 tunnel_group_id(u8 drv_group_id) +{ + return drv_group_id & DP_GROUP_ID_MASK; +} + +const char *drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->name; +} +EXPORT_SYMBOL(drm_dp_tunnel_name); + +static const char *drm_dp_tunnel_group_name(const struct drm_dp_tunnel_group *group) +{ + return group->name; +} + +static struct drm_dp_tunnel_group * +lookup_or_alloc_group(struct drm_dp_tunnel_mgr *mgr, u8 drv_group_id) +{ + struct drm_dp_tunnel_group *group = NULL; + int i; + + for (i = 0; i < mgr->group_count; i++) { + /* + * A tunnel group with 0 group ID shouldn't have more than one + * tunnels. + */ + if (tunnel_group_id(drv_group_id) && + mgr->groups[i].drv_group_id == drv_group_id) + return &mgr->groups[i]; + + if (!group && !mgr->groups[i].active) + group = &mgr->groups[i]; + } + + if (!group) { + drm_dbg_kms(mgr->dev, + "DPTUN: Can't allocate more tunnel groups\n"); + return NULL; + } + + group->drv_group_id = drv_group_id; + group->active = true; + + /* + * The group name format here and elsewhere: Driver-ID:Group-ID:* + * (* standing for all DP-Adapters/tunnels in the group). + */ + snprintf(group->name, sizeof(group->name), "%d:%d:*", + tunnel_group_drv_id(drv_group_id) & ((1 << DP_GROUP_ID_BITS) - 1), + tunnel_group_id(drv_group_id) & ((1 << DP_USB4_DRIVER_ID_BITS) - 1)); + + return group; +} + +static void free_group(struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel_mgr *mgr = group->mgr; + + if (drm_WARN_ON(mgr->dev, !list_empty(&group->tunnels))) + return; + + group->drv_group_id = 0; + group->available_bw = -1; + group->active = false; +} + +static struct drm_dp_tunnel * +tunnel_get(struct drm_dp_tunnel *tunnel) +{ + kref_get(&tunnel->kref); + + return tunnel; +} + +static void free_tunnel(struct kref *kref) +{ + struct drm_dp_tunnel *tunnel = container_of(kref, typeof(*tunnel), kref); + struct drm_dp_tunnel_group *group = tunnel->group; + + list_del(&tunnel->node); + if (list_empty(&group->tunnels)) + free_group(group); + + kfree(tunnel); +} + +static void tunnel_put(struct drm_dp_tunnel *tunnel) +{ + kref_put(&tunnel->kref, free_tunnel); +} + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE +static void track_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + ref_tracker_alloc(&tunnel->group->mgr->ref_tracker, + tracker, GFP_KERNEL); +} + +static void untrack_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + ref_tracker_free(&tunnel->group->mgr->ref_tracker, + tracker); +} +#else +static void track_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ +} + +static void untrack_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ +} +#endif + +/** + * drm_dp_tunnel_get - Get a reference for a DP tunnel + * @tunnel: Tunnel object + * @tracker: Debug tracker for the reference + * + * Get a reference for @tunnel, along with a debug tracker to help locating + * the source of a reference leak/double reference put etc. issue. + * + * The reference must be dropped after use calling drm_dp_tunnel_put() + * passing @tunnel and *@tracker returned from here. + * + * Returns @tunnel - as a convenience - along with *@tracker. + */ +struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + track_tunnel_ref(tunnel, tracker); + + return tunnel_get(tunnel); +} +EXPORT_SYMBOL(drm_dp_tunnel_get); + +/** + * drm_dp_tunnel_put - Put a reference for a DP tunnel + * @tunnel - Tunnel object + * @tracker - Debug tracker for the reference + * + * Put a reference for @tunnel along with its debug *@tracker, which + * was obtained with drm_dp_tunnel_get(). + */ +void drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + untrack_tunnel_ref(tunnel, tracker); + + tunnel_put(tunnel); +} +EXPORT_SYMBOL(drm_dp_tunnel_put); + +static bool add_tunnel_to_group(struct drm_dp_tunnel_mgr *mgr, + u8 drv_group_id, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group *group; + + group = lookup_or_alloc_group(mgr, drv_group_id); + if (!group) + return false; + + tunnel->group = group; + list_add(&tunnel->node, &group->tunnels); + + return true; +} + +static struct drm_dp_tunnel * +create_tunnel(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux, + const struct drm_dp_tunnel_regs *regs) +{ + u8 drv_group_id = tunnel_reg_drv_group_id(regs); + struct drm_dp_tunnel *tunnel; + + tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL); + if (!tunnel) + return NULL; + + INIT_LIST_HEAD(&tunnel->node); + + kref_init(&tunnel->kref); + + tunnel->aux = aux; + + tunnel->adapter_id = tunnel_reg(regs, DP_IN_ADAPTER_INFO) & DP_IN_ADAPTER_NUMBER_MASK; + + snprintf(tunnel->name, sizeof(tunnel->name), "%d:%d:%d", + tunnel_group_drv_id(drv_group_id) & ((1 << DP_GROUP_ID_BITS) - 1), + tunnel_group_id(drv_group_id) & ((1 << DP_USB4_DRIVER_ID_BITS) - 1), + tunnel->adapter_id & ((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1)); + + tunnel->bw_granularity = tunnel_reg_bw_granularity(regs); + tunnel->allocated_bw = tunnel_reg(regs, DP_ALLOCATED_BW) * + tunnel->bw_granularity; + /* + * An initial allocated BW of 0 indicates an undefined state: the + * actual allocation is determined by the TBT CM, usually following a + * legacy allocation policy (based on the max DPRX caps). From the + * driver's POV the state becomes defined only after the first + * allocation request. + */ + if (!tunnel->allocated_bw) + tunnel->allocated_bw = -1; + + tunnel->bw_alloc_supported = tunnel_reg_bw_alloc_supported(regs); + tunnel->bw_alloc_enabled = tunnel_reg_bw_alloc_enabled(regs); + + if (!add_tunnel_to_group(mgr, drv_group_id, tunnel)) { + kfree(tunnel); + + return NULL; + } + + track_tunnel_ref(tunnel, &tunnel->tracker); + + return tunnel; +} + +static void destroy_tunnel(struct drm_dp_tunnel *tunnel) +{ + untrack_tunnel_ref(tunnel, &tunnel->tracker); + tunnel_put(tunnel); +} + +/** + * drm_dp_tunnel_set_io_error - Set the IO error flag for a DP tunnel + * @tunnel: Tunnel object + * + * Set the IO error flag for @tunnel. Drivers can call this function upon + * detecting a failure that affects the tunnel functionality, for instance + * after a DP AUX transfer failure on the port @tunnel is connected to. + * + * This disables further management of @tunnel, including any related + * AUX accesses for tunneling DPCD registers, returning error to the + * initiators of these. The driver is supposed to drop this tunnel and - + * optionally - recreate it. + */ +void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel) +{ + tunnel->has_io_error = true; +} +EXPORT_SYMBOL(drm_dp_tunnel_set_io_error); + +#define SKIP_DPRX_CAPS_CHECK BIT(0) +#define ALLOW_ALLOCATED_BW_CHANGE BIT(1) +static bool tunnel_regs_are_valid(struct drm_dp_tunnel_mgr *mgr, + const struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + u8 drv_group_id = tunnel_reg_drv_group_id(regs); + bool check_dprx = !(flags & SKIP_DPRX_CAPS_CHECK); + bool ret = true; + + if (!tunnel_reg_bw_alloc_supported(regs)) { + if (tunnel_group_id(drv_group_id)) { + drm_dbg_kms(mgr->dev, + "DPTUN: A non-zero group ID is only allowed with BWA support\n"); + ret = false; + } + + if (tunnel_reg(regs, DP_ALLOCATED_BW)) { + drm_dbg_kms(mgr->dev, + "DPTUN: BW is allocated without BWA support\n"); + ret = false; + } + + return ret; + } + + if (!tunnel_group_id(drv_group_id)) { + drm_dbg_kms(mgr->dev, + "DPTUN: BWA support requires a non-zero group ID\n"); + ret = false; + } + + if (check_dprx && hweight8(tunnel_reg_max_dprx_lane_count(regs)) != 1) { + drm_dbg_kms(mgr->dev, + "DPTUN: Invalid DPRX lane count: %d\n", + tunnel_reg_max_dprx_lane_count(regs)); + + ret = false; + } + + if (check_dprx && !tunnel_reg_max_dprx_rate(regs)) { + drm_dbg_kms(mgr->dev, + "DPTUN: DPRX rate is 0\n"); + + ret = false; + } + + if (tunnel_reg_bw_granularity(regs) < 0) { + drm_dbg_kms(mgr->dev, + "DPTUN: Invalid BW granularity\n"); + + ret = false; + } + + if (tunnel_reg(regs, DP_ALLOCATED_BW) > tunnel_reg(regs, DP_ESTIMATED_BW)) { + drm_dbg_kms(mgr->dev, + "DPTUN: Allocated BW %d > estimated BW %d Mb/s\n", + DPTUN_BW_ARG(tunnel_reg(regs, DP_ALLOCATED_BW) * + tunnel_reg_bw_granularity(regs)), + DPTUN_BW_ARG(tunnel_reg(regs, DP_ESTIMATED_BW) * + tunnel_reg_bw_granularity(regs))); + + ret = false; + } + + return ret; +} + +static int tunnel_allocated_bw(const struct drm_dp_tunnel *tunnel) +{ + return max(tunnel->allocated_bw, 0); +} + +static bool tunnel_info_changes_are_valid(struct drm_dp_tunnel *tunnel, + const struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + u8 new_drv_group_id = tunnel_reg_drv_group_id(regs); + bool ret = true; + + if (tunnel->bw_alloc_supported != tunnel_reg_bw_alloc_supported(regs)) { + tun_dbg(tunnel, + "BW alloc support has changed %s -> %s\n", + str_yes_no(tunnel->bw_alloc_supported), + str_yes_no(tunnel_reg_bw_alloc_supported(regs))); + + ret = false; + } + + if (tunnel->group->drv_group_id != new_drv_group_id) { + tun_dbg(tunnel, + "Driver/group ID has changed %d:%d:* -> %d:%d:*\n", + tunnel_group_drv_id(tunnel->group->drv_group_id), + tunnel_group_id(tunnel->group->drv_group_id), + tunnel_group_drv_id(new_drv_group_id), + tunnel_group_id(new_drv_group_id)); + + ret = false; + } + + if (!tunnel->bw_alloc_supported) + return ret; + + if (tunnel->bw_granularity != tunnel_reg_bw_granularity(regs)) { + tun_dbg(tunnel, + "BW granularity has changed: %d -> %d Mb/s\n", + DPTUN_BW_ARG(tunnel->bw_granularity), + DPTUN_BW_ARG(tunnel_reg_bw_granularity(regs))); + + ret = false; + } + + /* + * On some devices at least the BW alloc mode enabled status is always + * reported as 0, so skip checking that here. + */ + + if (!(flags & ALLOW_ALLOCATED_BW_CHANGE) && + tunnel_allocated_bw(tunnel) != + tunnel_reg(regs, DP_ALLOCATED_BW) * tunnel->bw_granularity) { + tun_dbg(tunnel, + "Allocated BW has changed: %d -> %d Mb/s\n", + DPTUN_BW_ARG(tunnel->allocated_bw), + DPTUN_BW_ARG(tunnel_reg(regs, DP_ALLOCATED_BW) * tunnel->bw_granularity)); + + ret = false; + } + + return ret; +} + +static int +read_and_verify_tunnel_regs(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + int err; + + err = read_tunnel_regs(tunnel->aux, regs); + if (err < 0) { + drm_dp_tunnel_set_io_error(tunnel); + + return err; + } + + if (!tunnel_regs_are_valid(tunnel->group->mgr, regs, flags)) + return -EINVAL; + + if (!tunnel_info_changes_are_valid(tunnel, regs, flags)) + return -EINVAL; + + return 0; +} + +static bool update_dprx_caps(struct drm_dp_tunnel *tunnel, const struct drm_dp_tunnel_regs *regs) +{ + bool changed = false; + + if (tunnel_reg_max_dprx_rate(regs) != tunnel->max_dprx_rate) { + tunnel->max_dprx_rate = tunnel_reg_max_dprx_rate(regs); + changed = true; + } + + if (tunnel_reg_max_dprx_lane_count(regs) != tunnel->max_dprx_lane_count) { + tunnel->max_dprx_lane_count = tunnel_reg_max_dprx_lane_count(regs); + changed = true; + } + + return changed; +} + +static int dev_id_len(const u8 *dev_id, int max_len) +{ + while (max_len && dev_id[max_len - 1] == '\0') + max_len--; + + return max_len; +} + +static int get_max_dprx_bw(const struct drm_dp_tunnel *tunnel) +{ + int max_dprx_bw = drm_dp_max_dprx_data_rate(tunnel->max_dprx_rate, + tunnel->max_dprx_lane_count); + + /* + * A BW request of roundup(max_dprx_bw, tunnel->bw_granularity) results in + * an allocation of max_dprx_bw. A BW request above this rounded-up + * value will fail. + */ + return min(roundup(max_dprx_bw, tunnel->bw_granularity), + MAX_DP_REQUEST_BW * tunnel->bw_granularity); +} + +static int get_max_tunnel_bw(const struct drm_dp_tunnel *tunnel) +{ + return min(get_max_dprx_bw(tunnel), tunnel->group->available_bw); +} + +/** + * drm_dp_tunnel_detect - Detect DP tunnel on the link + * @mgr: Tunnel manager + * @aux: DP AUX on which the tunnel will be detected + * + * Detect if there is any DP tunnel on the link and add it to the tunnel + * group's tunnel list. + * + * Returns a pointer to a tunnel on success, or an ERR_PTR() error on + * failure. + */ +struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + struct drm_dp_tunnel_regs regs; + struct drm_dp_tunnel *tunnel; + int err; + + err = read_tunnel_regs(aux, ®s); + if (err) + return ERR_PTR(err); + + if (!(tunnel_reg(®s, DP_TUNNELING_CAPABILITIES) & + DP_TUNNELING_SUPPORT)) + return ERR_PTR(-ENODEV); + + /* The DPRX caps are valid only after enabling BW alloc mode. */ + if (!tunnel_regs_are_valid(mgr, ®s, SKIP_DPRX_CAPS_CHECK)) + return ERR_PTR(-EINVAL); + + tunnel = create_tunnel(mgr, aux, ®s); + if (!tunnel) + return ERR_PTR(-ENOMEM); + + tun_dbg(tunnel, + "OUI:%*phD DevID:%*pE Rev-HW:%d.%d SW:%d.%d PR-Sup:%s BWA-Sup:%s BWA-En:%s\n", + DP_TUNNELING_OUI_BYTES, + tunnel_reg_ptr(®s, DP_TUNNELING_OUI), + dev_id_len(tunnel_reg_ptr(®s, DP_TUNNELING_DEV_ID), DP_TUNNELING_DEV_ID_BYTES), + tunnel_reg_ptr(®s, DP_TUNNELING_DEV_ID), + (tunnel_reg(®s, DP_TUNNELING_HW_REV) & DP_TUNNELING_HW_REV_MAJOR_MASK) >> + DP_TUNNELING_HW_REV_MAJOR_SHIFT, + (tunnel_reg(®s, DP_TUNNELING_HW_REV) & DP_TUNNELING_HW_REV_MINOR_MASK) >> + DP_TUNNELING_HW_REV_MINOR_SHIFT, + tunnel_reg(®s, DP_TUNNELING_SW_REV_MAJOR), + tunnel_reg(®s, DP_TUNNELING_SW_REV_MINOR), + str_yes_no(tunnel_reg(®s, DP_TUNNELING_CAPABILITIES) & + DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT), + str_yes_no(tunnel->bw_alloc_supported), + str_yes_no(tunnel->bw_alloc_enabled)); + + return tunnel; +} +EXPORT_SYMBOL(drm_dp_tunnel_detect); + +/** + * drm_dp_tunnel_destroy - Destroy tunnel object + * @tunnel: Tunnel object + * + * Remove the tunnel from the tunnel topology and destroy it. + * + * Returns 0 on success, -ENODEV if the tunnel has been destroyed already. + */ +int drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel) +{ + if (!tunnel) + return 0; + + if (drm_WARN_ON(tunnel->group->mgr->dev, tunnel->destroyed)) + return -ENODEV; + + tun_dbg(tunnel, "destroying\n"); + + tunnel->destroyed = true; + destroy_tunnel(tunnel); + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_destroy); + +static int check_tunnel(const struct drm_dp_tunnel *tunnel) +{ + if (tunnel->destroyed) + return -ENODEV; + + if (tunnel->has_io_error) + return -EIO; + + return 0; +} + +static int group_allocated_bw(struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel *tunnel; + int group_allocated_bw = 0; + + for_each_tunnel_in_group(group, tunnel) { + if (check_tunnel(tunnel) == 0 && + tunnel->bw_alloc_enabled) + group_allocated_bw += tunnel_allocated_bw(tunnel); + } + + return group_allocated_bw; +} + +/* + * The estimated BW reported by the TBT Connection Manager for each tunnel in + * a group includes the BW already allocated for the given tunnel and the + * unallocated BW which is free to be used by any tunnel in the group. + */ +static int group_free_bw(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->estimated_bw - tunnel_allocated_bw(tunnel); +} + +static int calc_group_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return group_allocated_bw(tunnel->group) + + group_free_bw(tunnel); +} + +static int update_group_available_bw(struct drm_dp_tunnel *tunnel, + const struct drm_dp_tunnel_regs *regs) +{ + struct drm_dp_tunnel *tunnel_iter; + int group_available_bw; + bool changed; + + tunnel->estimated_bw = tunnel_reg(regs, DP_ESTIMATED_BW) * tunnel->bw_granularity; + + if (calc_group_available_bw(tunnel) == tunnel->group->available_bw) + return 0; + + for_each_tunnel_in_group(tunnel->group, tunnel_iter) { + int err; + + if (tunnel_iter == tunnel) + continue; + + if (check_tunnel(tunnel_iter) != 0 || + !tunnel_iter->bw_alloc_enabled) + continue; + + err = drm_dp_dpcd_probe(tunnel_iter->aux, DP_DPCD_REV); + if (err) { + tun_dbg(tunnel_iter, + "Probe failed, assume disconnected (err %pe)\n", + ERR_PTR(err)); + drm_dp_tunnel_set_io_error(tunnel_iter); + } + } + + group_available_bw = calc_group_available_bw(tunnel); + + tun_dbg(tunnel, "Updated group available BW: %d->%d\n", + DPTUN_BW_ARG(tunnel->group->available_bw), + DPTUN_BW_ARG(group_available_bw)); + + changed = tunnel->group->available_bw != group_available_bw; + + tunnel->group->available_bw = group_available_bw; + + return changed ? 1 : 0; +} + +static int set_bw_alloc_mode(struct drm_dp_tunnel *tunnel, bool enable) +{ + u8 mask = DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE | DP_UNMASK_BW_ALLOCATION_IRQ; + u8 val; + + if (drm_dp_dpcd_readb(tunnel->aux, DP_DPTX_BW_ALLOCATION_MODE_CONTROL, &val) < 0) + goto out_err; + + if (enable) + val |= mask; + else + val &= ~mask; + + if (drm_dp_dpcd_writeb(tunnel->aux, DP_DPTX_BW_ALLOCATION_MODE_CONTROL, val) < 0) + goto out_err; + + tunnel->bw_alloc_enabled = enable; + + return 0; + +out_err: + drm_dp_tunnel_set_io_error(tunnel); + + return -EIO; +} + +/** + * drm_dp_tunnel_enable_bw_alloc - Enable DP tunnel BW allocation mode + * @tunnel: Tunnel object + * + * Enable the DP tunnel BW allocation mode on @tunnel if it supports it. + * + * Returns 0 in case of success, negative error code otherwise. + */ +int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_regs regs; + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + if (!tunnel->bw_alloc_supported) + return -EOPNOTSUPP; + + if (!tunnel_group_id(tunnel->group->drv_group_id)) + return -EINVAL; + + err = set_bw_alloc_mode(tunnel, true); + if (err) + goto out; + + /* + * After a BWA disable/re-enable sequence the allocated BW can either + * stay at its last requested value or, for instance after system + * suspend/resume, TBT CM can reset back the allocation to the amount + * allocated in the legacy/non-BWA mode. Accordingly allow for the + * allocation to change wrt. the last SW state. + */ + err = read_and_verify_tunnel_regs(tunnel, ®s, + ALLOW_ALLOCATED_BW_CHANGE); + if (err) { + set_bw_alloc_mode(tunnel, false); + + goto out; + } + + if (!tunnel->max_dprx_rate) + update_dprx_caps(tunnel, ®s); + + if (tunnel->group->available_bw == -1) { + err = update_group_available_bw(tunnel, ®s); + if (err > 0) + err = 0; + } +out: + tun_dbg_stat(tunnel, err, + "Enabling BW alloc mode: DPRX:%dx%d Group alloc:%d/%d Mb/s", + tunnel->max_dprx_rate / 100, tunnel->max_dprx_lane_count, + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + return err; +} +EXPORT_SYMBOL(drm_dp_tunnel_enable_bw_alloc); + +/** + * drm_dp_tunnel_disable_bw_alloc - Disable DP tunnel BW allocation mode + * @tunnel: Tunnel object + * + * Disable the DP tunnel BW allocation mode on @tunnel. + * + * Returns 0 in case of success, negative error code otherwise. + */ +int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + tunnel->allocated_bw = -1; + + err = set_bw_alloc_mode(tunnel, false); + + tun_dbg_stat(tunnel, err, "Disabling BW alloc mode"); + + return err; +} +EXPORT_SYMBOL(drm_dp_tunnel_disable_bw_alloc); + +/** + * drm_dp_tunnel_bw_alloc_is_enabled - Query the BW allocation mode enabled state + * @tunnel: Tunnel object + * + * Query if the BW allocation mode is enabled for @tunnel. + * + * Returns %true if the BW allocation mode is enabled for @tunnel. + */ +bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel) +{ + return tunnel && tunnel->bw_alloc_enabled; +} +EXPORT_SYMBOL(drm_dp_tunnel_bw_alloc_is_enabled); + +static int clear_bw_req_state(struct drm_dp_aux *aux) +{ + u8 bw_req_mask = DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED; + + if (drm_dp_dpcd_writeb(aux, DP_TUNNELING_STATUS, bw_req_mask) < 0) + return -EIO; + + return 0; +} + +static int bw_req_complete(struct drm_dp_aux *aux, bool *status_changed) +{ + u8 bw_req_mask = DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED; + u8 status_change_mask = DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED; + u8 val; + int err; + + if (drm_dp_dpcd_readb(aux, DP_TUNNELING_STATUS, &val) < 0) + return -EIO; + + *status_changed = val & status_change_mask; + + val &= bw_req_mask; + + if (!val) + return -EAGAIN; + + err = clear_bw_req_state(aux); + if (err < 0) + return err; + + return val == DP_BW_REQUEST_SUCCEEDED ? 0 : -ENOSPC; +} + +static int allocate_tunnel_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + struct drm_dp_tunnel_mgr *mgr = tunnel->group->mgr; + int request_bw = DIV_ROUND_UP(bw, tunnel->bw_granularity); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + long timeout; + int err; + + if (bw < 0) { + err = -EINVAL; + goto out; + } + + if (request_bw * tunnel->bw_granularity == tunnel->allocated_bw) + return 0; + + /* Atomic check should prevent the following. */ + if (drm_WARN_ON(mgr->dev, request_bw > MAX_DP_REQUEST_BW)) { + err = -EINVAL; + goto out; + } + + err = clear_bw_req_state(tunnel->aux); + if (err) + goto out; + + if (drm_dp_dpcd_writeb(tunnel->aux, DP_REQUEST_BW, request_bw) < 0) { + err = -EIO; + goto out; + } + + timeout = msecs_to_jiffies(3000); + add_wait_queue(&mgr->bw_req_queue, &wait); + + for (;;) { + bool status_changed; + + err = bw_req_complete(tunnel->aux, &status_changed); + if (err != -EAGAIN) + break; + + if (status_changed) { + struct drm_dp_tunnel_regs regs; + + err = read_and_verify_tunnel_regs(tunnel, ®s, + ALLOW_ALLOCATED_BW_CHANGE); + if (err) + break; + } + + if (!timeout) { + err = -ETIMEDOUT; + break; + } + + timeout = wait_woken(&wait, TASK_UNINTERRUPTIBLE, timeout); + }; + + remove_wait_queue(&mgr->bw_req_queue, &wait); + + if (err) + goto out; + + tunnel->allocated_bw = request_bw * tunnel->bw_granularity; + +out: + tun_dbg_stat(tunnel, err, "Allocating %d/%d Mb/s for tunnel: Group alloc:%d/%d Mb/s", + DPTUN_BW_ARG(request_bw * tunnel->bw_granularity), + DPTUN_BW_ARG(get_max_tunnel_bw(tunnel)), + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + if (err == -EIO) + drm_dp_tunnel_set_io_error(tunnel); + + return err; +} + +/** + * drm_dp_tunnel_alloc_bw - Allocate BW for a DP tunnel + * @tunnel: Tunnel object + * @bw: BW in kB/s units + * + * Allocate @bw kB/s for @tunnel. The allocated BW must be freed after use by + * calling this function for the same tunnel setting @bw to 0. + * + * Returns 0 in case of success, a negative error code otherwise. + */ +int drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + return allocate_tunnel_bw(tunnel, bw); +} +EXPORT_SYMBOL(drm_dp_tunnel_alloc_bw); + +/** + * drm_dp_tunnel_atomic_get_allocated_bw - Get the BW allocated for a DP tunnel + * @tunnel: Tunnel object + * + * Get the current BW allocated for @tunnel. After the tunnel is created / + * resumed and the BW allocation mode is enabled for it, the allocation + * becomes determined only after the first allocation request by the driver + * calling drm_dp_tunnel_alloc_bw(). + * + * Return the BW allocated for the tunnel, or -1 if the allocation is + * undetermined. + */ +int drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel) +{ + return tunnel->allocated_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_get_allocated_bw); + +/* + * Return 0 if the status hasn't changed, 1 if the status has changed, a + * negative error code in case of an I/O failure. + */ +static int check_and_clear_status_change(struct drm_dp_tunnel *tunnel) +{ + u8 mask = DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED; + u8 val; + + if (drm_dp_dpcd_readb(tunnel->aux, DP_TUNNELING_STATUS, &val) < 0) + goto out_err; + + val &= mask; + + if (val) { + if (drm_dp_dpcd_writeb(tunnel->aux, DP_TUNNELING_STATUS, val) < 0) + goto out_err; + + return 1; + } + + if (!drm_dp_tunnel_bw_alloc_is_enabled(tunnel)) + return 0; + + /* + * Check for estimated BW changes explicitly to account for lost + * BW change notifications. + */ + if (drm_dp_dpcd_readb(tunnel->aux, DP_ESTIMATED_BW, &val) < 0) + goto out_err; + + if (val * tunnel->bw_granularity != tunnel->estimated_bw) + return 1; + + return 0; + +out_err: + drm_dp_tunnel_set_io_error(tunnel); + + return -EIO; +} + +/** + * drm_dp_tunnel_update_state - Update DP tunnel SW state with the HW state + * @tunnel: Tunnel object + * + * Update the SW state of @tunnel with the HW state. + * + * Returns 0 if the state has not changed, 1 if it has changed and got updated + * successfully and a negative error code otherwise. + */ +int drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_regs regs; + bool changed = false; + int ret; + + ret = check_tunnel(tunnel); + if (ret < 0) + return ret; + + ret = check_and_clear_status_change(tunnel); + if (ret < 0) + goto out; + + if (!ret) + return 0; + + ret = read_and_verify_tunnel_regs(tunnel, ®s, 0); + if (ret) + goto out; + + if (update_dprx_caps(tunnel, ®s)) + changed = true; + + ret = update_group_available_bw(tunnel, ®s); + if (ret == 1) + changed = true; + +out: + tun_dbg_stat(tunnel, ret < 0 ? ret : 0, + "State update: Changed:%s DPRX:%dx%d Tunnel alloc:%d/%d Group alloc:%d/%d Mb/s", + str_yes_no(changed), + tunnel->max_dprx_rate / 100, tunnel->max_dprx_lane_count, + DPTUN_BW_ARG(tunnel->allocated_bw), + DPTUN_BW_ARG(get_max_tunnel_bw(tunnel)), + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + if (ret < 0) + return ret; + + if (changed) + return 1; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_update_state); + +/* + * drm_dp_tunnel_handle_irq - Handle DP tunnel IRQs + * + * Handle any pending DP tunnel IRQs, waking up waiters for a completion + * event. + * + * Returns 1 if the state of the tunnel has changed which requires calling + * drm_dp_tunnel_update_state(), a negative error code in case of a failure, + * 0 otherwise. + */ +int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_aux *aux) +{ + u8 val; + + if (drm_dp_dpcd_readb(aux, DP_TUNNELING_STATUS, &val) < 0) + return -EIO; + + if (val & (DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED)) + wake_up_all(&mgr->bw_req_queue); + + if (val & (DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED)) + return 1; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_handle_irq); + +/** + * drm_dp_tunnel_max_dprx_rate - Query the maximum rate of the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query the maximum link rate of the DPRX connected + * to @tunnel. Note that this rate will not be limited by the BW limit of the + * tunnel, as opposed to the standard and extended DP_MAX_LINK_RATE DPCD + * registers. + * + * Returns the maximum link rate in 10 kbit/s units. + */ +int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->max_dprx_rate; +} +EXPORT_SYMBOL(drm_dp_tunnel_max_dprx_rate); + +/** + * drm_dp_tunnel_max_dprx_lane_count - Query the maximum lane count of the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query the maximum lane count of the DPRX connected + * to @tunnel. Note that this lane count will not be limited by the BW limit of + * the tunnel, as opposed to the standard and extended DP_MAX_LANE_COUNT DPCD + * registers. + * + * Returns the maximum lane count. + */ +int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->max_dprx_lane_count; +} +EXPORT_SYMBOL(drm_dp_tunnel_max_dprx_lane_count); + +/** + * drm_dp_tunnel_available_bw - Query the estimated total available BW of the tunnel + * @tunnel: Tunnel object + * + * This function is used to query the estimated total available BW of the + * tunnel. This includes the currently allocated and free BW for all the + * tunnels in @tunnel's group. The available BW is valid only after the BW + * allocation mode has been enabled for the tunnel and its state got updated + * calling drm_dp_tunnel_update_state(). + * + * Returns the @tunnel group's estimated total available bandwidth in kB/s + * units, or -1 if the available BW isn't valid (the BW allocation mode is + * not enabled or the tunnel's state hasn't been updated). + */ +int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->group->available_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_available_bw); + +static struct drm_dp_tunnel_group_state * +drm_dp_tunnel_atomic_get_group_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + return (struct drm_dp_tunnel_group_state *) + drm_atomic_get_private_obj_state(state, + &tunnel->group->base); +} + +static struct drm_dp_tunnel_state * +add_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + tun_dbg_atomic(tunnel, + "Adding state for tunnel %p to group state %p\n", + tunnel, group_state); + + tunnel_state = kzalloc(sizeof(*tunnel_state), GFP_KERNEL); + if (!tunnel_state) + return NULL; + + tunnel_state->group_state = group_state; + + drm_dp_tunnel_ref_get(tunnel, &tunnel_state->tunnel_ref); + + INIT_LIST_HEAD(&tunnel_state->node); + list_add(&tunnel_state->node, &group_state->tunnel_states); + + return tunnel_state; +} + +static void free_tunnel_state(struct drm_dp_tunnel_state *tunnel_state) +{ + tun_dbg_atomic(tunnel_state->tunnel_ref.tunnel, + "Freeing state for tunnel %p\n", + tunnel_state->tunnel_ref.tunnel); + + list_del(&tunnel_state->node); + + kfree(tunnel_state->stream_bw); + drm_dp_tunnel_ref_put(&tunnel_state->tunnel_ref); + + kfree(tunnel_state); +} + +static void free_group_state(struct drm_dp_tunnel_group_state *group_state) +{ + struct drm_dp_tunnel_state *tunnel_state; + struct drm_dp_tunnel_state *tunnel_state_tmp; + + for_each_tunnel_state_safe(group_state, tunnel_state, tunnel_state_tmp) + free_tunnel_state(tunnel_state); + + kfree(group_state); +} + +static struct drm_dp_tunnel_state * +get_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + for_each_tunnel_state(group_state, tunnel_state) + if (tunnel_state->tunnel_ref.tunnel == tunnel) + return tunnel_state; + + return NULL; +} + +static struct drm_dp_tunnel_state * +get_or_add_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + tunnel_state = get_tunnel_state(group_state, tunnel); + if (tunnel_state) + return tunnel_state; + + return add_tunnel_state(group_state, tunnel); +} + +static struct drm_private_state * +tunnel_group_duplicate_state(struct drm_private_obj *obj) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = kzalloc(sizeof(*group_state), GFP_KERNEL); + if (!group_state) + return NULL; + + INIT_LIST_HEAD(&group_state->tunnel_states); + + __drm_atomic_helper_private_obj_duplicate_state(obj, &group_state->base); + + for_each_tunnel_state(to_group_state(obj->state), tunnel_state) { + struct drm_dp_tunnel_state *new_tunnel_state; + + new_tunnel_state = get_or_add_tunnel_state(group_state, + tunnel_state->tunnel_ref.tunnel); + if (!new_tunnel_state) + goto out_free_state; + + new_tunnel_state->stream_mask = tunnel_state->stream_mask; + new_tunnel_state->stream_bw = kmemdup(tunnel_state->stream_bw, + sizeof(*tunnel_state->stream_bw) * + hweight32(tunnel_state->stream_mask), + GFP_KERNEL); + + if (!new_tunnel_state->stream_bw) + goto out_free_state; + } + + return &group_state->base; + +out_free_state: + free_group_state(group_state); + + return NULL; +} + +static void tunnel_group_destroy_state(struct drm_private_obj *obj, struct drm_private_state *state) +{ + free_group_state(to_group_state(state)); +} + +static const struct drm_private_state_funcs tunnel_group_funcs = { + .atomic_duplicate_state = tunnel_group_duplicate_state, + .atomic_destroy_state = tunnel_group_destroy_state, +}; + +/** + * drm_dp_tunnel_atomic_get_state - get/allocate the new atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the new atomic state for @tunnel, duplicating it from the old tunnel + * state if not yet allocated. + * + * Return the state or an ERR_PTR() error on failure. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(group_state)) + return ERR_CAST(group_state); + + tunnel_state = get_or_add_tunnel_state(group_state, tunnel); + if (!tunnel_state) + return ERR_PTR(-ENOMEM); + + return tunnel_state; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_state); + +/** + * drm_dp_tunnel_atomic_get_old_state - get the old atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the old atomic state for @tunnel. + * + * Return the old state or NULL if the tunnel's atomic state is not in @state. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_old_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *old_group_state; + int i; + + for_each_old_group_in_state(state, old_group_state, i) + if (to_group(old_group_state->base.obj) == tunnel->group) + return get_tunnel_state(old_group_state, tunnel); + + return NULL; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_old_state); + +/** + * drm_dp_tunnel_atomic_get_new_state - get the new atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the new atomic state for @tunnel. + * + * Return the new state or NULL if the tunnel's atomic state is not in @state. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *new_group_state; + int i; + + for_each_new_group_in_state(state, new_group_state, i) + if (to_group(new_group_state->base.obj) == tunnel->group) + return get_tunnel_state(new_group_state, tunnel); + + return NULL; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_new_state); + +static bool init_group(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel_group_state *group_state; + + group_state = kzalloc(sizeof(*group_state), GFP_KERNEL); + if (!group_state) + return false; + + INIT_LIST_HEAD(&group_state->tunnel_states); + + group->mgr = mgr; + group->available_bw = -1; + INIT_LIST_HEAD(&group->tunnels); + + drm_atomic_private_obj_init(mgr->dev, &group->base, &group_state->base, + &tunnel_group_funcs); + + return true; +} + +static void cleanup_group(struct drm_dp_tunnel_group *group) +{ + drm_atomic_private_obj_fini(&group->base); +} + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE +static void check_unique_stream_ids(const struct drm_dp_tunnel_group_state *group_state) +{ + const struct drm_dp_tunnel_state *tunnel_state; + u32 stream_mask = 0; + + for_each_tunnel_state(group_state, tunnel_state) { + drm_WARN(to_group(group_state->base.obj)->mgr->dev, + tunnel_state->stream_mask & stream_mask, + "[DPTUN %s]: conflicting stream IDs %x (IDs in other tunnels %x)\n", + tunnel_state->tunnel_ref.tunnel->name, + tunnel_state->stream_mask, + stream_mask); + + stream_mask |= tunnel_state->stream_mask; + } +} +#else +static void check_unique_stream_ids(const struct drm_dp_tunnel_group_state *group_state) +{ +} +#endif + +static int stream_id_to_idx(u32 stream_mask, u8 stream_id) +{ + return hweight32(stream_mask & (BIT(stream_id) - 1)); +} + +static int resize_bw_array(struct drm_dp_tunnel_state *tunnel_state, + unsigned long old_mask, unsigned long new_mask) +{ + unsigned long move_mask = old_mask & new_mask; + int *new_bws = NULL; + int id; + + WARN_ON(!new_mask); + + if (old_mask == new_mask) + return 0; + + new_bws = kcalloc(hweight32(new_mask), sizeof(*new_bws), GFP_KERNEL); + if (!new_bws) + return -ENOMEM; + + for_each_set_bit(id, &move_mask, BITS_PER_TYPE(move_mask)) + new_bws[stream_id_to_idx(new_mask, id)] = + tunnel_state->stream_bw[stream_id_to_idx(old_mask, id)]; + + kfree(tunnel_state->stream_bw); + tunnel_state->stream_bw = new_bws; + tunnel_state->stream_mask = new_mask; + + return 0; +} + +static int set_stream_bw(struct drm_dp_tunnel_state *tunnel_state, + u8 stream_id, int bw) +{ + int err; + + err = resize_bw_array(tunnel_state, + tunnel_state->stream_mask, + tunnel_state->stream_mask | BIT(stream_id)); + if (err) + return err; + + tunnel_state->stream_bw[stream_id_to_idx(tunnel_state->stream_mask, stream_id)] = bw; + + return 0; +} + +static int clear_stream_bw(struct drm_dp_tunnel_state *tunnel_state, + u8 stream_id) +{ + if (!(tunnel_state->stream_mask & ~BIT(stream_id))) { + free_tunnel_state(tunnel_state); + return 0; + } + + return resize_bw_array(tunnel_state, + tunnel_state->stream_mask, + tunnel_state->stream_mask & ~BIT(stream_id)); +} + +/** + * drm_dp_tunnel_atomic_set_stream_bw - Set the BW for a DP tunnel stream + * @state: Atomic state + * @tunnel: DP tunnel containing the stream + * @stream_id: Stream ID + * @bw: BW of the stream + * + * Set a DP tunnel stream's required BW in the atomic state. + * + * Returns 0 in case of success, a negative error code otherwise. + */ +int drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw) +{ + struct drm_dp_tunnel_group_state *new_group_state; + struct drm_dp_tunnel_state *tunnel_state; + int err; + + if (drm_WARN_ON(tunnel->group->mgr->dev, + stream_id > BITS_PER_TYPE(tunnel_state->stream_mask))) + return -EINVAL; + + tun_dbg(tunnel, + "Setting %d Mb/s for stream %d\n", + DPTUN_BW_ARG(bw), stream_id); + + new_group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(new_group_state)) + return PTR_ERR(new_group_state); + + if (bw == 0) { + tunnel_state = get_tunnel_state(new_group_state, tunnel); + if (!tunnel_state) + return 0; + + return clear_stream_bw(tunnel_state, stream_id); + } + + tunnel_state = get_or_add_tunnel_state(new_group_state, tunnel); + if (drm_WARN_ON(state->dev, !tunnel_state)) + return -EINVAL; + + err = set_stream_bw(tunnel_state, stream_id, bw); + if (err) + return err; + + check_unique_stream_ids(new_group_state); + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_set_stream_bw); + +/** + * drm_dp_tunnel_atomic_get_required_bw - Get the BW required by a DP tunnel + * @tunnel_state: Atomic state of the queried tunnel + * + * Calculate the BW required by a tunnel adding up the required BW of all + * the streams in the tunnel. + * + * Return the total BW required by the tunnel. + */ +int drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state) +{ + int tunnel_bw = 0; + int i; + + if (!tunnel_state || !tunnel_state->stream_mask) + return 0; + + for (i = 0; i < hweight32(tunnel_state->stream_mask); i++) + tunnel_bw += tunnel_state->stream_bw[i]; + + return tunnel_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_required_bw); + +/** + * drm_dp_tunnel_atomic_get_group_streams_in_state - Get mask of stream IDs in a group + * @state: Atomic state + * @tunnel: Tunnel object + * @stream_mask: Mask of streams in @tunnel's group + * + * Get the mask of all the stream IDs in the tunnel group of @tunnel. + * + * Return 0 in case of success - with the stream IDs in @stream_mask - or a + * negative error code in case of failure. + */ +int drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(group_state)) + return PTR_ERR(group_state); + + *stream_mask = 0; + for_each_tunnel_state(group_state, tunnel_state) + *stream_mask |= tunnel_state->stream_mask; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_group_streams_in_state); + +static int +drm_dp_tunnel_atomic_check_group_bw(struct drm_dp_tunnel_group_state *new_group_state, + u32 *failed_stream_mask) +{ + struct drm_dp_tunnel_group *group = to_group(new_group_state->base.obj); + struct drm_dp_tunnel_state *new_tunnel_state; + u32 group_stream_mask = 0; + int group_bw = 0; + + for_each_tunnel_state(new_group_state, new_tunnel_state) { + struct drm_dp_tunnel *tunnel = new_tunnel_state->tunnel_ref.tunnel; + int max_dprx_bw = get_max_dprx_bw(tunnel); + int tunnel_bw = drm_dp_tunnel_atomic_get_required_bw(new_tunnel_state); + + tun_dbg(tunnel, + "%sRequired %d/%d Mb/s total for tunnel.\n", + tunnel_bw > max_dprx_bw ? "Not enough BW: " : "", + DPTUN_BW_ARG(tunnel_bw), + DPTUN_BW_ARG(max_dprx_bw)); + + if (tunnel_bw > max_dprx_bw) { + *failed_stream_mask = new_tunnel_state->stream_mask; + return -ENOSPC; + } + + group_bw += min(roundup(tunnel_bw, tunnel->bw_granularity), + max_dprx_bw); + group_stream_mask |= new_tunnel_state->stream_mask; + } + + tun_grp_dbg(group, + "%sRequired %d/%d Mb/s total for tunnel group.\n", + group_bw > group->available_bw ? "Not enough BW: " : "", + DPTUN_BW_ARG(group_bw), + DPTUN_BW_ARG(group->available_bw)); + + if (group_bw > group->available_bw) { + *failed_stream_mask = group_stream_mask; + return -ENOSPC; + } + + return 0; +} + +/** + * drm_dp_tunnel_atomic_check_stream_bws - Check BW limit for all streams in state + * @state: Atomic state + * @failed_stream_mask: Mask of stream IDs with a BW limit failure + * + * Check the required BW of each DP tunnel in @state against both the DPRX BW + * limit of the tunnel and the BW limit of the tunnel group. Return a mask of + * stream IDs in @failed_stream_mask once a check fails. The mask will contain + * either all the streams in a tunnel (in case a DPRX BW limit check failed) or + * all the streams in a tunnel group (in case a group BW limit check failed). + * + * Return 0 if all the BW limit checks passed, -ENOSPC in case a BW limit + * check failed - with @failed_stream_mask containing the streams failing the + * check - or a negative error code otherwise. + */ +int drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask) +{ + struct drm_dp_tunnel_group_state *new_group_state; + int i; + + for_each_new_group_in_state(state, new_group_state, i) { + int ret; + + ret = drm_dp_tunnel_atomic_check_group_bw(new_group_state, + failed_stream_mask); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_check_stream_bws); + +static void destroy_mgr(struct drm_dp_tunnel_mgr *mgr) +{ + int i; + + for (i = 0; i < mgr->group_count; i++) { + cleanup_group(&mgr->groups[i]); + drm_WARN_ON(mgr->dev, !list_empty(&mgr->groups[i].tunnels)); + } + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + ref_tracker_dir_exit(&mgr->ref_tracker); +#endif + + kfree(mgr->groups); + kfree(mgr); +} + +/** + * drm_dp_tunnel_mgr_create - Create a DP tunnel manager + * @dev: DRM device object + * + * Creates a DP tunnel manager for @dev. + * + * Returns a pointer to the tunnel manager if created successfully or NULL in + * case of an error. + */ +struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count) +{ + struct drm_dp_tunnel_mgr *mgr; + int i; + + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return NULL; + + mgr->dev = dev; + init_waitqueue_head(&mgr->bw_req_queue); + + mgr->groups = kcalloc(max_group_count, sizeof(*mgr->groups), GFP_KERNEL); + if (!mgr->groups) { + kfree(mgr); + + return NULL; + } + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + ref_tracker_dir_init(&mgr->ref_tracker, 16, "dptun"); +#endif + + for (i = 0; i < max_group_count; i++) { + if (!init_group(mgr, &mgr->groups[i])) { + destroy_mgr(mgr); + + return NULL; + } + + mgr->group_count++; + } + + return mgr; +} +EXPORT_SYMBOL(drm_dp_tunnel_mgr_create); + +/** + * drm_dp_tunnel_mgr_destroy - Destroy DP tunnel manager + * @mgr: Tunnel manager object + * + * Destroy the tunnel manager. + */ +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr) +{ + destroy_mgr(mgr); +} +EXPORT_SYMBOL(drm_dp_tunnel_mgr_destroy); diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 281afff6ee4e..8bfd5d007be8 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -1382,6 +1382,66 @@ #define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET 0x69494 #define DP_HDCP_2_2_REG_DBG_OFFSET 0x69518 +/* DP-tunneling */ +#define DP_TUNNELING_OUI 0xe0000 +#define DP_TUNNELING_OUI_BYTES 3 + +#define DP_TUNNELING_DEV_ID 0xe0003 +#define DP_TUNNELING_DEV_ID_BYTES 6 + +#define DP_TUNNELING_HW_REV 0xe0009 +#define DP_TUNNELING_HW_REV_MAJOR_SHIFT 4 +#define DP_TUNNELING_HW_REV_MAJOR_MASK (0xf << DP_TUNNELING_HW_REV_MAJOR_SHIFT) +#define DP_TUNNELING_HW_REV_MINOR_SHIFT 0 +#define DP_TUNNELING_HW_REV_MINOR_MASK (0xf << DP_TUNNELING_HW_REV_MINOR_SHIFT) + +#define DP_TUNNELING_SW_REV_MAJOR 0xe000a +#define DP_TUNNELING_SW_REV_MINOR 0xe000b + +#define DP_TUNNELING_CAPABILITIES 0xe000d +#define DP_IN_BW_ALLOCATION_MODE_SUPPORT (1 << 7) +#define DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT (1 << 6) +#define DP_TUNNELING_SUPPORT (1 << 0) + +#define DP_IN_ADAPTER_INFO 0xe000e +#define DP_IN_ADAPTER_NUMBER_BITS 7 +#define DP_IN_ADAPTER_NUMBER_MASK ((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1) + +#define DP_USB4_DRIVER_ID 0xe000f +#define DP_USB4_DRIVER_ID_BITS 4 +#define DP_USB4_DRIVER_ID_MASK ((1 << DP_USB4_DRIVER_ID_BITS) - 1) + +#define DP_USB4_DRIVER_BW_CAPABILITY 0xe0020 +#define DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT (1 << 7) + +#define DP_IN_ADAPTER_TUNNEL_INFORMATION 0xe0021 +#define DP_GROUP_ID_BITS 3 +#define DP_GROUP_ID_MASK ((1 << DP_GROUP_ID_BITS) - 1) + +#define DP_BW_GRANULARITY 0xe0022 +#define DP_BW_GRANULARITY_MASK 0x3 + +#define DP_ESTIMATED_BW 0xe0023 +#define DP_ALLOCATED_BW 0xe0024 + +#define DP_TUNNELING_STATUS 0xe0025 +#define DP_BW_ALLOCATION_CAPABILITY_CHANGED (1 << 3) +#define DP_ESTIMATED_BW_CHANGED (1 << 2) +#define DP_BW_REQUEST_SUCCEEDED (1 << 1) +#define DP_BW_REQUEST_FAILED (1 << 0) + +#define DP_TUNNELING_MAX_LINK_RATE 0xe0028 + +#define DP_TUNNELING_MAX_LANE_COUNT 0xe0029 +#define DP_TUNNELING_MAX_LANE_COUNT_MASK 0x1f + +#define DP_DPTX_BW_ALLOCATION_MODE_CONTROL 0xe0030 +#define DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE (1 << 7) +#define DP_UNMASK_BW_ALLOCATION_IRQ (1 << 6) + +#define DP_REQUEST_BW 0xe0031 +#define MAX_DP_REQUEST_BW 255 + /* LTTPR: Link Training (LT)-tunable PHY Repeaters */ #define DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV 0xf0000 /* 1.3 */ #define DP_MAX_LINK_RATE_PHY_REPEATER 0xf0001 /* 1.4a */ diff --git a/include/drm/display/drm_dp_tunnel.h b/include/drm/display/drm_dp_tunnel.h new file mode 100644 index 000000000000..87212c847915 --- /dev/null +++ b/include/drm/display/drm_dp_tunnel.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __DRM_DP_TUNNEL_H__ +#define __DRM_DP_TUNNEL_H__ + +#include +#include +#include + +struct drm_dp_aux; + +struct drm_device; + +struct drm_atomic_state; +struct drm_dp_tunnel_mgr; +struct drm_dp_tunnel_state; + +struct ref_tracker; + +struct drm_dp_tunnel_ref { + struct drm_dp_tunnel *tunnel; + struct ref_tracker *tracker; +}; + +#ifdef CONFIG_DRM_DISPLAY_DP_TUNNEL + +struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) +{ + tunnel_ref->tunnel = drm_dp_tunnel_get(tunnel, &tunnel_ref->tracker); +} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) +{ + drm_dp_tunnel_put(tunnel_ref->tunnel, &tunnel_ref->tracker); + tunnel_ref->tunnel = NULL; +} + +struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); +int drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel); +bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw); +int drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel); + +void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); + +int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel); + +const char *drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_old_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw); +int drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask); + +int drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask); + +int drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state); + +struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count); +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr); + +#else + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) +{ + return NULL; +} + +static inline void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) {} + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel) +{ + return false; +} + +static inline int +drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline int +drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel) {} + +static inline int +drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline const char * +drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel) +{ + return NULL; +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state) +{ + return 0; +} + +static inline struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr) {} + +#endif /* CONFIG_DRM_DISPLAY_DP_TUNNEL */ + +#endif /* __DRM_DP_TUNNEL_H__ */ -- cgit v1.2.3 From 6496dbecb9c242cb87c237dbf1a51a89588b20f7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:37 +0200 Subject: drm/i915/dp: Handle DP tunnel IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle DP tunnel IRQs a sink (or rather a BW management component like the Thunderbolt Connection Manager) raises to signal the completion of a BW request by the driver, or to signal any state change related to the link BW. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-18-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 37 +++++++++++++++++++++++++-------- include/drm/display/drm_dp.h | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3b7adaaa7c96..51b24cab51ff 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4904,13 +4904,15 @@ static bool intel_dp_mst_link_status(struct intel_dp *intel_dp) * - %true if pending interrupts were serviced (or no interrupts were * pending) w/o detecting an error condition. * - %false if an error condition - like AUX failure or a loss of link - is - * detected, which needs servicing from the hotplug work. + * detected, or another condition - like a DP tunnel BW state change - needs + * servicing from the hotplug work. */ static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); bool link_ok = true; + bool reprobe_needed = false; drm_WARN_ON_ONCE(&i915->drm, intel_dp->active_mst_links < 0); @@ -4937,6 +4939,13 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) intel_dp_mst_hpd_irq(intel_dp, esi, ack); + if (esi[3] & DP_TUNNELING_IRQ) { + if (drm_dp_tunnel_handle_irq(i915->display.dp_tunnel_mgr, + &intel_dp->aux)) + reprobe_needed = true; + ack[3] |= DP_TUNNELING_IRQ; + } + if (!memchr_inv(ack, 0, sizeof(ack))) break; @@ -4947,7 +4956,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) drm_dp_mst_hpd_irq_send_new_request(&intel_dp->mst_mgr); } - return link_ok; + return link_ok && !reprobe_needed; } static void @@ -5307,23 +5316,32 @@ static void intel_dp_check_device_service_irq(struct intel_dp *intel_dp) drm_dbg_kms(&i915->drm, "Sink specific irq unhandled\n"); } -static void intel_dp_check_link_service_irq(struct intel_dp *intel_dp) +static bool intel_dp_check_link_service_irq(struct intel_dp *intel_dp) { + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + bool reprobe_needed = false; u8 val; if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) - return; + return false; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, &val) != 1 || !val) - return; + return false; + + if ((val & DP_TUNNELING_IRQ) && + drm_dp_tunnel_handle_irq(i915->display.dp_tunnel_mgr, + &intel_dp->aux)) + reprobe_needed = true; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, val) != 1) - return; + return reprobe_needed; if (val & HDMI_LINK_STATUS_CHANGED) intel_dp_handle_hdmi_link_status_change(intel_dp); + + return reprobe_needed; } /* @@ -5344,6 +5362,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); u8 old_sink_count = intel_dp->sink_count; + bool reprobe_needed = false; bool ret; /* @@ -5366,7 +5385,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) } intel_dp_check_device_service_irq(intel_dp); - intel_dp_check_link_service_irq(intel_dp); + reprobe_needed = intel_dp_check_link_service_irq(intel_dp); /* Handle CEC interrupts, if any */ drm_dp_cec_irq(&intel_dp->aux); @@ -5393,10 +5412,10 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) * FIXME get rid of the ad-hoc phy test modeset code * and properly incorporate it into the normal modeset. */ - return false; + reprobe_needed = true; } - return true; + return !reprobe_needed; } /* XXX this is probably wrong for multiple downstream ports */ diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 8bfd5d007be8..4891bd916d26 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -1081,6 +1081,7 @@ # define STREAM_STATUS_CHANGED (1 << 2) # define HDMI_LINK_STATUS_CHANGED (1 << 3) # define CONNECTED_OFF_ENTRY_REQUESTED (1 << 4) +# define DP_TUNNELING_IRQ (1 << 5) #define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) -- cgit v1.2.3 From 11b4eedfc87de394ed8cc54dea87c745d37ff9dc Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:29 +0100 Subject: fbdev: Do not include in header Forward declare struct backlight_device and remove the include statement. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-5-tzimmermann@suse.de --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 2ce2f5c2fca9..7380d959c5d5 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -13,11 +13,11 @@ #include #include #include -#include #include #include +struct backlight_device; struct vm_area_struct; struct fb_info; struct device; -- cgit v1.2.3 From 183c81569ddef5b7cc9b0403a9bdf9090e54c4f2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:30 +0100 Subject: fbdev: Do not include in header Forward declare struct inode and remove the include statement. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-6-tzimmermann@suse.de --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 7380d959c5d5..f269ba520280 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -8,7 +8,6 @@ #define FBIO_CURSOR _IOWR('F', 0x08, struct fb_cursor_user) -#include #include #include #include @@ -22,6 +21,7 @@ struct vm_area_struct; struct fb_info; struct device; struct file; +struct inode; struct videomode; struct device_node; -- cgit v1.2.3 From 0f115335cff5caa53738e0240d6f7f0b85c72e14 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:31 +0100 Subject: fbdev: Do not include in header Forward declare struct notifier_block and remove the include statement. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-7-tzimmermann@suse.de --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index f269ba520280..90f348f14a49 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -22,6 +21,7 @@ struct fb_info; struct device; struct file; struct inode; +struct notifier_block; struct videomode; struct device_node; -- cgit v1.2.3 From 7a46212f2a15cd8f041d2b4243ab64649c394260 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:32 +0100 Subject: fbdev: Do not include in header Forward declare struct page and remove the include statement. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-8-tzimmermann@suse.de --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 90f348f14a49..42155898374b 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -11,7 +11,6 @@ #include #include #include -#include #include @@ -22,6 +21,7 @@ struct device; struct file; struct inode; struct notifier_block; +struct page; struct videomode; struct device_node; -- cgit v1.2.3 From f6d520783a087fad2c5196f1231876312996cf07 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:33 +0100 Subject: fbdev: Clean up forward declarations in header file Add forward declarations for struct i2c_adapter and struct module, and sort the list alphabetically. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-9-tzimmermann@suse.de --- include/linux/fb.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 42155898374b..8f70ca727a30 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -15,15 +15,17 @@ #include struct backlight_device; -struct vm_area_struct; -struct fb_info; struct device; +struct device_node; +struct fb_info; struct file; +struct i2c_adapter; struct inode; +struct module; struct notifier_block; struct page; struct videomode; -struct device_node; +struct vm_area_struct; /* Definitions below are used in the parsed monitor specs */ #define FB_DPMS_ACTIVE_OFF 1 -- cgit v1.2.3 From 0c591381e4462005234f942d9fc36a369c0f5998 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 19 Feb 2024 10:37:34 +0100 Subject: fbdev: Clean up include statements in header file Include mutex.h, printk.h and types.h, remove several unnecessary include statements, and sort the list alphabetically. Signed-off-by: Thomas Zimmermann Reviewed-by: Jani Nikula Acked-by: Helge Deller Link: https://patchwork.freedesktop.org/patch/msgid/20240219093941.3684-10-tzimmermann@suse.de --- include/linux/fb.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 8f70ca727a30..708e6a177b1b 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -2,15 +2,15 @@ #ifndef _LINUX_FB_H #define _LINUX_FB_H -#include -#include #include #define FBIO_CURSOR _IOWR('F', 0x08, struct fb_cursor_user) -#include +#include +#include +#include +#include #include -#include #include -- cgit v1.2.3 From 73984daf07a1a89ace8f0db6dd2d640654ebbbee Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 22 Feb 2024 19:13:47 +0100 Subject: drm/tests: helpers: Include missing drm_drv header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a few functions declared in our kunit helpers header, some of them dereferencing the struct drm_driver. However, we don't include the drm_drv.h header file defining that structure, leading to compilation errors if we don't include both headers. Fixes: d98780310719 ("drm/tests: helpers: Allow to pass a custom drm_driver") Reviewed-by: Maíra Canal Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20240222-kms-hdmi-connector-state-v7-1-8f4af575fce2@kernel.org --- include/drm/drm_kunit_helpers.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index ba483c87f0e7..3ae19892229d 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -3,6 +3,8 @@ #ifndef DRM_KUNIT_HELPERS_H_ #define DRM_KUNIT_HELPERS_H_ +#include + #include #include -- cgit v1.2.3 From 7a48da0febd5113d9de6f51592a09825ebd8415c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 22 Feb 2024 19:13:49 +0100 Subject: drm/tests: Add helper to create mock plane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're going to need a full-blown, functional, KMS device to test more components of the atomic modesetting infrastructure. Let's add a new helper to create a dumb, mocked, primary plane. By default, it will create a linear XRGB8888 plane, using the default helpers. Reviewed-by: Maíra Canal Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20240222-kms-hdmi-connector-state-v7-3-8f4af575fce2@kernel.org --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 85 +++++++++++++++++++++++++++++++ include/drm/drm_kunit_helpers.h | 11 ++++ 2 files changed, 96 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 4fb11b938bc1..32dc8354641a 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -164,5 +165,89 @@ drm_kunit_helper_atomic_state_alloc(struct kunit *test, } EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); +static const uint32_t default_plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const uint64_t default_plane_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const struct drm_plane_helper_funcs default_plane_helper_funcs = { +}; + +static const struct drm_plane_funcs default_plane_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .reset = drm_atomic_helper_plane_reset, +}; + +/** + * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test + * @test: The test context object + * @drm: The device to alloc the plane for + * @funcs: Callbacks for the new plane. Optional. + * @helper_funcs: Helpers callbacks for the new plane. Optional. + * @formats: array of supported formats (DRM_FORMAT\_\*). Optional. + * @num_formats: number of elements in @formats + * @modifiers: array of struct drm_format modifiers terminated by + * DRM_FORMAT_MOD_INVALID. Optional. + * + * This allocates and initializes a mock struct &drm_plane meant to be + * part of a mock device for a KUnit test. + * + * Resources will be cleaned up automatically. + * + * @funcs will default to the default helpers implementations. + * @helper_funcs will default to an empty implementation. @formats will + * default to XRGB8888 only. @modifiers will default to a linear + * modifier only. + * + * Returns: + * A pointer to the new plane, or an ERR_PTR() otherwise. + */ +struct drm_plane * +drm_kunit_helper_create_primary_plane(struct kunit *test, + struct drm_device *drm, + const struct drm_plane_funcs *funcs, + const struct drm_plane_helper_funcs *helper_funcs, + const uint32_t *formats, + unsigned int num_formats, + const uint64_t *modifiers) +{ + struct drm_plane *plane; + + if (!funcs) + funcs = &default_plane_funcs; + + if (!helper_funcs) + helper_funcs = &default_plane_helper_funcs; + + if (!formats || !num_formats) { + formats = default_plane_formats; + num_formats = ARRAY_SIZE(default_plane_formats); + } + + if (!modifiers) + modifiers = default_plane_modifiers; + + plane = __drmm_universal_plane_alloc(drm, + sizeof(struct drm_plane), 0, + 0, + funcs, + formats, + num_formats, + default_plane_modifiers, + DRM_PLANE_TYPE_PRIMARY, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); + + drm_plane_helper_add(plane, helper_funcs); + + return plane; +} +EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane); + MODULE_AUTHOR("Maxime Ripard "); MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 3ae19892229d..38667d624aa8 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -10,6 +10,8 @@ #include struct drm_device; +struct drm_plane_funcs; +struct drm_plane_helper_funcs; struct kunit; struct device *drm_kunit_helper_alloc_device(struct kunit *test); @@ -99,4 +101,13 @@ drm_kunit_helper_atomic_state_alloc(struct kunit *test, struct drm_device *drm, struct drm_modeset_acquire_ctx *ctx); +struct drm_plane * +drm_kunit_helper_create_primary_plane(struct kunit *test, + struct drm_device *drm, + const struct drm_plane_funcs *funcs, + const struct drm_plane_helper_funcs *helper_funcs, + const uint32_t *formats, + unsigned int num_formats, + const uint64_t *modifiers); + #endif // DRM_KUNIT_HELPERS_H_ -- cgit v1.2.3 From 51f90720381dea79208513d059e0eb426dee511e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 22 Feb 2024 19:13:50 +0100 Subject: drm/tests: Add helper to create mock crtc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're going to need a full-blown, functional, KMS device to test more components of the atomic modesetting infrastructure. Let's add a new helper to create a dumb, mocked, CRTC. By default it will create a CRTC relying only on the default helpers, but drivers are free to deviate from that. Reviewed-by: Maíra Canal Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20240222-kms-hdmi-connector-state-v7-4-8f4af575fce2@kernel.org --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 62 +++++++++++++++++++++++++++++++ include/drm/drm_kunit_helpers.h | 10 +++++ 2 files changed, 72 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 32dc8354641a..d5317d13d3fc 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -249,5 +249,67 @@ drm_kunit_helper_create_primary_plane(struct kunit *test, } EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane); +static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = { +}; + +static const struct drm_crtc_funcs default_crtc_funcs = { + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .reset = drm_atomic_helper_crtc_reset, +}; + +/** + * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test + * @test: The test context object + * @drm: The device to alloc the plane for + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC. Optional. + * @funcs: Callbacks for the new plane. Optional. + * @helper_funcs: Helpers callbacks for the new plane. Optional. + * + * This allocates and initializes a mock struct &drm_crtc meant to be + * part of a mock device for a KUnit test. + * + * Resources will be cleaned up automatically. + * + * @funcs will default to the default helpers implementations. + * @helper_funcs will default to an empty implementation. + * + * Returns: + * A pointer to the new CRTC, or an ERR_PTR() otherwise. + */ +struct drm_crtc * +drm_kunit_helper_create_crtc(struct kunit *test, + struct drm_device *drm, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const struct drm_crtc_helper_funcs *helper_funcs) +{ + struct drm_crtc *crtc; + int ret; + + if (!funcs) + funcs = &default_crtc_funcs; + + if (!helper_funcs) + helper_funcs = &default_crtc_helper_funcs; + + crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + ret = drmm_crtc_init_with_planes(drm, crtc, + primary, + cursor, + funcs, + NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_crtc_helper_add(crtc, helper_funcs); + + return crtc; +} +EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc); + MODULE_AUTHOR("Maxime Ripard "); MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 38667d624aa8..6e99627edf45 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -9,6 +9,8 @@ #include +struct drm_crtc_funcs; +struct drm_crtc_helper_funcs; struct drm_device; struct drm_plane_funcs; struct drm_plane_helper_funcs; @@ -110,4 +112,12 @@ drm_kunit_helper_create_primary_plane(struct kunit *test, unsigned int num_formats, const uint64_t *modifiers); +struct drm_crtc * +drm_kunit_helper_create_crtc(struct kunit *test, + struct drm_device *drm, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const struct drm_crtc_helper_funcs *helper_funcs); + #endif // DRM_KUNIT_HELPERS_H_ -- cgit v1.2.3 From 8df1ddb5bf11ab820ad991e164dab82c0960add9 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 2 Feb 2024 14:11:16 -0800 Subject: drm/dp: Don't attempt AUX transfers when eDP panels are not powered If an eDP panel is not powered on then any attempts to talk to it over the DP AUX channel will timeout. Unfortunately these attempts may be quite slow. Userspace can initiate these attempts either via a /dev/drm_dp_auxN device or via the created i2c device. Making the DP AUX drivers timeout faster is a difficult proposition. In theory we could just poll the panel's HPD line in the AUX transfer function and immediately return an error there. However, this is easier said than done. For one thing, there's no hard requirement to hook the HPD line up for eDP panels and it's OK to just delay a fixed amount. For another thing, the HPD line may not be fast to probe. On parade-ps8640 we need to wait for the bridge chip's firmware to boot before we can get the HPD line and this is a slow process. The fact that the transfers are taking so long to timeout is causing real problems. The open source fwupd daemon sometimes scans DP busses looking for devices whose firmware need updating. If it happens to scan while a panel is turned off this scan can take a long time. The fwupd daemon could try to be smarter and only scan when eDP panels are turned on, but we can also improve the behavior in the kernel. Let's let eDP panels drivers specify that a panel is turned off and then modify the common AUX transfer code not to attempt a transfer in this case. Tested-by: Steev Klimaszewski Reviewed-by: Hsin-Yi Wang Tested-by: Eizan Miyamoto Acked-by: Neil Armstrong Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20240202141109.1.I24277520ac754ea538c9b14578edc94e1df11b48@changeid --- drivers/gpu/drm/display/drm_dp_helper.c | 35 ++++++++++++++++++++++++ drivers/gpu/drm/panel/panel-edp.c | 3 ++ drivers/gpu/drm/panel/panel-samsung-atna33xc20.c | 2 ++ include/drm/display/drm_dp_helper.h | 6 ++++ 4 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 9ac52cf5d4d8..57a32e962322 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -532,6 +532,15 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, mutex_lock(&aux->hw_mutex); + /* + * If the device attached to the aux bus is powered down then there's + * no reason to attempt a transfer. Error out immediately. + */ + if (aux->powered_down) { + ret = -EBUSY; + goto unlock; + } + /* * The specification doesn't give any recommendation on how often to * retry native transactions. We used to retry 7 times like for @@ -599,6 +608,29 @@ int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset) } EXPORT_SYMBOL(drm_dp_dpcd_probe); +/** + * drm_dp_dpcd_set_powered() - Set whether the DP device is powered + * @aux: DisplayPort AUX channel; for convenience it's OK to pass NULL here + * and the function will be a no-op. + * @powered: true if powered; false if not + * + * If the endpoint device on the DP AUX bus is known to be powered down + * then this function can be called to make future transfers fail immediately + * instead of needing to time out. + * + * If this function is never called then a device defaults to being powered. + */ +void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered) +{ + if (!aux) + return; + + mutex_lock(&aux->hw_mutex); + aux->powered_down = !powered; + mutex_unlock(&aux->hw_mutex); +} +EXPORT_SYMBOL(drm_dp_dpcd_set_powered); + /** * drm_dp_dpcd_read() - read a series of bytes from the DPCD * @aux: DisplayPort AUX channel (SST or MST) @@ -1858,6 +1890,9 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, struct drm_dp_aux_msg msg; int err = 0; + if (aux->powered_down) + return -EBUSY; + dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES); memset(&msg, 0, sizeof(msg)); diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index bd71d239272a..d58f90bc48fb 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -413,6 +413,7 @@ static int panel_edp_suspend(struct device *dev) { struct panel_edp *p = dev_get_drvdata(dev); + drm_dp_dpcd_set_powered(p->aux, false); gpiod_set_value_cansleep(p->enable_gpio, 0); regulator_disable(p->supply); p->unprepared_time = ktime_get_boottime(); @@ -469,6 +470,7 @@ static int panel_edp_prepare_once(struct panel_edp *p) } gpiod_set_value_cansleep(p->enable_gpio, 1); + drm_dp_dpcd_set_powered(p->aux, true); p->powered_on_time = ktime_get_boottime(); @@ -507,6 +509,7 @@ static int panel_edp_prepare_once(struct panel_edp *p) return 0; error: + drm_dp_dpcd_set_powered(p->aux, false); gpiod_set_value_cansleep(p->enable_gpio, 0); regulator_disable(p->supply); p->unprepared_time = ktime_get_boottime(); diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c index 5703f4712d96..76c2a8f6718c 100644 --- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c +++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c @@ -72,6 +72,7 @@ static int atana33xc20_suspend(struct device *dev) if (p->el3_was_on) atana33xc20_wait(p->el_on3_off_time, 150); + drm_dp_dpcd_set_powered(p->aux, false); ret = regulator_disable(p->supply); if (ret) return ret; @@ -93,6 +94,7 @@ static int atana33xc20_resume(struct device *dev) ret = regulator_enable(p->supply); if (ret) return ret; + drm_dp_dpcd_set_powered(p->aux, true); p->powered_on_time = ktime_get_boottime(); if (p->no_hpd) { diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 0c1a4021e098..4cf6cb3797ad 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -464,9 +464,15 @@ struct drm_dp_aux { * @is_remote: Is this AUX CH actually using sideband messaging. */ bool is_remote; + + /** + * @powered_down: If true then the remote endpoint is powered down. + */ + bool powered_down; }; int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset); +void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered); ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, void *buffer, size_t size); ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, -- cgit v1.2.3