From 7f35680ada234ce00828b8ea841ba7ca1e00ff52 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Jun 2022 11:20:04 +0200 Subject: [PATCH 01/63] drm/ast: Support multiple outputs Systems with AST graphics can have multiple output; typically VGA plus some other port. Record detected output chips in a bitmask and initialize each output on its own. Assume a VGA output by default and use SIL164 and DP501 if available. For ASTDP assume that it can run in parallel with VGA. Tested on AST2100. v3: * define a macro for each BIT(ast_tx_chip) (Patrik) v2: * make VGA/SIL164/DP501 mutually exclusive Signed-off-by: Thomas Zimmermann Reviewed-by: Patrik Jakobsson Fixes: a59b026419f3 ("drm/ast: Initialize encoder and connector for VGA in helper function") Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Dave Airlie Cc: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220607092008.22123-2-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_dp.c | 5 ++-- drivers/gpu/drm/ast/ast_dp501.c | 2 +- drivers/gpu/drm/ast/ast_drv.h | 9 +++++-- drivers/gpu/drm/ast/ast_main.c | 21 +++++++--------- drivers/gpu/drm/ast/ast_mode.c | 44 ++++++++++++++++++--------------- drivers/gpu/drm/ast/ast_post.c | 2 +- 6 files changed, 44 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c index 4551bc8a3ecf..f573d582407e 100644 --- a/drivers/gpu/drm/ast/ast_dp.c +++ b/drivers/gpu/drm/ast/ast_dp.c @@ -160,13 +160,12 @@ void ast_dp_launch(struct drm_device *dev, u8 bPower) } if (bDPExecute) - ast->tx_chip_type = AST_TX_ASTDP; + ast->tx_chip_types |= BIT(AST_TX_ASTDP); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, ASTDP_HOST_EDID_READ_DONE); - } else - ast->tx_chip_type = AST_TX_NONE; + } } diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 204c926a18ea..4f75a9efb610 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -450,7 +450,7 @@ void ast_init_3rdtx(struct drm_device *dev) ast_init_dvo(dev); break; default: - if (ast->tx_chip_type == AST_TX_SIL164) + if (ast->tx_chip_types & BIT(AST_TX_SIL164)) ast_init_dvo(dev); else ast_init_analog(dev); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index afebe35f205e..a34db4380f68 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -73,6 +73,11 @@ enum ast_tx_chip { AST_TX_ASTDP, }; +#define AST_TX_NONE_BIT BIT(AST_TX_NONE) +#define AST_TX_SIL164_BIT BIT(AST_TX_SIL164) +#define AST_TX_DP501_BIT BIT(AST_TX_DP501) +#define AST_TX_ASTDP_BIT BIT(AST_TX_ASTDP) + #define AST_DRAM_512Mx16 0 #define AST_DRAM_1Gx16 1 #define AST_DRAM_512Mx32 2 @@ -173,7 +178,7 @@ struct ast_private { struct drm_plane primary_plane; struct ast_cursor_plane cursor_plane; struct drm_crtc crtc; - union { + struct { struct { struct drm_encoder encoder; struct ast_vga_connector vga_connector; @@ -199,7 +204,7 @@ struct ast_private { ast_use_defaults } config_mode; - enum ast_tx_chip tx_chip_type; + unsigned long tx_chip_types; /* bitfield of enum ast_chip_type */ u8 *dp501_fw_addr; const struct firmware *dp501_fw; /* dp501 fw */ }; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index d770d5a23c1a..067453266897 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -216,7 +216,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) } /* Check 3rd Tx option (digital output afaik) */ - ast->tx_chip_type = AST_TX_NONE; + ast->tx_chip_types |= AST_TX_NONE_BIT; /* * VGACRA3 Enhanced Color Mode Register, check if DVO is already @@ -229,7 +229,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) if (!*need_post) { jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); if (jreg & 0x80) - ast->tx_chip_type = AST_TX_SIL164; + ast->tx_chip_types = AST_TX_SIL164_BIT; } if ((ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST2500)) { @@ -241,7 +241,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); switch (jreg) { case 0x04: - ast->tx_chip_type = AST_TX_SIL164; + ast->tx_chip_types = AST_TX_SIL164_BIT; break; case 0x08: ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL); @@ -254,22 +254,19 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) } fallthrough; case 0x0c: - ast->tx_chip_type = AST_TX_DP501; + ast->tx_chip_types = AST_TX_DP501_BIT; } } else if (ast->chip == AST2600) ast_dp_launch(&ast->base, 0); /* Print stuff for diagnostic purposes */ - switch(ast->tx_chip_type) { - case AST_TX_SIL164: + if (ast->tx_chip_types & AST_TX_NONE_BIT) + drm_info(dev, "Using analog VGA\n"); + if (ast->tx_chip_types & AST_TX_SIL164_BIT) drm_info(dev, "Using Sil164 TMDS transmitter\n"); - break; - case AST_TX_DP501: + if (ast->tx_chip_types & AST_TX_DP501_BIT) drm_info(dev, "Using DP501 DisplayPort transmitter\n"); - break; - default: - drm_info(dev, "Analog VGA only\n"); - } + return 0; } diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 323af2746aa9..db2010a55674 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -997,10 +997,10 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_ON: ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x01, 0xdf, 0); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xfc, 0); - if (ast->tx_chip_type == AST_TX_DP501) + if (ast->tx_chip_types & AST_TX_DP501_BIT) ast_set_dp501_video_output(crtc->dev, 1); - if (ast->tx_chip_type == AST_TX_ASTDP) { + if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { ast_dp_power_on_off(crtc->dev, AST_DP_POWER_ON); ast_wait_for_vretrace(ast); ast_dp_set_on_off(crtc->dev, 1); @@ -1012,17 +1012,17 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: ch = mode; - if (ast->tx_chip_type == AST_TX_DP501) + if (ast->tx_chip_types & AST_TX_DP501_BIT) ast_set_dp501_video_output(crtc->dev, 0); - break; - if (ast->tx_chip_type == AST_TX_ASTDP) { + if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { ast_dp_set_on_off(crtc->dev, 0); ast_dp_power_on_off(crtc->dev, AST_DP_POWER_OFF); } ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x01, 0xdf, 0x20); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xfc, ch); + break; } } @@ -1155,7 +1155,7 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, ast_crtc_load_lut(ast, crtc); //Set Aspeed Display-Port - if (ast->tx_chip_type == AST_TX_ASTDP) + if (ast->tx_chip_types & AST_TX_ASTDP_BIT) ast_dp_set_mode(crtc, vbios_mode_info); mutex_unlock(&ast->ioregs_lock); @@ -1739,22 +1739,26 @@ int ast_mode_config_init(struct ast_private *ast) ast_crtc_init(dev); - switch (ast->tx_chip_type) { - case AST_TX_NONE: + if (ast->tx_chip_types & AST_TX_NONE_BIT) { ret = ast_vga_output_init(ast); - break; - case AST_TX_SIL164: - ret = ast_sil164_output_init(ast); - break; - case AST_TX_DP501: - ret = ast_dp501_output_init(ast); - break; - case AST_TX_ASTDP: - ret = ast_astdp_output_init(ast); - break; + if (ret) + return ret; + } + if (ast->tx_chip_types & AST_TX_SIL164_BIT) { + ret = ast_sil164_output_init(ast); + if (ret) + return ret; + } + if (ast->tx_chip_types & AST_TX_DP501_BIT) { + ret = ast_dp501_output_init(ast); + if (ret) + return ret; + } + if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { + ret = ast_astdp_output_init(ast); + if (ret) + return ret; } - if (ret) - return ret; drm_mode_config_reset(dev); diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 0aa9cf0fb5c3..82fd3c8adee1 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -391,7 +391,7 @@ void ast_post_gpu(struct drm_device *dev) ast_init_3rdtx(dev); } else { - if (ast->tx_chip_type != AST_TX_NONE) + if (ast->tx_chip_types & AST_TX_SIL164_BIT) ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */ } } From 20e10881a043af63f2962a9e6bca64661225b383 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 8 Jun 2022 10:21:41 -0500 Subject: [PATCH 02/63] dma-buf: Add an API for exporting sync files (v14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modern userspace APIs like Vulkan are built on an explicit synchronization model. This doesn't always play nicely with the implicit synchronization used in the kernel and assumed by X11 and Wayland. The client -> compositor half of the synchronization isn't too bad, at least on intel, because we can control whether or not i915 synchronizes on the buffer and whether or not it's considered written. The harder part is the compositor -> client synchronization when we get the buffer back from the compositor. We're required to be able to provide the client with a VkSemaphore and VkFence representing the point in time where the window system (compositor and/or display) finished using the buffer. With current APIs, it's very hard to do this in such a way that we don't get confused by the Vulkan driver's access of the buffer. In particular, once we tell the kernel that we're rendering to the buffer again, any CPU waits on the buffer or GPU dependencies will wait on some of the client rendering and not just the compositor. This new IOCTL solves this problem by allowing us to get a snapshot of the implicit synchronization state of a given dma-buf in the form of a sync file. It's effectively the same as a poll() or I915_GEM_WAIT only, instead of CPU waiting directly, it encapsulates the wait operation, at the current moment in time, in a sync_file so we can check/wait on it later. As long as the Vulkan driver does the sync_file export from the dma-buf before we re-introduce it for rendering, it will only contain fences from the compositor or display. This allows to accurately turn it into a VkFence or VkSemaphore without any over-synchronization. By making this an ioctl on the dma-buf itself, it allows this new functionality to be used in an entirely driver-agnostic way without having access to a DRM fd. This makes it ideal for use in driver-generic code in Mesa or in a client such as a compositor where the DRM fd may be hard to reach. v2 (Jason Ekstrand): - Use a wrapper dma_fence_array of all fences including the new one when importing an exclusive fence. v3 (Jason Ekstrand): - Lock around setting shared fences as well as exclusive - Mark SIGNAL_SYNC_FILE as a read-write ioctl. - Initialize ret to 0 in dma_buf_wait_sync_file v4 (Jason Ekstrand): - Use the new dma_resv_get_singleton helper v5 (Jason Ekstrand): - Rename the IOCTLs to import/export rather than wait/signal - Drop the WRITE flag and always get/set the exclusive fence v6 (Jason Ekstrand): - Drop the sync_file import as it was all-around sketchy and not nearly as useful as import. - Re-introduce READ/WRITE flag support for export - Rework the commit message v7 (Jason Ekstrand): - Require at least one sync flag - Fix a refcounting bug: dma_resv_get_excl() doesn't take a reference - Use _rcu helpers since we're accessing the dma_resv read-only v8 (Jason Ekstrand): - Return -ENOMEM if the sync_file_create fails - Predicate support on IS_ENABLED(CONFIG_SYNC_FILE) v9 (Jason Ekstrand): - Add documentation for the new ioctl v10 (Jason Ekstrand): - Go back to dma_buf_sync_file as the ioctl struct name v11 (Daniel Vetter): - Go back to dma_buf_export_sync_file as the ioctl struct name - Better kerneldoc describing what the read/write flags do v12 (Christian König): - Document why we chose to make it an ioctl on dma-buf v13 (Jason Ekstrand): - Rebase on Christian König's fence rework v14 (Daniel Vetter & Christian König): - Use dma_rev_usage_rw to get the properly inverted usage to pass to dma_resv_get_singleton() - Clean up the sync_file and fd if copy_to_user() fails Signed-off-by: Jason Ekstrand Signed-off-by: Jason Ekstrand Signed-off-by: Jason Ekstrand Acked-by: Simon Ser Reviewed-by: Christian König Reviewed-by: Daniel Vetter Cc: Sumit Semwal Cc: Maarten Lankhorst Signed-off-by: Simon Ser Link: https://patchwork.freedesktop.org/patch/msgid/20220608152142.14495-2-jason@jlekstrand.net --- drivers/dma-buf/dma-buf.c | 67 ++++++++++++++++++++++++++++++++++++ include/uapi/linux/dma-buf.h | 35 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index a2f9a1815e38..491797a5dfc8 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -192,6 +193,9 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) * Note that this only signals the completion of the respective fences, i.e. the * DMA transfers are complete. Cache flushing and any other necessary * preparations before CPU access can begin still need to happen. + * + * As an alternative to poll(), the set of fences on DMA buffer can be + * exported as a &sync_file using &dma_buf_sync_file_export. */ static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb) @@ -326,6 +330,64 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf) return 0; } +#if IS_ENABLED(CONFIG_SYNC_FILE) +static long dma_buf_export_sync_file(struct dma_buf *dmabuf, + void __user *user_data) +{ + struct dma_buf_export_sync_file arg; + enum dma_resv_usage usage; + struct dma_fence *fence = NULL; + struct sync_file *sync_file; + int fd, ret; + + if (copy_from_user(&arg, user_data, sizeof(arg))) + return -EFAULT; + + if (arg.flags & ~DMA_BUF_SYNC_RW) + return -EINVAL; + + if ((arg.flags & DMA_BUF_SYNC_RW) == 0) + return -EINVAL; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + usage = dma_resv_usage_rw(arg.flags & DMA_BUF_SYNC_WRITE); + ret = dma_resv_get_singleton(dmabuf->resv, usage, &fence); + if (ret) + goto err_put_fd; + + if (!fence) + fence = dma_fence_get_stub(); + + sync_file = sync_file_create(fence); + + dma_fence_put(fence); + + if (!sync_file) { + ret = -ENOMEM; + goto err_put_fd; + } + + arg.fd = fd; + if (copy_to_user(user_data, &arg, sizeof(arg))) { + ret = -EFAULT; + goto err_put_file; + } + + fd_install(fd, sync_file->file); + + return 0; + +err_put_file: + fput(sync_file->file); +err_put_fd: + put_unused_fd(fd); + return ret; +} +#endif + static long dma_buf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -369,6 +431,11 @@ static long dma_buf_ioctl(struct file *file, case DMA_BUF_SET_NAME_B: return dma_buf_set_name(dmabuf, (const char __user *)arg); +#if IS_ENABLED(CONFIG_SYNC_FILE) + case DMA_BUF_IOCTL_EXPORT_SYNC_FILE: + return dma_buf_export_sync_file(dmabuf, (void __user *)arg); +#endif + default: return -ENOTTY; } diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h index 8e4a2ca0bcbf..46f1e3e98b02 100644 --- a/include/uapi/linux/dma-buf.h +++ b/include/uapi/linux/dma-buf.h @@ -85,6 +85,40 @@ struct dma_buf_sync { #define DMA_BUF_NAME_LEN 32 +/** + * struct dma_buf_export_sync_file - Get a sync_file from a dma-buf + * + * Userspace can perform a DMA_BUF_IOCTL_EXPORT_SYNC_FILE to retrieve the + * current set of fences on a dma-buf file descriptor as a sync_file. CPU + * waits via poll() or other driver-specific mechanisms typically wait on + * whatever fences are on the dma-buf at the time the wait begins. This + * is similar except that it takes a snapshot of the current fences on the + * dma-buf for waiting later instead of waiting immediately. This is + * useful for modern graphics APIs such as Vulkan which assume an explicit + * synchronization model but still need to inter-operate with dma-buf. + */ +struct dma_buf_export_sync_file { + /** + * @flags: Read/write flags + * + * Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both. + * + * If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set, + * the returned sync file waits on any writers of the dma-buf to + * complete. Waiting on the returned sync file is equivalent to + * poll() with POLLIN. + * + * If DMA_BUF_SYNC_WRITE is set, the returned sync file waits on + * any users of the dma-buf (read or write) to complete. Waiting + * on the returned sync file is equivalent to poll() with POLLOUT. + * If both DMA_BUF_SYNC_WRITE and DMA_BUF_SYNC_READ are set, this + * is equivalent to just DMA_BUF_SYNC_WRITE. + */ + __u32 flags; + /** @fd: Returned sync file descriptor */ + __s32 fd; +}; + #define DMA_BUF_BASE 'b' #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) @@ -94,5 +128,6 @@ struct dma_buf_sync { #define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 1, const char *) #define DMA_BUF_SET_NAME_A _IOW(DMA_BUF_BASE, 1, u32) #define DMA_BUF_SET_NAME_B _IOW(DMA_BUF_BASE, 1, u64) +#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) #endif From 594740497e998d30477ab26093bfb81c28cd3ff1 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 8 Jun 2022 10:21:42 -0500 Subject: [PATCH 03/63] dma-buf: Add an API for importing sync files (v10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is analogous to the previous sync file export patch in that it allows you to import a sync_file into a dma-buf. Unlike the previous patch, however, this does add genuinely new functionality to dma-buf. Without this, the only way to attach a sync_file to a dma-buf is to submit a batch to your driver of choice which waits on the sync_file and claims to write to the dma-buf. Even if said batch is a no-op, a submit is typically way more overhead than just attaching a fence. A submit may also imply extra synchronization with other work because it happens on a hardware queue. In the Vulkan world, this is useful for dealing with the out-fence from vkQueuePresent. Current Linux window-systems (X11, Wayland, etc.) all rely on dma-buf implicit sync. Since Vulkan is an explicit sync API, we get a set of fences (VkSemaphores) in vkQueuePresent and have to stash those as an exclusive (write) fence on the dma-buf. We handle it in Mesa today with the above mentioned dummy submit trick. This ioctl would allow us to set it directly without the dummy submit. This may also open up possibilities for GPU drivers to move away from implicit sync for their kernel driver uAPI and instead provide sync files and rely on dma-buf import/export for communicating with other implicit sync clients. We make the explicit choice here to only allow setting RW fences which translates to an exclusive fence on the dma_resv. There's no use for read-only fences for communicating with other implicit sync userspace and any such attempts are likely to be racy at best. When we got to insert the RW fence, the actual fence we set as the new exclusive fence is a combination of the sync_file provided by the user and all the other fences on the dma_resv. This ensures that the newly added exclusive fence will never signal before the old one would have and ensures that we don't break any dma_resv contracts. We require userspace to specify RW in the flags for symmetry with the export ioctl and in case we ever want to support read fences in the future. There is one downside here that's worth documenting: If two clients writing to the same dma-buf using this API race with each other, their actions on the dma-buf may happen in parallel or in an undefined order. Both with and without this API, the pattern is the same: Collect all the fences on dma-buf, submit work which depends on said fences, and then set a new exclusive (write) fence on the dma-buf which depends on said work. The difference is that, when it's all handled by the GPU driver's submit ioctl, the three operations happen atomically under the dma_resv lock. If two userspace submits race, one will happen before the other. You aren't guaranteed which but you are guaranteed that they're strictly ordered. If userspace manages the fences itself, then these three operations happen separately and the two render operations may happen genuinely in parallel or get interleaved. However, this is a case of userspace racing with itself. As long as we ensure userspace can't back the kernel into a corner, it should be fine. v2 (Jason Ekstrand): - Use a wrapper dma_fence_array of all fences including the new one when importing an exclusive fence. v3 (Jason Ekstrand): - Lock around setting shared fences as well as exclusive - Mark SIGNAL_SYNC_FILE as a read-write ioctl. - Initialize ret to 0 in dma_buf_wait_sync_file v4 (Jason Ekstrand): - Use the new dma_resv_get_singleton helper v5 (Jason Ekstrand): - Rename the IOCTLs to import/export rather than wait/signal - Drop the WRITE flag and always get/set the exclusive fence v6 (Jason Ekstrand): - Split import and export into separate patches - New commit message v7 (Daniel Vetter): - Fix the uapi header to use the right struct in the ioctl - Use a separate dma_buf_import_sync_file struct - Add kerneldoc for dma_buf_import_sync_file v8 (Jason Ekstrand): - Rebase on Christian König's fence rework v9 (Daniel Vetter): - Fix -EINVAL checks for the flags parameter - Add documentation about read/write fences - Add documentation about the expected usage of import/export and specifically call out the possible userspace race. v10 (Simon Ser): - Fix a typo in the docs Signed-off-by: Jason Ekstrand Signed-off-by: Jason Ekstrand Signed-off-by: Jason Ekstrand Reviewed-by: Christian König Reviewed-by: Daniel Vetter Cc: Sumit Semwal Cc: Maarten Lankhorst Signed-off-by: Simon Ser Link: https://patchwork.freedesktop.org/patch/msgid/20220608152142.14495-3-jason@jlekstrand.net --- drivers/dma-buf/dma-buf.c | 39 ++++++++++++++++++++++++++++ include/uapi/linux/dma-buf.h | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 491797a5dfc8..5e1b0534b3ce 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -386,6 +386,43 @@ err_put_fd: put_unused_fd(fd); return ret; } + +static long dma_buf_import_sync_file(struct dma_buf *dmabuf, + const void __user *user_data) +{ + struct dma_buf_import_sync_file arg; + struct dma_fence *fence; + enum dma_resv_usage usage; + int ret = 0; + + if (copy_from_user(&arg, user_data, sizeof(arg))) + return -EFAULT; + + if (arg.flags & ~DMA_BUF_SYNC_RW) + return -EINVAL; + + if ((arg.flags & DMA_BUF_SYNC_RW) == 0) + return -EINVAL; + + fence = sync_file_get_fence(arg.fd); + if (!fence) + return -EINVAL; + + usage = (arg.flags & DMA_BUF_SYNC_WRITE) ? DMA_RESV_USAGE_WRITE : + DMA_RESV_USAGE_READ; + + dma_resv_lock(dmabuf->resv, NULL); + + ret = dma_resv_reserve_fences(dmabuf->resv, 1); + if (!ret) + dma_resv_add_fence(dmabuf->resv, fence, usage); + + dma_resv_unlock(dmabuf->resv); + + dma_fence_put(fence); + + return ret; +} #endif static long dma_buf_ioctl(struct file *file, @@ -434,6 +471,8 @@ static long dma_buf_ioctl(struct file *file, #if IS_ENABLED(CONFIG_SYNC_FILE) case DMA_BUF_IOCTL_EXPORT_SYNC_FILE: return dma_buf_export_sync_file(dmabuf, (void __user *)arg); + case DMA_BUF_IOCTL_IMPORT_SYNC_FILE: + return dma_buf_import_sync_file(dmabuf, (const void __user *)arg); #endif default: diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h index 46f1e3e98b02..30fb8834aa3c 100644 --- a/include/uapi/linux/dma-buf.h +++ b/include/uapi/linux/dma-buf.h @@ -96,6 +96,24 @@ struct dma_buf_sync { * dma-buf for waiting later instead of waiting immediately. This is * useful for modern graphics APIs such as Vulkan which assume an explicit * synchronization model but still need to inter-operate with dma-buf. + * + * The intended usage pattern is the following: + * + * 1. Export a sync_file with flags corresponding to the expected GPU usage + * via DMA_BUF_IOCTL_EXPORT_SYNC_FILE. + * + * 2. Submit rendering work which uses the dma-buf. The work should wait on + * the exported sync file before rendering and produce another sync_file + * when complete. + * + * 3. Import the rendering-complete sync_file into the dma-buf with flags + * corresponding to the GPU usage via DMA_BUF_IOCTL_IMPORT_SYNC_FILE. + * + * Unlike doing implicit synchronization via a GPU kernel driver's exec ioctl, + * the above is not a single atomic operation. If userspace wants to ensure + * ordering via these fences, it is the respnosibility of userspace to use + * locks or other mechanisms to ensure that no other context adds fences or + * submits work between steps 1 and 3 above. */ struct dma_buf_export_sync_file { /** @@ -119,6 +137,36 @@ struct dma_buf_export_sync_file { __s32 fd; }; +/** + * struct dma_buf_import_sync_file - Insert a sync_file into a dma-buf + * + * Userspace can perform a DMA_BUF_IOCTL_IMPORT_SYNC_FILE to insert a + * sync_file into a dma-buf for the purposes of implicit synchronization + * with other dma-buf consumers. This allows clients using explicitly + * synchronized APIs such as Vulkan to inter-op with dma-buf consumers + * which expect implicit synchronization such as OpenGL or most media + * drivers/video. + */ +struct dma_buf_import_sync_file { + /** + * @flags: Read/write flags + * + * Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both. + * + * If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set, + * this inserts the sync_file as a read-only fence. Any subsequent + * implicitly synchronized writes to this dma-buf will wait on this + * fence but reads will not. + * + * If DMA_BUF_SYNC_WRITE is set, this inserts the sync_file as a + * write fence. All subsequent implicitly synchronized access to + * this dma-buf will wait on this fence. + */ + __u32 flags; + /** @fd: Sync file descriptor */ + __s32 fd; +}; + #define DMA_BUF_BASE 'b' #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) @@ -129,5 +177,6 @@ struct dma_buf_export_sync_file { #define DMA_BUF_SET_NAME_A _IOW(DMA_BUF_BASE, 1, u32) #define DMA_BUF_SET_NAME_B _IOW(DMA_BUF_BASE, 1, u64) #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) +#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file) #endif From faa406f4233ecbd781554ae0a850e56f53003d2e Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 8 Jun 2022 14:58:21 +0100 Subject: [PATCH 04/63] drm/cma-helper: Describe what a "contiguous chunk" actually means Since it's inception in 2012 it has been understood that the DRM GEM CMA helpers do not depend on CMA as the backend allocator. In fact the first bug fix to ensure the cma-helpers work correctly with an IOMMU backend appeared in 2014. However currently the documentation for drm_gem_cma_create() talks about "a contiguous chunk of memory" without making clear which address space it will be a contiguous part of. Additionally the CMA introduction is actively misleading because it only contemplates the CMA backend. This matters because when the device accesses the bus through an IOMMU (and don't use the CMA backend) then the allocated memory is contiguous only in the IOVA space. This is a significant difference compared to the CMA backend and the behaviour can be a surprise even to someone who does a reasonable level of code browsing (but doesn't find all the relevant function pointers ;-) ). Improve the kernel doc comments accordingly. Signed-off-by: Daniel Thompson Reviewed-by: Lucas Stach Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220608135821.1153346-1-daniel.thompson@linaro.org --- drivers/gpu/drm/drm_gem_cma_helper.c | 39 +++++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index f36734c2c9e1..42abee9a0f4f 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -26,12 +26,22 @@ /** * DOC: cma helpers * - * The Contiguous Memory Allocator reserves a pool of memory at early boot - * that is used to service requests for large blocks of contiguous memory. + * The DRM GEM/CMA helpers are a means to provide buffer objects that are + * presented to the device as a contiguous chunk of memory. This is useful + * for devices that do not support scatter-gather DMA (either directly or + * by using an intimately attached IOMMU). * - * The DRM GEM/CMA helpers use this allocator as a means to provide buffer - * objects that are physically contiguous in memory. This is useful for - * display drivers that are unable to map scattered buffers via an IOMMU. + * Despite the name, the DRM GEM/CMA helpers are not hardwired to use the + * Contiguous Memory Allocator (CMA). + * + * For devices that access the memory bus through an (external) IOMMU then + * the buffer objects are allocated using a traditional page-based + * allocator and may be scattered through physical memory. However they + * are contiguous in the IOVA space so appear contiguous to devices using + * them. + * + * For other devices then the helpers rely on CMA to provide buffer + * objects that are physically contiguous in memory. * * For GEM callback helpers in struct &drm_gem_object functions, see likewise * named functions with an _object_ infix (e.g., drm_gem_cma_object_vmap() wraps @@ -111,8 +121,14 @@ error: * @drm: DRM device * @size: size of the object to allocate * - * This function creates a CMA GEM object and allocates a contiguous chunk of - * memory as backing store. + * This function creates a CMA GEM object and allocates memory as backing store. + * The allocated memory will occupy a contiguous chunk of bus address space. + * + * For devices that are directly connected to the memory bus then the allocated + * memory will be physically contiguous. For devices that access through an + * IOMMU, then the allocated memory is not expected to be physically contiguous + * because having contiguous IOVAs is sufficient to meet a devices DMA + * requirements. * * Returns: * A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative @@ -162,9 +178,12 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_create); * @size: size of the object to allocate * @handle: return location for the GEM handle * - * This function creates a CMA GEM object, allocating a physically contiguous - * chunk of memory as backing store. The GEM object is then added to the list - * of object associated with the given file and a handle to it is returned. + * This function creates a CMA GEM object, allocating a chunk of memory as + * backing store. The GEM object is then added to the list of object associated + * with the given file and a handle to it is returned. + * + * The allocated memory will occupy a contiguous chunk of bus address space. + * See drm_gem_cma_create() for more details. * * Returns: * A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative From 514c62048b9cca3b7d1ac23c373ad9b4ea92ba1f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 8 Jun 2022 17:41:16 +0200 Subject: [PATCH 05/63] fbcon: Remove obsolete reference to initmem_freed initmem_freed was removed in v2.1.124, and the underlying issue was fixed for good in commit 92b004d1aa9f367c ("video/logo: prevent use of logos after they have been freed"). Signed-off-by: Geert Uytterhoeven Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/b8b9147a48e233fe32e072f2085c7b413cd92a00.1654702835.git.geert+renesas@glider.be --- drivers/video/fbdev/core/fbcon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index c4e91715ef00..d765bbdf19dc 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2182,7 +2182,6 @@ static int fbcon_switch(struct vc_data *vc) if (logo_shown == FBCON_LOGO_DRAW) { logo_shown = fg_console; - /* This is protected above by initmem_freed */ fb_show_logo(info, ops->rotate); update_region(vc, vc->vc_origin + vc->vc_size_row * vc->vc_top, From c11256f82ec82216f69dafee75b3d854d92392d1 Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Fri, 1 Apr 2022 05:48:08 +0530 Subject: [PATCH 06/63] drm/panel: nt36672a: add backlight support Add support for backlight. This panel supports backlight control through the QCOM WLED driver in Xiaomi Poco F1 device. Signed-off-by: Joel Selvaraj Reviewed-by: Marijn Suijten Signed-off-by: Sumit Semwal Link: https://patchwork.freedesktop.org/patch/msgid/BY5PR02MB700935F5817128CB7C3991CDD9E09@BY5PR02MB7009.namprd02.prod.outlook.com --- drivers/gpu/drm/panel/panel-novatek-nt36672a.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c index 231f371901e8..6d6ce42787e2 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c @@ -628,6 +628,10 @@ static int nt36672a_panel_add(struct nt36672a_panel *pinfo) drm_panel_init(&pinfo->base, dev, &panel_funcs, DRM_MODE_CONNECTOR_DSI); + ret = drm_panel_of_backlight(&pinfo->base); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + drm_panel_add(&pinfo->base); return 0; From 2292639b4cc89f4411a7dd565634517fb09ec97c Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Fri, 1 Apr 2022 05:48:09 +0530 Subject: [PATCH 07/63] dt-bindings: display: novatek, nt36672a: add backlight property Add backlight property and update example to include it. Signed-off-by: Joel Selvaraj Acked-by: Rob Herring Reviewed-by: Marijn Suijten Signed-off-by: Sumit Semwal Link: https://patchwork.freedesktop.org/patch/msgid/BY5PR02MB70090BB5D8C7D655BEE0642FD9E09@BY5PR02MB7009.namprd02.prod.outlook.com --- .../devicetree/bindings/display/panel/novatek,nt36672a.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml index 563766d283f6..41ee3157a1cd 100644 --- a/Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml @@ -46,6 +46,7 @@ properties: reg: true port: true + backlight: true required: - compatible @@ -73,6 +74,7 @@ examples: vddpos-supply = <&lab>; vddneg-supply = <&ibb>; + backlight = <&pmi8998_wled>; reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; port { From 6aed665f9d8368ae1e962f44339150884bb47f5b Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 19 Mar 2022 11:27:51 +0100 Subject: [PATCH 08/63] drm/nouveau/bios: Rename prom_init() and friends functions While working at fixing powerpc headers, I ended up with the following error. drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c:48:1: error: conflicting types for 'prom_init'; have 'void *(struct nvkm_bios *, const char *)' make[5]: *** [scripts/Makefile.build:288: drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.o] Error 1 powerpc and a few other architectures have a prom_init() global function. One day or another it will conflict with the one in shadowrom.c Those being static, they can easily be renamed. Do it. While at it, also rename the ops structure as 'nvbios_prom' instead of 'nvbios_rom' in order to make it clear that it refers to the NV_PROM device. Signed-off-by: Christophe Leroy Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/7e0612b61511ec8030e3b2dcbfaa7751781c8b91.1647684507.git.christophe.leroy@csgroup.eu --- drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h | 2 +- drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c | 2 +- .../gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h index fac1bff1311b..cfa8a0c356dd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h @@ -19,7 +19,7 @@ struct nvbios_source { int nvbios_extend(struct nvkm_bios *, u32 length); int nvbios_shadow(struct nvkm_bios *); -extern const struct nvbios_source nvbios_rom; +extern const struct nvbios_source nvbios_prom; extern const struct nvbios_source nvbios_ramin; extern const struct nvbios_source nvbios_acpi_fast; extern const struct nvbios_source nvbios_acpi_slow; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c index 4b571cc6bc70..19188683c8fc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c @@ -171,7 +171,7 @@ nvbios_shadow(struct nvkm_bios *bios) struct shadow mthds[] = { { 0, &nvbios_of }, { 0, &nvbios_ramin }, - { 0, &nvbios_rom }, + { 0, &nvbios_prom }, { 0, &nvbios_acpi_fast }, { 4, &nvbios_acpi_slow }, { 1, &nvbios_pcirom }, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c index ffa4b395220a..39144ceb117b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c @@ -25,7 +25,7 @@ #include static u32 -prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) { struct nvkm_device *device = data; u32 i; @@ -38,14 +38,14 @@ prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) } static void -prom_fini(void *data) +nvbios_prom_fini(void *data) { struct nvkm_device *device = data; nvkm_pci_rom_shadow(device->pci, true); } static void * -prom_init(struct nvkm_bios *bios, const char *name) +nvbios_prom_init(struct nvkm_bios *bios, const char *name) { struct nvkm_device *device = bios->subdev.device; if (device->card_type == NV_40 && device->chipset >= 0x4c) @@ -55,10 +55,10 @@ prom_init(struct nvkm_bios *bios, const char *name) } const struct nvbios_source -nvbios_rom = { +nvbios_prom = { .name = "PROM", - .init = prom_init, - .fini = prom_fini, - .read = prom_read, + .init = nvbios_prom_init, + .fini = nvbios_prom_fini, + .read = nvbios_prom_read, .rw = false, }; From bd63f11f4c3c46afec07d821f74736161ff6e526 Mon Sep 17 00:00:00 2001 From: Xiaomeng Tong Date: Sun, 27 Mar 2022 13:09:45 +0800 Subject: [PATCH 09/63] virtio-gpu: fix a missing check to avoid NULL dereference 'cache_ent' could be set NULL inside virtio_gpu_cmd_get_capset() and it will lead to a NULL dereference by a lately use of it (i.e., ptr = cache_ent->caps_cache). Fix it with a NULL check. Fixes: 62fb7a5e10962 ("virtio-gpu: add 3d/virgl support") Signed-off-by: Xiaomeng Tong Reviewed-by: Chia-I Wu Link: http://patchwork.freedesktop.org/patch/msgid/20220327050945.1614-1-xiam0nd.tong@gmail.com [ kraxel: minor codestyle fixup ] Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_ioctl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index f8d83358d2a0..9b2702116f93 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -580,8 +580,10 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev, spin_unlock(&vgdev->display_info_lock); /* not in cache - need to talk to hw */ - virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver, - &cache_ent); + ret = virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver, + &cache_ent); + if (ret) + return ret; virtio_gpu_notify(vgdev); copy_exit: From 430ac054e5ea172a880e07da494f65f0b80d8fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Wed, 13 Apr 2022 18:12:59 +0200 Subject: [PATCH 10/63] drm/bochs: Explicitly include linux/module.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of relying on it getting pulled in indirectly. Signed-off-by: Michel Dänzer Reviewed-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20220413161259.1854270-1-michel@daenzer.net Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/tiny/bochs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index ed971c8bb446..4f8bf86633df 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include From d9c1452c0f07c2e0766a6cf3921eca182e26fdf3 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Thu, 21 Apr 2022 10:20:54 -0400 Subject: [PATCH 11/63] drm/qxl: remove qxl_log_level global Smatch reports this issue qxl_kms.c:36:5: warning: symbol 'qxl_log_level' was not declared. Should it be static? qxl_log_level is defined qxl_kms.c but unused, so remove. Signed-off-by: Tom Rix Link: http://patchwork.freedesktop.org/patch/msgid/20220421142054.3751507-1-trix@redhat.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_kms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index a054e4a00fe8..9bf6d4cc98d4 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -33,8 +33,6 @@ #include "qxl_drv.h" #include "qxl_object.h" -int qxl_log_level; - static bool qxl_check_device(struct qxl_device *qdev) { struct qxl_rom *rom = qdev->rom; From 20f038d074d4fa52e88a36bebf25e81c88d7e6fd Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Fri, 29 Apr 2022 05:49:11 +0000 Subject: [PATCH 12/63] drm/virtio: simplify the return expression Simplify the return expression. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Link: http://patchwork.freedesktop.org/patch/msgid/20220429054911.3851977-1-chi.minghao@zte.com.cn Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_prime.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c index 55d80b77d9b0..44425f20d91a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_prime.c +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c @@ -90,7 +90,6 @@ static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = { int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo) { - int ret; struct virtio_gpu_object_array *objs; objs = virtio_gpu_array_alloc(1); @@ -98,11 +97,8 @@ int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, return -ENOMEM; virtio_gpu_array_add_obj(objs, &bo->base.base); - ret = virtio_gpu_cmd_resource_assign_uuid(vgdev, objs); - if (ret) - return ret; - return 0; + return virtio_gpu_cmd_resource_assign_uuid(vgdev, objs); } struct dma_buf *virtgpu_gem_prime_export(struct drm_gem_object *obj, From 9e9fa6a9198b767b00f48160800128e83a038f9f Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 20 May 2022 13:52:35 -0700 Subject: [PATCH 13/63] udmabuf: Set the DMA mask for the udmabuf device (v2) If the DMA mask is not set explicitly, the following warning occurs when the userspace tries to access the dma-buf via the CPU as reported by syzbot here: WARNING: CPU: 1 PID: 3595 at kernel/dma/mapping.c:188 __dma_map_sg_attrs+0x181/0x1f0 kernel/dma/mapping.c:188 Modules linked in: CPU: 0 PID: 3595 Comm: syz-executor249 Not tainted 5.17.0-rc2-syzkaller-00316-g0457e5153e0e #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:__dma_map_sg_attrs+0x181/0x1f0 kernel/dma/mapping.c:188 Code: 00 00 00 00 00 fc ff df 48 c1 e8 03 80 3c 10 00 75 71 4c 8b 3d c0 83 b5 0d e9 db fe ff ff e8 b6 0f 13 00 0f 0b e8 af 0f 13 00 <0f> 0b 45 31 e4 e9 54 ff ff ff e8 a0 0f 13 00 49 8d 7f 50 48 b8 00 RSP: 0018:ffffc90002a07d68 EFLAGS: 00010293 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: ffff88807e25e2c0 RSI: ffffffff81649e91 RDI: ffff88801b848408 RBP: ffff88801b848000 R08: 0000000000000002 R09: ffff88801d86c74f R10: ffffffff81649d72 R11: 0000000000000001 R12: 0000000000000002 R13: ffff88801d86c680 R14: 0000000000000001 R15: 0000000000000000 FS: 0000555556e30300(0000) GS:ffff8880b9d00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000200000cc CR3: 000000001d74a000 CR4: 00000000003506e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: dma_map_sgtable+0x70/0xf0 kernel/dma/mapping.c:264 get_sg_table.isra.0+0xe0/0x160 drivers/dma-buf/udmabuf.c:72 begin_cpu_udmabuf+0x130/0x1d0 drivers/dma-buf/udmabuf.c:126 dma_buf_begin_cpu_access+0xfd/0x1d0 drivers/dma-buf/dma-buf.c:1164 dma_buf_ioctl+0x259/0x2b0 drivers/dma-buf/dma-buf.c:363 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:874 [inline] __se_sys_ioctl fs/ioctl.c:860 [inline] __x64_sys_ioctl+0x193/0x200 fs/ioctl.c:860 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae RIP: 0033:0x7f62fcf530f9 Code: 28 c3 e8 2a 14 00 00 66 2e 0f 1f 84 00 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffe3edab9b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f62fcf530f9 RDX: 0000000020000200 RSI: 0000000040086200 RDI: 0000000000000006 RBP: 00007f62fcf170e0 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007f62fcf17170 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 v2: Dont't forget to deregister if DMA mask setup fails. Reported-by: syzbot+10e27961f4da37c443b2@syzkaller.appspotmail.com Cc: Gerd Hoffmann Signed-off-by: Vivek Kasireddy Link: http://patchwork.freedesktop.org/patch/msgid/20220520205235.3687336-1-vivek.kasireddy@intel.com Signed-off-by: Gerd Hoffmann --- drivers/dma-buf/udmabuf.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index e7330684d3b8..384cb3d06ff3 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -365,7 +365,23 @@ static struct miscdevice udmabuf_misc = { static int __init udmabuf_dev_init(void) { - return misc_register(&udmabuf_misc); + int ret; + + ret = misc_register(&udmabuf_misc); + if (ret < 0) { + pr_err("Could not initialize udmabuf device\n"); + return ret; + } + + ret = dma_coerce_mask_and_coherent(udmabuf_misc.this_device, + DMA_BIT_MASK(64)); + if (ret < 0) { + pr_err("Could not setup DMA mask for udmabuf device\n"); + misc_deregister(&udmabuf_misc); + return ret; + } + + return 0; } static void __exit udmabuf_dev_exit(void) From c24968734abfed81c8f93dc5f44a7b7a9aecadfa Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Thu, 2 Jun 2022 14:42:22 +0400 Subject: [PATCH 14/63] drm/virtio: Fix NULL vs IS_ERR checking in virtio_gpu_object_shmem_init Since drm_prime_pages_to_sg() function return error pointers. The drm_gem_shmem_get_sg_table() function returns error pointers too. Using IS_ERR() to check the return value to fix this. Fixes: 2f2aa13724d5 ("drm/virtio: move virtio_gpu_mem_entry initialization to new function") Signed-off-by: Miaoqian Lin Link: http://patchwork.freedesktop.org/patch/msgid/20220602104223.54527-1-linmq006@gmail.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index f293e6ad52da..1cc8f3fc8e4b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -168,9 +168,9 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev, * since virtio_gpu doesn't support dma-buf import from other devices. */ shmem->pages = drm_gem_shmem_get_sg_table(&bo->base); - if (!shmem->pages) { + if (IS_ERR(shmem->pages)) { drm_gem_shmem_unpin(&bo->base); - return -EINVAL; + return PTR_ERR(shmem->pages); } if (use_dma_api) { From f17c655cfb99796918d96ae25261db2640407d01 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 Jun 2022 13:51:22 +0200 Subject: [PATCH 15/63] drm/mgag200: Include for G200 BIOS code After moving the vmalloc() call to another file, the rsp include statement needs to be moved as well. Resolves a build warning on parisc. drivers/gpu/drm/mgag200/mgag200_g200.c: In function 'mgag200_g200_init_refclk': drivers/gpu/drm/mgag200/mgag200_g200.c:120:16: error: implicit declaration of function 'vmalloc'; did you mean 'kvmalloc'? [-Werror=implicit-function-declaration] Signed-off-by: Thomas Zimmermann Fixes: 85397f6bc4ff ("drm/mgag200: Initialize each model in separate function") Reviewed-by: Daniel Vetter Reported-by: kernel test robot Cc: Thomas Zimmermann Cc: Jocelyn Falempe Cc: Dave Airlie Cc: dri-devel@lists.freedesktop.org Link: https://lore.kernel.org/all/202206080734.ztAvDG7O-lkp@intel.com/ Link: https://patchwork.freedesktop.org/patch/msgid/20220608115122.7448-1-tzimmermann@suse.de --- drivers/gpu/drm/mgag200/mgag200_drv.c | 1 - drivers/gpu/drm/mgag200/mgag200_g200.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 361eb7dffda1..73e8e4e9e54b 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/drivers/gpu/drm/mgag200/mgag200_g200.c b/drivers/gpu/drm/mgag200/mgag200_g200.c index 616e11391e02..674385921b7f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include From 0949ee75da6c918fcbd567e1bfa4943a56ab4e5d Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 7 Jun 2022 20:23:34 +0200 Subject: [PATCH 16/63] firmware: sysfb: Make sysfb_create_simplefb() return a pdev pointer This function just returned 0 on success or an errno code on error, but it could be useful for sysfb_init() callers to have a pointer to the device. Signed-off-by: Javier Martinez Canillas Reviewed-by: Daniel Vetter Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220607182338.344270-2-javierm@redhat.com --- drivers/firmware/sysfb.c | 4 ++-- drivers/firmware/sysfb_simplefb.c | 16 ++++++++-------- include/linux/sysfb.h | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 2bfbb05f7d89..b032f40a92de 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -46,8 +46,8 @@ static __init int sysfb_init(void) /* try to create a simple-framebuffer device */ compatible = sysfb_parse_mode(si, &mode); if (compatible) { - ret = sysfb_create_simplefb(si, &mode); - if (!ret) + pd = sysfb_create_simplefb(si, &mode); + if (!IS_ERR(pd)) return 0; } diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c index bda8712bfd8c..a353e27f83f5 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -57,8 +57,8 @@ __init bool sysfb_parse_mode(const struct screen_info *si, return false; } -__init int sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode) +__init struct platform_device *sysfb_create_simplefb(const struct screen_info *si, + const struct simplefb_platform_data *mode) { struct platform_device *pd; struct resource res; @@ -76,7 +76,7 @@ __init int sysfb_create_simplefb(const struct screen_info *si, base |= (u64)si->ext_lfb_base << 32; if (!base || (u64)(resource_size_t)base != base) { printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } /* @@ -93,7 +93,7 @@ __init int sysfb_create_simplefb(const struct screen_info *si, length = mode->height * mode->stride; if (length > size) { printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } length = PAGE_ALIGN(length); @@ -104,11 +104,11 @@ __init int sysfb_create_simplefb(const struct screen_info *si, res.start = base; res.end = res.start + length - 1; if (res.end <= res.start) - return -EINVAL; + return ERR_PTR(-EINVAL); pd = platform_device_alloc("simple-framebuffer", 0); if (!pd) - return -ENOMEM; + return ERR_PTR(-ENOMEM); sysfb_apply_efi_quirks(pd); @@ -124,10 +124,10 @@ __init int sysfb_create_simplefb(const struct screen_info *si, if (ret) goto err_put_device; - return 0; + return pd; err_put_device: platform_device_put(pd); - return ret; + return ERR_PTR(ret); } diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index b0dcfa26d07b..708152e9037b 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -72,8 +72,8 @@ static inline void sysfb_apply_efi_quirks(struct platform_device *pd) bool sysfb_parse_mode(const struct screen_info *si, struct simplefb_platform_data *mode); -int sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode); +struct platform_device *sysfb_create_simplefb(const struct screen_info *si, + const struct simplefb_platform_data *mode); #else /* CONFIG_SYSFB_SIMPLE */ @@ -83,10 +83,10 @@ static inline bool sysfb_parse_mode(const struct screen_info *si, return false; } -static inline int sysfb_create_simplefb(const struct screen_info *si, - const struct simplefb_platform_data *mode) +static inline struct platform_device *sysfb_create_simplefb(const struct screen_info *si, + const struct simplefb_platform_data *mode) { - return -EINVAL; + return ERR_PTR(-EINVAL); } #endif /* CONFIG_SYSFB_SIMPLE */ From bc824922b264aff40eba8c160972ee07a95e7dd4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 7 Jun 2022 20:23:35 +0200 Subject: [PATCH 17/63] firmware: sysfb: Add sysfb_disable() helper function This can be used by subsystems to unregister a platform device registered by sysfb and also to disable future platform device registration in sysfb. Suggested-by: Daniel Vetter Signed-off-by: Javier Martinez Canillas Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220607182338.344270-3-javierm@redhat.com --- .../driver-api/firmware/other_interfaces.rst | 6 +++ drivers/firmware/sysfb.c | 54 ++++++++++++++++--- include/linux/sysfb.h | 12 +++++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Documentation/driver-api/firmware/other_interfaces.rst b/Documentation/driver-api/firmware/other_interfaces.rst index b81794e0cfbb..06ac89adaafb 100644 --- a/Documentation/driver-api/firmware/other_interfaces.rst +++ b/Documentation/driver-api/firmware/other_interfaces.rst @@ -13,6 +13,12 @@ EDD Interfaces .. kernel-doc:: drivers/firmware/edd.c :internal: +Generic System Framebuffers Interface +------------------------------------- + +.. kernel-doc:: drivers/firmware/sysfb.c + :export: + Intel Stratix10 SoC Service Layer --------------------------------- Some features of the Intel Stratix10 SoC require a level of privilege diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index b032f40a92de..1f276f108cc9 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -34,21 +34,59 @@ #include #include +static struct platform_device *pd; +static DEFINE_MUTEX(disable_lock); +static bool disabled; + +static bool sysfb_unregister(void) +{ + if (IS_ERR_OR_NULL(pd)) + return false; + + platform_device_unregister(pd); + pd = NULL; + + return true; +} + +/** + * sysfb_disable() - disable the Generic System Framebuffers support + * + * This disables the registration of system framebuffer devices that match the + * generic drivers that make use of the system framebuffer set up by firmware. + * + * It also unregisters a device if this was already registered by sysfb_init(). + * + * Context: The function can sleep. A @disable_lock mutex is acquired to serialize + * against sysfb_init(), that registers a system framebuffer device. + */ +void sysfb_disable(void) +{ + mutex_lock(&disable_lock); + sysfb_unregister(); + disabled = true; + mutex_unlock(&disable_lock); +} +EXPORT_SYMBOL_GPL(sysfb_disable); + static __init int sysfb_init(void) { struct screen_info *si = &screen_info; struct simplefb_platform_data mode; - struct platform_device *pd; const char *name; bool compatible; - int ret; + int ret = 0; + + mutex_lock(&disable_lock); + if (disabled) + goto unlock_mutex; /* try to create a simple-framebuffer device */ compatible = sysfb_parse_mode(si, &mode); if (compatible) { pd = sysfb_create_simplefb(si, &mode); if (!IS_ERR(pd)) - return 0; + goto unlock_mutex; } /* if the FB is incompatible, create a legacy framebuffer device */ @@ -60,8 +98,10 @@ static __init int sysfb_init(void) name = "platform-framebuffer"; pd = platform_device_alloc(name, 0); - if (!pd) - return -ENOMEM; + if (!pd) { + ret = -ENOMEM; + goto unlock_mutex; + } sysfb_apply_efi_quirks(pd); @@ -73,9 +113,11 @@ static __init int sysfb_init(void) if (ret) goto err; - return 0; + goto unlock_mutex; err: platform_device_put(pd); +unlock_mutex: + mutex_unlock(&disable_lock); return ret; } diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index 708152e9037b..8ba8b5be5567 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -55,6 +55,18 @@ struct efifb_dmi_info { int flags; }; +#ifdef CONFIG_SYSFB + +void sysfb_disable(void); + +#else /* CONFIG_SYSFB */ + +static inline void sysfb_disable(void) +{ +} + +#endif /* CONFIG_SYSFB */ + #ifdef CONFIG_EFI extern struct efifb_dmi_info efifb_dmi_list[]; From 873eb3b11860aada97ddc02d48b54522b92848db Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 7 Jun 2022 20:23:36 +0200 Subject: [PATCH 18/63] fbdev: Disable sysfb device registration when removing conflicting FBs The platform devices registered by sysfb match with firmware-based DRM or fbdev drivers, that are used to have early graphics using a framebuffer provided by the system firmware. DRM or fbdev drivers later are probed and remove conflicting framebuffers, leading to these platform devices for generic drivers to be unregistered. But the current solution has a race, since the sysfb_init() function could be called after a DRM or fbdev driver is probed and request to unregister the devices for drivers with conflicting framebuffes. To prevent this, disable any future sysfb platform device registration by calling sysfb_disable(), if a driver requests to remove the conflicting framebuffers. Suggested-by: Daniel Vetter Signed-off-by: Javier Martinez Canillas Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220607182338.344270-4-javierm@redhat.com --- drivers/video/fbdev/core/fbmem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 2fda5917c212..e0720fef0ee6 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1764,6 +1765,17 @@ int remove_conflicting_framebuffers(struct apertures_struct *a, do_free = true; } + /* + * If a driver asked to unregister a platform device registered by + * sysfb, then can be assumed that this is a driver for a display + * that is set up by the system firmware and has a generic driver. + * + * Drivers for devices that don't have a generic driver will never + * ask for this, so let's assume that a real driver for the display + * was already probed and prevent sysfb to register devices later. + */ + sysfb_disable(); + mutex_lock(®istration_lock); do_remove_conflicting_framebuffers(a, name, primary); mutex_unlock(®istration_lock); From bdde97ac4bea26d48f60e353396b2b4fb0234ac4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 7 Jun 2022 20:23:37 +0200 Subject: [PATCH 19/63] Revert "fbdev: Prevent probing generic drivers if a FB is already registered" This reverts commit fb561bf9abde49f7e00fdbf9ed2ccf2d86cac8ee. With commit 27599aacbaefcbf2af7b06b0029459bbf682000d Author: Thomas Zimmermann Date: Tue Jan 25 10:12:18 2022 +0100 fbdev: Hot-unplug firmware fb devices on forced removal this should be fixed properly and we can remove this somewhat hackish check here (e.g. this won't catch drm drivers if fbdev emulation isn't enabled). Cc: Thomas Zimmermann Cc: Zack Rusin Cc: Javier Martinez Canillas Cc: Zack Rusin Cc: Hans de Goede Cc: Ilya Trukhanov Signed-off-by: Daniel Vetter Signed-off-by: Daniel Vetter Reviewed-by: Javier Martinez Canillas Cc: Peter Jones Cc: linux-fbdev@vger.kernel.org Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220607182338.344270-5-javierm@redhat.com --- drivers/video/fbdev/efifb.c | 11 ----------- drivers/video/fbdev/simplefb.c | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index ea42ba6445b2..edca3703b964 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -351,17 +351,6 @@ static int efifb_probe(struct platform_device *dev) char *option = NULL; efi_memory_desc_t md; - /* - * Generic drivers must not be registered if a framebuffer exists. - * If a native driver was probed, the display hardware was already - * taken and attempting to use the system framebuffer is dangerous. - */ - if (num_registered_fb > 0) { - dev_err(&dev->dev, - "efifb: a framebuffer is already registered\n"); - return -EINVAL; - } - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled) return -ENODEV; diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 94fc9c6d0411..0ef41173325a 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -413,17 +413,6 @@ static int simplefb_probe(struct platform_device *pdev) struct simplefb_par *par; struct resource *res, *mem; - /* - * Generic drivers must not be registered if a framebuffer exists. - * If a native driver was probed, the display hardware was already - * taken and attempting to use the system framebuffer is dangerous. - */ - if (num_registered_fb > 0) { - dev_err(&pdev->dev, - "simplefb: a framebuffer is already registered\n"); - return -EINVAL; - } - if (fb_get_options("simplefb", NULL)) return -ENODEV; From efeeaefe9be56e8ae5e5b4e9ff6d2275ec977ec5 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Fri, 20 May 2022 16:15:55 +0200 Subject: [PATCH 20/63] drm: Add support for the LogiCVC display controller Introduces a driver for the LogiCVC display controller, a programmable logic controller optimized for use in Xilinx Zynq-7000 SoCs and other Xilinx FPGAs. The controller is mostly configured at logic synthesis time so only a subset of configuration is left for the driver to handle. The following features are implemented and tested: - LVDS 4-bit interface; - RGB565 pixel formats; - Multiple layers and hardware composition; - Layer-wide alpha mode; The following features are implemented but untested: - Other RGB pixel formats; - Layer framebuffer configuration for version 4; - Lowest-layer used as background color; - Per-pixel alpha mode. The following features are not implemented: - YUV pixel formats; - DVI, LVDS 3-bit, ITU656 and camera link interfaces; - External parallel input for layer; - Color-keying; - LUT-based alpha modes. Additional implementation-specific notes: - Panels are only enabled after the first page flip to avoid flashing a white screen. - Depth used in context of the LogiCVC driver only counts color components to match the definition of the synthesis parameters. Support is implemented for both version 3 and 4 of the controller. With version 3, framebuffers are stored in a dedicated contiguous memory area, with a base address hardcoded for each layer. This requires using a dedicated CMA pool registered at the base address and tweaking a few offset-related registers to try to use any buffer allocated from the pool. This is done on a best-effort basis to have the hardware cope with the DRM framebuffer allocation model and there is no guarantee that each buffer allocated by GEM CMA can be used for any layer. In particular, buffers allocated below the base address for a layer are guaranteed not to be configurable for that layer. See the implementation of logicvc_layer_buffer_find_setup for specifics. Version 4 allows configuring each buffer address directly, which guarantees that any buffer can be configured. Signed-off-by: Paul Kocialkowski Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220520141555.1429041-2-paul.kocialkowski@bootlin.com --- MAINTAINERS | 6 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/logicvc/Kconfig | 9 + drivers/gpu/drm/logicvc/Makefile | 9 + drivers/gpu/drm/logicvc/logicvc_crtc.c | 280 +++++++++ drivers/gpu/drm/logicvc/logicvc_crtc.h | 21 + drivers/gpu/drm/logicvc/logicvc_drm.c | 497 ++++++++++++++++ drivers/gpu/drm/logicvc/logicvc_drm.h | 67 +++ drivers/gpu/drm/logicvc/logicvc_interface.c | 214 +++++++ drivers/gpu/drm/logicvc/logicvc_interface.h | 28 + drivers/gpu/drm/logicvc/logicvc_layer.c | 628 ++++++++++++++++++++ drivers/gpu/drm/logicvc/logicvc_layer.h | 64 ++ drivers/gpu/drm/logicvc/logicvc_mode.c | 80 +++ drivers/gpu/drm/logicvc/logicvc_mode.h | 15 + drivers/gpu/drm/logicvc/logicvc_of.c | 185 ++++++ drivers/gpu/drm/logicvc/logicvc_of.h | 46 ++ drivers/gpu/drm/logicvc/logicvc_regs.h | 80 +++ 18 files changed, 2232 insertions(+) create mode 100644 drivers/gpu/drm/logicvc/Kconfig create mode 100644 drivers/gpu/drm/logicvc/Makefile create mode 100644 drivers/gpu/drm/logicvc/logicvc_crtc.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_crtc.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_drm.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_drm.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_interface.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_interface.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_layer.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_layer.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_mode.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_mode.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_of.c create mode 100644 drivers/gpu/drm/logicvc/logicvc_of.h create mode 100644 drivers/gpu/drm/logicvc/logicvc_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index ede21cc48708..4fcf76fbb6d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6212,6 +6212,12 @@ S: Orphan / Obsolete F: drivers/gpu/drm/i810/ F: include/uapi/drm/i810_drm.h +DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER +M: Paul Kocialkowski +S: Supported +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/logicvc/ + DRM DRIVER FOR LVDS PANELS M: Laurent Pinchart L: dri-devel@lists.freedesktop.org diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index e88c497fa010..22e7fa48d693 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -351,6 +351,8 @@ source "drivers/gpu/drm/etnaviv/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" +source "drivers/gpu/drm/logicvc/Kconfig" + source "drivers/gpu/drm/mediatek/Kconfig" source "drivers/gpu/drm/mxsfb/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 15fe3163f822..13ef240b3d2b 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_DRM_STM) += stm/ obj-$(CONFIG_DRM_STI) += sti/ obj-y += imx/ obj-$(CONFIG_DRM_INGENIC) += ingenic/ +obj-$(CONFIG_DRM_LOGICVC) += logicvc/ obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ obj-$(CONFIG_DRM_MESON) += meson/ obj-y += i2c/ diff --git a/drivers/gpu/drm/logicvc/Kconfig b/drivers/gpu/drm/logicvc/Kconfig new file mode 100644 index 000000000000..300b2be07385 --- /dev/null +++ b/drivers/gpu/drm/logicvc/Kconfig @@ -0,0 +1,9 @@ +config DRM_LOGICVC + tristate "LogiCVC DRM" + depends on DRM + depends on OF || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + DRM display driver for the logiCVC programmable logic block from Xylon diff --git a/drivers/gpu/drm/logicvc/Makefile b/drivers/gpu/drm/logicvc/Makefile new file mode 100644 index 000000000000..6e4b01979d38 --- /dev/null +++ b/drivers/gpu/drm/logicvc/Makefile @@ -0,0 +1,9 @@ +logicvc-drm-y += \ + logicvc_crtc.o \ + logicvc_drm.o \ + logicvc_interface.o \ + logicvc_layer.o \ + logicvc_mode.o \ + logicvc_of.o + +obj-$(CONFIG_DRM_LOGICVC) += logicvc-drm.o diff --git a/drivers/gpu/drm/logicvc/logicvc_crtc.c b/drivers/gpu/drm/logicvc/logicvc_crtc.c new file mode 100644 index 000000000000..c94bb9bb456b --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_crtc.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "logicvc_crtc.h" +#include "logicvc_drm.h" +#include "logicvc_interface.h" +#include "logicvc_layer.h" +#include "logicvc_regs.h" + +#define logicvc_crtc(c) \ + container_of(c, struct logicvc_crtc, drm_crtc) + +static enum drm_mode_status +logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc, + const struct drm_display_mode *mode) +{ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return -EINVAL; + + return 0; +} + +static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc, + struct drm_atomic_state *state) +{ + struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc); + struct drm_crtc_state *old_state = + drm_atomic_get_old_crtc_state(state, drm_crtc); + struct drm_device *drm_dev = drm_crtc->dev; + unsigned long flags; + + /* + * We need to grab the pending event here if vblank was already enabled + * since we won't get a call to atomic_enable to grab it. + */ + if (drm_crtc->state->event && old_state->active) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0); + + crtc->event = drm_crtc->state->event; + drm_crtc->state->event = NULL; + + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + } +} + +static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc, + struct drm_atomic_state *state) +{ + struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc); + struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev); + struct drm_crtc_state *old_state = + drm_atomic_get_old_crtc_state(state, drm_crtc); + struct drm_crtc_state *new_state = + drm_atomic_get_new_crtc_state(state, drm_crtc); + struct drm_display_mode *mode = &new_state->adjusted_mode; + + struct drm_device *drm_dev = drm_crtc->dev; + unsigned int hact, hfp, hsl, hbp; + unsigned int vact, vfp, vsl, vbp; + unsigned long flags; + u32 ctrl; + + /* Timings */ + + hact = mode->hdisplay; + hfp = mode->hsync_start - mode->hdisplay; + hsl = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vact = mode->vdisplay; + vfp = mode->vsync_start - mode->vdisplay; + vsl = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1); + regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1); + regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1); + regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1); + + regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1); + regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1); + regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1); + regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1); + + /* Signals */ + + ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE | + LOGICVC_CTRL_DE_ENABLE; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= LOGICVC_CTRL_HSYNC_INVERT; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= LOGICVC_CTRL_VSYNC_INVERT; + + if (logicvc->interface) { + struct drm_connector *connector = + &logicvc->interface->drm_connector; + struct drm_display_info *display_info = + &connector->display_info; + + if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW) + ctrl |= LOGICVC_CTRL_DE_INVERT; + + if (display_info->bus_flags & + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) + ctrl |= LOGICVC_CTRL_CLOCK_INVERT; + } + + regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG, + LOGICVC_CTRL_HSYNC_ENABLE | + LOGICVC_CTRL_HSYNC_INVERT | + LOGICVC_CTRL_VSYNC_ENABLE | + LOGICVC_CTRL_VSYNC_INVERT | + LOGICVC_CTRL_DE_ENABLE | + LOGICVC_CTRL_DE_INVERT | + LOGICVC_CTRL_PIXEL_INVERT | + LOGICVC_CTRL_CLOCK_INVERT, ctrl); + + /* Generate internal state reset. */ + regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0); + + drm_crtc_vblank_on(drm_crtc); + + /* Register our event after vblank is enabled. */ + if (drm_crtc->state->event && !old_state->active) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0); + + crtc->event = drm_crtc->state->event; + drm_crtc->state->event = NULL; + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + } +} + +static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc, + struct drm_atomic_state *state) +{ + struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev); + struct drm_device *drm_dev = drm_crtc->dev; + + drm_crtc_vblank_off(drm_crtc); + + /* Disable and clear CRTC bits. */ + regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG, + LOGICVC_CTRL_HSYNC_ENABLE | + LOGICVC_CTRL_HSYNC_INVERT | + LOGICVC_CTRL_VSYNC_ENABLE | + LOGICVC_CTRL_VSYNC_INVERT | + LOGICVC_CTRL_DE_ENABLE | + LOGICVC_CTRL_DE_INVERT | + LOGICVC_CTRL_PIXEL_INVERT | + LOGICVC_CTRL_CLOCK_INVERT, 0); + + /* Generate internal state reset. */ + regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0); + + /* Consume any leftover event since vblank is now disabled. */ + if (drm_crtc->state->event && !drm_crtc->state->active) { + spin_lock_irq(&drm_dev->event_lock); + + drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event); + drm_crtc->state->event = NULL; + spin_unlock_irq(&drm_dev->event_lock); + } +} + +static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = { + .mode_valid = logicvc_crtc_mode_valid, + .atomic_begin = logicvc_crtc_atomic_begin, + .atomic_enable = logicvc_crtc_atomic_enable, + .atomic_disable = logicvc_crtc_atomic_disable, +}; + +static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc) +{ + struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev); + + /* Clear any pending V_SYNC interrupt. */ + regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG, + LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC); + + /* Unmask V_SYNC interrupt. */ + regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG, + LOGICVC_INT_MASK_V_SYNC, 0); + + return 0; +} + +static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc) +{ + struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev); + + /* Mask V_SYNC interrupt. */ + regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG, + LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC); +} + +static const struct drm_crtc_funcs logicvc_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = logicvc_crtc_enable_vblank, + .disable_vblank = logicvc_crtc_disable_vblank, +}; + +void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct logicvc_crtc *crtc = logicvc->crtc; + unsigned long flags; + + if (!crtc) + return; + + drm_crtc_handle_vblank(&crtc->drm_crtc); + + if (crtc->event) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event); + drm_crtc_vblank_put(&crtc->drm_crtc); + crtc->event = NULL; + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + } +} + +int logicvc_crtc_init(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + struct device_node *of_node = dev->of_node; + struct logicvc_crtc *crtc; + struct logicvc_layer *layer_primary; + int ret; + + crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + layer_primary = logicvc_layer_get_primary(logicvc); + if (!layer_primary) { + drm_err(drm_dev, "Failed to get primary layer\n"); + return -EINVAL; + } + + ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc, + &layer_primary->drm_plane, NULL, + &logicvc_crtc_funcs, NULL); + if (ret) { + drm_err(drm_dev, "Failed to initialize CRTC\n"); + return ret; + } + + drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs); + + crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1); + + logicvc->crtc = crtc; + + return 0; +} diff --git a/drivers/gpu/drm/logicvc/logicvc_crtc.h b/drivers/gpu/drm/logicvc/logicvc_crtc.h new file mode 100644 index 000000000000..b122901f2936 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_crtc.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_CRTC_H_ +#define _LOGICVC_CRTC_H_ + +struct drm_pending_vblank_event; +struct logicvc_drm; + +struct logicvc_crtc { + struct drm_crtc drm_crtc; + struct drm_pending_vblank_event *event; +}; + +void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc); +int logicvc_crtc_init(struct logicvc_drm *logicvc); + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c b/drivers/gpu/drm/logicvc/logicvc_drm.c new file mode 100644 index 000000000000..df1805cf0f95 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_drm.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "logicvc_crtc.h" +#include "logicvc_drm.h" +#include "logicvc_interface.h" +#include "logicvc_mode.h" +#include "logicvc_layer.h" +#include "logicvc_of.h" +#include "logicvc_regs.h" + +DEFINE_DRM_GEM_CMA_FOPS(logicvc_drm_fops); + +static int logicvc_drm_gem_cma_dumb_create(struct drm_file *file_priv, + struct drm_device *drm_dev, + struct drm_mode_create_dumb *args) +{ + struct logicvc_drm *logicvc = logicvc_drm(drm_dev); + + /* Stride is always fixed to its configuration value. */ + args->pitch = logicvc->config.row_stride * DIV_ROUND_UP(args->bpp, 8); + + return drm_gem_cma_dumb_create_internal(file_priv, drm_dev, args); +} + +static struct drm_driver logicvc_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_ATOMIC, + + .fops = &logicvc_drm_fops, + .name = "logicvc-drm", + .desc = "Xylon LogiCVC DRM driver", + .date = "20200403", + .major = 1, + .minor = 0, + + DRM_GEM_CMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_cma_dumb_create), +}; + +static struct regmap_config logicvc_drm_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .name = "logicvc-drm", +}; + +static irqreturn_t logicvc_drm_irq_handler(int irq, void *data) +{ + struct logicvc_drm *logicvc = data; + irqreturn_t ret = IRQ_NONE; + u32 stat = 0; + + /* Get pending interrupt sources. */ + regmap_read(logicvc->regmap, LOGICVC_INT_STAT_REG, &stat); + + /* Clear all pending interrupt sources. */ + regmap_write(logicvc->regmap, LOGICVC_INT_STAT_REG, stat); + + if (stat & LOGICVC_INT_STAT_V_SYNC) { + logicvc_crtc_vblank_handler(logicvc); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int logicvc_drm_config_parse(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + struct device_node *of_node = dev->of_node; + struct logicvc_drm_config *config = &logicvc->config; + struct device_node *layers_node; + int ret; + + logicvc_of_property_parse_bool(of_node, LOGICVC_OF_PROPERTY_DITHERING, + &config->dithering); + logicvc_of_property_parse_bool(of_node, + LOGICVC_OF_PROPERTY_BACKGROUND_LAYER, + &config->background_layer); + logicvc_of_property_parse_bool(of_node, + LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE, + &config->layers_configurable); + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE, + &config->display_interface); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE, + &config->display_colorspace); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_DISPLAY_DEPTH, + &config->display_depth); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_ROW_STRIDE, + &config->row_stride); + if (ret) + return ret; + + layers_node = of_get_child_by_name(of_node, "layers"); + if (!layers_node) { + drm_err(drm_dev, "Missing non-optional layers node\n"); + return -EINVAL; + } + + config->layers_count = of_get_child_count(layers_node); + if (!config->layers_count) { + drm_err(drm_dev, + "Missing a non-optional layers children node\n"); + return -EINVAL; + } + + return 0; +} + +static int logicvc_clocks_prepare(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + + struct { + struct clk **clk; + char *name; + bool optional; + } clocks_map[] = { + { + .clk = &logicvc->vclk, + .name = "vclk", + .optional = false, + }, + { + .clk = &logicvc->vclk2, + .name = "vclk2", + .optional = true, + }, + { + .clk = &logicvc->lvdsclk, + .name = "lvdsclk", + .optional = true, + }, + { + .clk = &logicvc->lvdsclkn, + .name = "lvdsclkn", + .optional = true, + }, + }; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(clocks_map); i++) { + struct clk *clk; + + clk = devm_clk_get(dev, clocks_map[i].name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -ENOENT && clocks_map[i].optional) + continue; + + drm_err(drm_dev, "Missing non-optional clock %s\n", + clocks_map[i].name); + + ret = PTR_ERR(clk); + goto error; + } + + ret = clk_prepare_enable(clk); + if (ret) { + drm_err(drm_dev, + "Failed to prepare and enable clock %s\n", + clocks_map[i].name); + goto error; + } + + *clocks_map[i].clk = clk; + } + + return 0; + +error: + for (i = 0; i < ARRAY_SIZE(clocks_map); i++) { + if (!*clocks_map[i].clk) + continue; + + clk_disable_unprepare(*clocks_map[i].clk); + *clocks_map[i].clk = NULL; + } + + return ret; +} + +static int logicvc_clocks_unprepare(struct logicvc_drm *logicvc) +{ + struct clk **clocks[] = { + &logicvc->vclk, + &logicvc->vclk2, + &logicvc->lvdsclk, + &logicvc->lvdsclkn, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(clocks); i++) { + if (!*clocks[i]) + continue; + + clk_disable_unprepare(*clocks[i]); + *clocks[i] = NULL; + } + + return 0; +} + +static const struct logicvc_drm_caps logicvc_drm_caps[] = { + { + .major = 3, + .layer_address = false, + }, + { + .major = 4, + .layer_address = true, + }, + { + .major = 5, + .layer_address = true, + }, +}; + +static const struct logicvc_drm_caps * +logicvc_drm_caps_match(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + const struct logicvc_drm_caps *caps = NULL; + unsigned int major, minor; + char level; + unsigned int i; + u32 version; + + regmap_read(logicvc->regmap, LOGICVC_IP_VERSION_REG, &version); + + major = FIELD_GET(LOGICVC_IP_VERSION_MAJOR_MASK, version); + minor = FIELD_GET(LOGICVC_IP_VERSION_MINOR_MASK, version); + level = FIELD_GET(LOGICVC_IP_VERSION_LEVEL_MASK, version) + 'a'; + + for (i = 0; i < ARRAY_SIZE(logicvc_drm_caps); i++) { + if (logicvc_drm_caps[i].major && + logicvc_drm_caps[i].major != major) + continue; + + if (logicvc_drm_caps[i].minor && + logicvc_drm_caps[i].minor != minor) + continue; + + if (logicvc_drm_caps[i].level && + logicvc_drm_caps[i].level != level) + continue; + + caps = &logicvc_drm_caps[i]; + } + + drm_info(drm_dev, "LogiCVC version %d.%02d.%c\n", major, minor, level); + + return caps; +} + +static int logicvc_drm_probe(struct platform_device *pdev) +{ + struct device_node *of_node = pdev->dev.of_node; + struct device_node *reserved_mem_node; + struct reserved_mem *reserved_mem = NULL; + const struct logicvc_drm_caps *caps; + struct logicvc_drm *logicvc; + struct device *dev = &pdev->dev; + struct drm_device *drm_dev; + struct regmap *regmap; + struct resource res; + void __iomem *base; + int irq; + int ret; + + ret = of_reserved_mem_device_init(dev); + if (ret && ret != -ENODEV) { + dev_err(dev, "Failed to init memory region\n"); + goto error_early; + } + + reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0); + if (reserved_mem_node) { + reserved_mem = of_reserved_mem_lookup(reserved_mem_node); + of_node_put(reserved_mem_node); + } + + /* Get regmap from parent if available. */ + if (of_node->parent) + regmap = syscon_node_to_regmap(of_node->parent); + + /* Register our own regmap otherwise. */ + if (IS_ERR_OR_NULL(regmap)) { + ret = of_address_to_resource(of_node, 0, &res); + if (ret) { + dev_err(dev, "Failed to get resource from address\n"); + goto error_reserved_mem; + } + + base = devm_ioremap_resource(dev, &res); + if (IS_ERR(base)) { + dev_err(dev, "Failed to map I/O base\n"); + ret = PTR_ERR(base); + goto error_reserved_mem; + } + + logicvc_drm_regmap_config.max_register = resource_size(&res) - + 4; + + regmap = devm_regmap_init_mmio(dev, base, + &logicvc_drm_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to create regmap for I/O\n"); + ret = PTR_ERR(regmap); + goto error_reserved_mem; + } + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Failed to get IRQ\n"); + ret = -ENODEV; + goto error_reserved_mem; + } + + logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver, + struct logicvc_drm, drm_dev); + if (IS_ERR(logicvc)) { + ret = PTR_ERR(logicvc); + goto error_reserved_mem; + } + + platform_set_drvdata(pdev, logicvc); + drm_dev = &logicvc->drm_dev; + + logicvc->regmap = regmap; + INIT_LIST_HEAD(&logicvc->layers_list); + + caps = logicvc_drm_caps_match(logicvc); + if (!caps) { + ret = -EINVAL; + goto error_reserved_mem; + } + + logicvc->caps = caps; + + if (reserved_mem) + logicvc->reserved_mem_base = reserved_mem->base; + + ret = logicvc_clocks_prepare(logicvc); + if (ret) { + drm_err(drm_dev, "Failed to prepare clocks\n"); + goto error_reserved_mem; + } + + ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0, + dev_name(dev), logicvc); + if (ret) { + drm_err(drm_dev, "Failed to request IRQ\n"); + goto error_clocks; + } + + ret = logicvc_drm_config_parse(logicvc); + if (ret && ret != -ENODEV) { + drm_err(drm_dev, "Failed to parse config\n"); + goto error_clocks; + } + + ret = drmm_mode_config_init(drm_dev); + if (ret) { + drm_err(drm_dev, "Failed to init mode config\n"); + goto error_clocks; + } + + ret = logicvc_layers_init(logicvc); + if (ret) { + drm_err(drm_dev, "Failed to initialize layers\n"); + goto error_clocks; + } + + ret = logicvc_crtc_init(logicvc); + if (ret) { + drm_err(drm_dev, "Failed to initialize CRTC\n"); + goto error_clocks; + } + + logicvc_layers_attach_crtc(logicvc); + + ret = logicvc_interface_init(logicvc); + if (ret) { + if (ret != -EPROBE_DEFER) + drm_err(drm_dev, "Failed to initialize interface\n"); + + goto error_clocks; + } + + logicvc_interface_attach_crtc(logicvc); + + ret = logicvc_mode_init(logicvc); + if (ret) { + drm_err(drm_dev, "Failed to initialize KMS\n"); + goto error_clocks; + } + + ret = drm_dev_register(drm_dev, 0); + if (ret) { + drm_err(drm_dev, "Failed to register DRM device\n"); + goto error_mode; + } + + drm_fbdev_generic_setup(drm_dev, drm_dev->mode_config.preferred_depth); + + return 0; + +error_mode: + logicvc_mode_fini(logicvc); + +error_clocks: + logicvc_clocks_unprepare(logicvc); + +error_reserved_mem: + of_reserved_mem_device_release(dev); + +error_early: + return ret; +} + +static int logicvc_drm_remove(struct platform_device *pdev) +{ + struct logicvc_drm *logicvc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct drm_device *drm_dev = &logicvc->drm_dev; + + drm_dev_unregister(drm_dev); + drm_atomic_helper_shutdown(drm_dev); + + logicvc_mode_fini(logicvc); + + logicvc_clocks_unprepare(logicvc); + + of_reserved_mem_device_release(dev); + + return 0; +} + +static const struct of_device_id logicvc_drm_of_table[] = { + { .compatible = "xylon,logicvc-3.02.a-display" }, + { .compatible = "xylon,logicvc-4.01.a-display" }, + {}, +}; +MODULE_DEVICE_TABLE(of, logicvc_drm_of_table); + +static struct platform_driver logicvc_drm_platform_driver = { + .probe = logicvc_drm_probe, + .remove = logicvc_drm_remove, + .driver = { + .name = "logicvc-drm", + .of_match_table = logicvc_drm_of_table, + }, +}; + +module_platform_driver(logicvc_drm_platform_driver); + +MODULE_AUTHOR("Paul Kocialkowski "); +MODULE_DESCRIPTION("Xylon LogiCVC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.h b/drivers/gpu/drm/logicvc/logicvc_drm.h new file mode 100644 index 000000000000..e0f4787c69f9 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_drm.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_DRM_H_ +#define _LOGICVC_DRM_H_ + +#include +#include +#include + +#define LOGICVC_DISPLAY_INTERFACE_RGB 0 +#define LOGICVC_DISPLAY_INTERFACE_ITU656 1 +#define LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS 2 +#define LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA 3 +#define LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS 4 +#define LOGICVC_DISPLAY_INTERFACE_DVI 5 + +#define LOGICVC_DISPLAY_COLORSPACE_RGB 0 +#define LOGICVC_DISPLAY_COLORSPACE_YUV422 1 +#define LOGICVC_DISPLAY_COLORSPACE_YUV444 2 + +#define logicvc_drm(d) \ + container_of(d, struct logicvc_drm, drm_dev) + +struct logicvc_crtc; +struct logicvc_interface; + +struct logicvc_drm_config { + u32 display_interface; + u32 display_colorspace; + u32 display_depth; + u32 row_stride; + bool dithering; + bool background_layer; + bool layers_configurable; + u32 layers_count; +}; + +struct logicvc_drm_caps { + unsigned int major; + unsigned int minor; + char level; + bool layer_address; +}; + +struct logicvc_drm { + const struct logicvc_drm_caps *caps; + struct logicvc_drm_config config; + + struct drm_device drm_dev; + phys_addr_t reserved_mem_base; + struct regmap *regmap; + + struct clk *vclk; + struct clk *vclk2; + struct clk *lvdsclk; + struct clk *lvdsclkn; + + struct list_head layers_list; + struct logicvc_crtc *crtc; + struct logicvc_interface *interface; +}; + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.c b/drivers/gpu/drm/logicvc/logicvc_interface.c new file mode 100644 index 000000000000..c73592f6c406 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_interface.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logicvc_crtc.h" +#include "logicvc_drm.h" +#include "logicvc_interface.h" +#include "logicvc_regs.h" + +#define logicvc_interface_from_drm_encoder(c) \ + container_of(c, struct logicvc_interface, drm_encoder) +#define logicvc_interface_from_drm_connector(c) \ + container_of(c, struct logicvc_interface, drm_connector) + +static void logicvc_encoder_enable(struct drm_encoder *drm_encoder) +{ + struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev); + struct logicvc_interface *interface = + logicvc_interface_from_drm_encoder(drm_encoder); + + regmap_update_bits(logicvc->regmap, LOGICVC_POWER_CTRL_REG, + LOGICVC_POWER_CTRL_VIDEO_ENABLE, + LOGICVC_POWER_CTRL_VIDEO_ENABLE); + + if (interface->drm_panel) { + drm_panel_prepare(interface->drm_panel); + drm_panel_enable(interface->drm_panel); + } +} + +static void logicvc_encoder_disable(struct drm_encoder *drm_encoder) +{ + struct logicvc_interface *interface = + logicvc_interface_from_drm_encoder(drm_encoder); + + if (interface->drm_panel) { + drm_panel_disable(interface->drm_panel); + drm_panel_unprepare(interface->drm_panel); + } +} + +static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = { + .enable = logicvc_encoder_enable, + .disable = logicvc_encoder_disable, +}; + +static const struct drm_encoder_funcs logicvc_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int logicvc_connector_get_modes(struct drm_connector *drm_connector) +{ + struct logicvc_interface *interface = + logicvc_interface_from_drm_connector(drm_connector); + + if (interface->drm_panel) + return drm_panel_get_modes(interface->drm_panel, drm_connector); + + WARN_ONCE(1, "Retrieving modes from a native connector is not implemented."); + + return 0; +} + +static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = { + .get_modes = logicvc_connector_get_modes, +}; + +static const struct drm_connector_funcs logicvc_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int logicvc_interface_encoder_type(struct logicvc_drm *logicvc) +{ + switch (logicvc->config.display_interface) { + case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS: + case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA: + case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS: + return DRM_MODE_ENCODER_LVDS; + case LOGICVC_DISPLAY_INTERFACE_DVI: + return DRM_MODE_ENCODER_TMDS; + case LOGICVC_DISPLAY_INTERFACE_RGB: + return DRM_MODE_ENCODER_DPI; + default: + return DRM_MODE_ENCODER_NONE; + } +} + +static int logicvc_interface_connector_type(struct logicvc_drm *logicvc) +{ + switch (logicvc->config.display_interface) { + case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS: + case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA: + case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS: + return DRM_MODE_CONNECTOR_LVDS; + case LOGICVC_DISPLAY_INTERFACE_DVI: + return DRM_MODE_CONNECTOR_DVID; + case LOGICVC_DISPLAY_INTERFACE_RGB: + return DRM_MODE_CONNECTOR_DPI; + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} + +static bool logicvc_interface_native_connector(struct logicvc_drm *logicvc) +{ + switch (logicvc->config.display_interface) { + case LOGICVC_DISPLAY_INTERFACE_DVI: + return true; + default: + return false; + } +} + +void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc) +{ + uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc); + + logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs; +} + +int logicvc_interface_init(struct logicvc_drm *logicvc) +{ + struct logicvc_interface *interface; + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + struct device_node *of_node = dev->of_node; + int encoder_type = logicvc_interface_encoder_type(logicvc); + int connector_type = logicvc_interface_connector_type(logicvc); + bool native_connector = logicvc_interface_native_connector(logicvc); + int ret; + + interface = devm_kzalloc(dev, sizeof(*interface), GFP_KERNEL); + if (!interface) { + ret = -ENOMEM; + goto error_early; + } + + ret = drm_of_find_panel_or_bridge(of_node, 0, 0, &interface->drm_panel, + &interface->drm_bridge); + if (ret == -EPROBE_DEFER) + goto error_early; + + ret = drm_encoder_init(drm_dev, &interface->drm_encoder, + &logicvc_encoder_funcs, encoder_type, NULL); + if (ret) { + drm_err(drm_dev, "Failed to initialize encoder\n"); + goto error_early; + } + + drm_encoder_helper_add(&interface->drm_encoder, + &logicvc_encoder_helper_funcs); + + if (native_connector || interface->drm_panel) { + ret = drm_connector_init(drm_dev, &interface->drm_connector, + &logicvc_connector_funcs, + connector_type); + if (ret) { + drm_err(drm_dev, "Failed to initialize connector\n"); + goto error_encoder; + } + + drm_connector_helper_add(&interface->drm_connector, + &logicvc_connector_helper_funcs); + + ret = drm_connector_attach_encoder(&interface->drm_connector, + &interface->drm_encoder); + if (ret) { + drm_err(drm_dev, + "Failed to attach connector to encoder\n"); + goto error_encoder; + } + } + + if (interface->drm_bridge) { + ret = drm_bridge_attach(&interface->drm_encoder, + interface->drm_bridge, NULL, 0); + if (ret) { + drm_err(drm_dev, + "Failed to attach bridge to encoder\n"); + goto error_encoder; + } + } + + logicvc->interface = interface; + + return 0; + +error_encoder: + drm_encoder_cleanup(&interface->drm_encoder); + +error_early: + return ret; +} diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.h b/drivers/gpu/drm/logicvc/logicvc_interface.h new file mode 100644 index 000000000000..fd709fad54f9 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_interface.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_INTERFACE_H_ +#define _LOGICVC_INTERFACE_H_ + +#include +#include +#include +#include + +struct logicvc_drm; + +struct logicvc_interface { + struct drm_encoder drm_encoder; + struct drm_connector drm_connector; + + struct drm_panel *drm_panel; + struct drm_bridge *drm_bridge; +}; + +void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc); +int logicvc_interface_init(struct logicvc_drm *logicvc); + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_layer.c b/drivers/gpu/drm/logicvc/logicvc_layer.c new file mode 100644 index 000000000000..bae1c7f99569 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_layer.c @@ -0,0 +1,628 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "logicvc_crtc.h" +#include "logicvc_drm.h" +#include "logicvc_layer.h" +#include "logicvc_of.h" +#include "logicvc_regs.h" + +#define logicvc_layer(p) \ + container_of(p, struct logicvc_layer, drm_plane) + +static uint32_t logicvc_layer_formats_rgb16[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_INVALID, +}; + +static uint32_t logicvc_layer_formats_rgb24[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_INVALID, +}; + +/* + * What we call depth in this driver only counts color components, not alpha. + * This allows us to stay compatible with the LogiCVC bistream definitions. + */ +static uint32_t logicvc_layer_formats_rgb24_alpha[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_INVALID, +}; + +static struct logicvc_layer_formats logicvc_layer_formats[] = { + { + .colorspace = LOGICVC_LAYER_COLORSPACE_RGB, + .depth = 16, + .formats = logicvc_layer_formats_rgb16, + }, + { + .colorspace = LOGICVC_LAYER_COLORSPACE_RGB, + .depth = 24, + .formats = logicvc_layer_formats_rgb24, + }, + { + .colorspace = LOGICVC_LAYER_COLORSPACE_RGB, + .depth = 24, + .alpha = true, + .formats = logicvc_layer_formats_rgb24_alpha, + }, + { } +}; + +static bool logicvc_layer_format_inverted(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_BGR565: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return true; + default: + return false; + } +} + +static int logicvc_plane_atomic_check(struct drm_plane *drm_plane, + struct drm_atomic_state *state) +{ + struct drm_device *drm_dev = drm_plane->dev; + struct logicvc_layer *layer = logicvc_layer(drm_plane); + struct logicvc_drm *logicvc = logicvc_drm(drm_dev); + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, drm_plane); + struct drm_crtc_state *crtc_state; + int min_scale, max_scale; + bool can_position; + int ret; + + if (!new_state->crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(new_state->state, + new_state->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + if (new_state->crtc_x < 0 || new_state->crtc_y < 0) { + drm_err(drm_dev, + "Negative on-CRTC positions are not supported.\n"); + return -EINVAL; + } + + if (!logicvc->caps->layer_address) { + ret = logicvc_layer_buffer_find_setup(logicvc, layer, new_state, + NULL); + if (ret) { + drm_err(drm_dev, "No viable setup for buffer found.\n"); + return ret; + } + } + + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + + can_position = (drm_plane->type == DRM_PLANE_TYPE_OVERLAY && + layer->index != (logicvc->config.layers_count - 1) && + logicvc->config.layers_configurable); + + ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, + min_scale, max_scale, + can_position, true); + if (ret) { + drm_err(drm_dev, "Invalid plane state\n\n"); + return ret; + } + + return 0; +} + +static void logicvc_plane_atomic_update(struct drm_plane *drm_plane, + struct drm_atomic_state *state) +{ + struct logicvc_layer *layer = logicvc_layer(drm_plane); + struct logicvc_drm *logicvc = logicvc_drm(drm_plane->dev); + struct drm_device *drm_dev = &logicvc->drm_dev; + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, drm_plane); + struct drm_crtc *drm_crtc = &logicvc->crtc->drm_crtc; + struct drm_display_mode *mode = &drm_crtc->state->adjusted_mode; + struct drm_framebuffer *fb = new_state->fb; + struct logicvc_layer_buffer_setup setup = {}; + u32 index = layer->index; + u32 reg; + + /* Layer dimensions */ + + regmap_write(logicvc->regmap, LOGICVC_LAYER_WIDTH_REG(index), + new_state->crtc_w - 1); + regmap_write(logicvc->regmap, LOGICVC_LAYER_HEIGHT_REG(index), + new_state->crtc_h - 1); + + if (logicvc->caps->layer_address) { + phys_addr_t fb_addr = drm_fb_cma_get_gem_addr(fb, new_state, 0); + + regmap_write(logicvc->regmap, LOGICVC_LAYER_ADDRESS_REG(index), + fb_addr); + } else { + /* Rely on offsets to configure the address. */ + + logicvc_layer_buffer_find_setup(logicvc, layer, new_state, + &setup); + + /* Layer memory offsets */ + + regmap_write(logicvc->regmap, LOGICVC_BUFFER_SEL_REG, + LOGICVC_BUFFER_SEL_VALUE(index, setup.buffer_sel)); + regmap_write(logicvc->regmap, LOGICVC_LAYER_HOFFSET_REG(index), + setup.hoffset); + regmap_write(logicvc->regmap, LOGICVC_LAYER_VOFFSET_REG(index), + setup.voffset); + } + + /* Layer position */ + + regmap_write(logicvc->regmap, LOGICVC_LAYER_HPOSITION_REG(index), + mode->hdisplay - 1 - new_state->crtc_x); + + /* Vertical position must be set last to sync layer register changes. */ + regmap_write(logicvc->regmap, LOGICVC_LAYER_VPOSITION_REG(index), + mode->vdisplay - 1 - new_state->crtc_y); + + /* Layer alpha */ + + if (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_LAYER) { + u32 alpha_bits; + u32 alpha_max; + u32 alpha; + + switch (layer->config.depth) { + case 8: + alpha_bits = 3; + break; + case 16: + if (layer->config.colorspace == + LOGICVC_LAYER_COLORSPACE_YUV) + alpha_bits = 8; + else + alpha_bits = 6; + break; + default: + alpha_bits = 8; + break; + } + + alpha_max = BIT(alpha_bits) - 1; + alpha = new_state->alpha * alpha_max / DRM_BLEND_ALPHA_OPAQUE; + + drm_dbg_kms(drm_dev, "Setting layer %d alpha to %d/%d\n", index, + alpha, alpha_max); + + regmap_write(logicvc->regmap, LOGICVC_LAYER_ALPHA_REG(index), + alpha); + } + + /* Layer control */ + + reg = LOGICVC_LAYER_CTRL_ENABLE; + + if (logicvc_layer_format_inverted(fb->format->format)) + reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_INVERT; + + reg |= LOGICVC_LAYER_CTRL_COLOR_KEY_DISABLE; + + regmap_write(logicvc->regmap, LOGICVC_LAYER_CTRL_REG(index), reg); +} + +static void logicvc_plane_atomic_disable(struct drm_plane *drm_plane, + struct drm_atomic_state *state) +{ + struct logicvc_layer *layer = logicvc_layer(drm_plane); + struct logicvc_drm *logicvc = logicvc_drm(drm_plane->dev); + u32 index = layer->index; + + regmap_write(logicvc->regmap, LOGICVC_LAYER_CTRL_REG(index), 0); +} + +static struct drm_plane_helper_funcs logicvc_plane_helper_funcs = { + .atomic_check = logicvc_plane_atomic_check, + .atomic_update = logicvc_plane_atomic_update, + .atomic_disable = logicvc_plane_atomic_disable, +}; + +static const struct drm_plane_funcs logicvc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +int logicvc_layer_buffer_find_setup(struct logicvc_drm *logicvc, + struct logicvc_layer *layer, + struct drm_plane_state *state, + struct logicvc_layer_buffer_setup *setup) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct drm_framebuffer *fb = state->fb; + /* All the supported formats have a single data plane. */ + u32 layer_bytespp = fb->format->cpp[0]; + u32 layer_stride = layer_bytespp * logicvc->config.row_stride; + u32 base_offset = layer->config.base_offset * layer_stride; + u32 buffer_offset = layer->config.buffer_offset * layer_stride; + u8 buffer_sel = 0; + u16 voffset = 0; + u16 hoffset = 0; + phys_addr_t fb_addr; + u32 fb_offset; + u32 gap; + + if (!logicvc->reserved_mem_base) { + drm_err(drm_dev, "No reserved memory base was registered!\n"); + return -ENOMEM; + } + + fb_addr = drm_fb_cma_get_gem_addr(fb, state, 0); + if (fb_addr < logicvc->reserved_mem_base) { + drm_err(drm_dev, + "Framebuffer memory below reserved memory base!\n"); + return -EINVAL; + } + + fb_offset = (u32) (fb_addr - logicvc->reserved_mem_base); + + if (fb_offset < base_offset) { + drm_err(drm_dev, + "Framebuffer offset below layer base offset!\n"); + return -EINVAL; + } + + gap = fb_offset - base_offset; + + /* Use the possible video buffers selection. */ + if (gap && buffer_offset) { + buffer_sel = gap / buffer_offset; + if (buffer_sel > LOGICVC_BUFFER_SEL_MAX) + buffer_sel = LOGICVC_BUFFER_SEL_MAX; + + gap -= buffer_sel * buffer_offset; + } + + /* Use the vertical offset. */ + if (gap && layer_stride && logicvc->config.layers_configurable) { + voffset = gap / layer_stride; + if (voffset > LOGICVC_LAYER_VOFFSET_MAX) + voffset = LOGICVC_LAYER_VOFFSET_MAX; + + gap -= voffset * layer_stride; + } + + /* Use the horizontal offset. */ + if (gap && layer_bytespp && logicvc->config.layers_configurable) { + hoffset = gap / layer_bytespp; + if (hoffset > LOGICVC_DIMENSIONS_MAX) + hoffset = LOGICVC_DIMENSIONS_MAX; + + gap -= hoffset * layer_bytespp; + } + + if (gap) { + drm_err(drm_dev, + "Unable to find layer %d buffer setup for 0x%x byte gap\n", + layer->index, fb_offset - base_offset); + return -EINVAL; + } + + drm_dbg_kms(drm_dev, "Found layer %d buffer setup for 0x%x byte gap:\n", + layer->index, fb_offset - base_offset); + + drm_dbg_kms(drm_dev, "- buffer_sel = 0x%x chunks of 0x%x bytes\n", + buffer_sel, buffer_offset); + drm_dbg_kms(drm_dev, "- voffset = 0x%x chunks of 0x%x bytes\n", voffset, + layer_stride); + drm_dbg_kms(drm_dev, "- hoffset = 0x%x chunks of 0x%x bytes\n", hoffset, + layer_bytespp); + + if (setup) { + setup->buffer_sel = buffer_sel; + setup->voffset = voffset; + setup->hoffset = hoffset; + } + + return 0; +} + +static struct logicvc_layer_formats *logicvc_layer_formats_lookup(struct logicvc_layer *layer) +{ + bool alpha; + unsigned int i = 0; + + alpha = (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_PIXEL); + + while (logicvc_layer_formats[i].formats) { + if (logicvc_layer_formats[i].colorspace == layer->config.colorspace && + logicvc_layer_formats[i].depth == layer->config.depth && + logicvc_layer_formats[i].alpha == alpha) + return &logicvc_layer_formats[i]; + + i++; + } + + return NULL; +} + +static unsigned int logicvc_layer_formats_count(struct logicvc_layer_formats *formats) +{ + unsigned int count = 0; + + while (formats->formats[count] != DRM_FORMAT_INVALID) + count++; + + return count; +} + +static int logicvc_layer_config_parse(struct logicvc_drm *logicvc, + struct logicvc_layer *layer) +{ + struct device_node *of_node = layer->of_node; + struct logicvc_layer_config *config = &layer->config; + int ret; + + logicvc_of_property_parse_bool(of_node, + LOGICVC_OF_PROPERTY_LAYER_PRIMARY, + &config->primary); + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_LAYER_COLORSPACE, + &config->colorspace); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_LAYER_DEPTH, + &config->depth); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE, + &config->alpha_mode); + if (ret) + return ret; + + /* + * Memory offset is only relevant without layer address configuration. + */ + if (logicvc->caps->layer_address) + return 0; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET, + &config->base_offset); + if (ret) + return ret; + + ret = logicvc_of_property_parse_u32(of_node, + LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET, + &config->buffer_offset); + if (ret) + return ret; + + return 0; +} + +struct logicvc_layer *logicvc_layer_get_from_index(struct logicvc_drm *logicvc, + u32 index) +{ + struct logicvc_layer *layer; + + list_for_each_entry(layer, &logicvc->layers_list, list) + if (layer->index == index) + return layer; + + return NULL; +} + +struct logicvc_layer *logicvc_layer_get_from_type(struct logicvc_drm *logicvc, + enum drm_plane_type type) +{ + struct logicvc_layer *layer; + + list_for_each_entry(layer, &logicvc->layers_list, list) + if (layer->drm_plane.type == type) + return layer; + + return NULL; +} + +struct logicvc_layer *logicvc_layer_get_primary(struct logicvc_drm *logicvc) +{ + return logicvc_layer_get_from_type(logicvc, DRM_PLANE_TYPE_PRIMARY); +} + +static int logicvc_layer_init(struct logicvc_drm *logicvc, + struct device_node *of_node, u32 index) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + struct logicvc_layer *layer = NULL; + struct logicvc_layer_formats *formats; + unsigned int formats_count; + enum drm_plane_type type; + unsigned int zpos; + int ret; + + layer = devm_kzalloc(dev, sizeof(*layer), GFP_KERNEL); + if (!layer) { + ret = -ENOMEM; + goto error; + } + + layer->of_node = of_node; + layer->index = index; + + ret = logicvc_layer_config_parse(logicvc, layer); + if (ret) { + drm_err(drm_dev, "Failed to parse config for layer #%d\n", + index); + goto error; + } + + formats = logicvc_layer_formats_lookup(layer); + if (!formats) { + drm_err(drm_dev, "Failed to lookup formats for layer #%d\n", + index); + goto error; + } + + formats_count = logicvc_layer_formats_count(formats); + + /* The final layer can be configured as a background layer. */ + if (logicvc->config.background_layer && + index == (logicvc->config.layers_count - 1)) { + /* + * A zero value for black is only valid for RGB, not for YUV, + * so this will need to take the format in account for YUV. + */ + u32 background = 0; + + drm_dbg_kms(drm_dev, "Using layer #%d as background layer\n", + index); + + regmap_write(logicvc->regmap, LOGICVC_BACKGROUND_COLOR_REG, + background); + + devm_kfree(dev, layer); + + return 0; + } + + if (layer->config.primary) + type = DRM_PLANE_TYPE_PRIMARY; + else + type = DRM_PLANE_TYPE_OVERLAY; + + ret = drm_universal_plane_init(drm_dev, &layer->drm_plane, 0, + &logicvc_plane_funcs, formats->formats, + formats_count, NULL, type, NULL); + if (ret) { + drm_err(drm_dev, "Failed to initialize layer plane\n"); + return ret; + } + + drm_plane_helper_add(&layer->drm_plane, &logicvc_plane_helper_funcs); + + zpos = logicvc->config.layers_count - index - 1; + drm_dbg_kms(drm_dev, "Giving layer #%d zpos %d\n", index, zpos); + + if (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_LAYER) + drm_plane_create_alpha_property(&layer->drm_plane); + + drm_plane_create_zpos_immutable_property(&layer->drm_plane, zpos); + + drm_dbg_kms(drm_dev, "Registering layer #%d\n", index); + + layer->formats = formats; + + list_add_tail(&layer->list, &logicvc->layers_list); + + return 0; + +error: + if (layer) + devm_kfree(dev, layer); + + return ret; +} + +static void logicvc_layer_fini(struct logicvc_drm *logicvc, + struct logicvc_layer *layer) +{ + struct device *dev = logicvc->drm_dev.dev; + + list_del(&layer->list); + devm_kfree(dev, layer); +} + +void logicvc_layers_attach_crtc(struct logicvc_drm *logicvc) +{ + uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc); + struct logicvc_layer *layer; + + list_for_each_entry(layer, &logicvc->layers_list, list) { + if (layer->drm_plane.type != DRM_PLANE_TYPE_OVERLAY) + continue; + + layer->drm_plane.possible_crtcs = possible_crtcs; + } +} + +int logicvc_layers_init(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct device *dev = drm_dev->dev; + struct device_node *of_node = dev->of_node; + struct device_node *layer_node = NULL; + struct device_node *layers_node; + struct logicvc_layer *layer; + struct logicvc_layer *next; + int ret = 0; + + layers_node = of_get_child_by_name(of_node, "layers"); + if (!layers_node) { + drm_err(drm_dev, "No layers node found in the description\n"); + ret = -ENODEV; + goto error; + } + + for_each_child_of_node(layers_node, layer_node) { + u32 index = 0; + + if (!logicvc_of_node_is_layer(layer_node)) + continue; + + ret = of_property_read_u32(layer_node, "reg", &index); + if (ret) + continue; + + layer = logicvc_layer_get_from_index(logicvc, index); + if (layer) { + drm_err(drm_dev, "Duplicated entry for layer #%d\n", + index); + continue; + } + + ret = logicvc_layer_init(logicvc, layer_node, index); + if (ret) + goto error; + + of_node_put(layer_node); + } + + of_node_put(layers_node); + + return 0; + +error: + list_for_each_entry_safe(layer, next, &logicvc->layers_list, list) + logicvc_layer_fini(logicvc, layer); + + return ret; +} diff --git a/drivers/gpu/drm/logicvc/logicvc_layer.h b/drivers/gpu/drm/logicvc/logicvc_layer.h new file mode 100644 index 000000000000..4a4b02e9b819 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_layer.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_LAYER_H_ +#define _LOGICVC_LAYER_H_ + +#include +#include +#include + +#define LOGICVC_LAYER_COLORSPACE_RGB 0 +#define LOGICVC_LAYER_COLORSPACE_YUV 1 + +#define LOGICVC_LAYER_ALPHA_LAYER 0 +#define LOGICVC_LAYER_ALPHA_PIXEL 1 + +struct logicvc_layer_buffer_setup { + u8 buffer_sel; + u16 voffset; + u16 hoffset; +}; + +struct logicvc_layer_config { + u32 colorspace; + u32 depth; + u32 alpha_mode; + u32 base_offset; + u32 buffer_offset; + bool primary; +}; + +struct logicvc_layer_formats { + u32 colorspace; + u32 depth; + bool alpha; + uint32_t *formats; +}; + +struct logicvc_layer { + struct logicvc_layer_config config; + struct logicvc_layer_formats *formats; + struct device_node *of_node; + + struct drm_plane drm_plane; + struct list_head list; + u32 index; +}; + +int logicvc_layer_buffer_find_setup(struct logicvc_drm *logicvc, + struct logicvc_layer *layer, + struct drm_plane_state *state, + struct logicvc_layer_buffer_setup *setup); +struct logicvc_layer *logicvc_layer_get_from_index(struct logicvc_drm *logicvc, + u32 index); +struct logicvc_layer *logicvc_layer_get_from_type(struct logicvc_drm *logicvc, + enum drm_plane_type type); +struct logicvc_layer *logicvc_layer_get_primary(struct logicvc_drm *logicvc); +void logicvc_layers_attach_crtc(struct logicvc_drm *logicvc); +int logicvc_layers_init(struct logicvc_drm *logicvc); + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_mode.c b/drivers/gpu/drm/logicvc/logicvc_mode.c new file mode 100644 index 000000000000..11940704f644 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_mode.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logicvc_drm.h" +#include "logicvc_interface.h" +#include "logicvc_layer.h" +#include "logicvc_mode.h" + +static const struct drm_mode_config_funcs logicvc_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int logicvc_mode_init(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct logicvc_layer *layer_primary; + uint32_t preferred_depth; + int ret; + + ret = drm_vblank_init(drm_dev, mode_config->num_crtc); + if (ret) { + drm_err(drm_dev, "Failed to initialize vblank\n"); + return ret; + } + + layer_primary = logicvc_layer_get_primary(logicvc); + if (!layer_primary) { + drm_err(drm_dev, "Failed to get primary layer\n"); + return -EINVAL; + } + + preferred_depth = layer_primary->formats->depth; + + /* DRM counts alpha in depth, our driver doesn't. */ + if (layer_primary->formats->alpha) + preferred_depth += 8; + + mode_config->min_width = 64; + mode_config->max_width = 2048; + mode_config->min_height = 1; + mode_config->max_height = 2048; + mode_config->preferred_depth = preferred_depth; + mode_config->funcs = &logicvc_mode_config_funcs; + + drm_mode_config_reset(drm_dev); + + drm_kms_helper_poll_init(drm_dev); + + return 0; +} + +void logicvc_mode_fini(struct logicvc_drm *logicvc) +{ + struct drm_device *drm_dev = &logicvc->drm_dev; + + drm_kms_helper_poll_fini(drm_dev); +} diff --git a/drivers/gpu/drm/logicvc/logicvc_mode.h b/drivers/gpu/drm/logicvc/logicvc_mode.h new file mode 100644 index 000000000000..fee538ab1b96 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_mode.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_MODE_H_ +#define _LOGICVC_MODE_H_ + +struct logicvc_drm; + +int logicvc_mode_init(struct logicvc_drm *logicvc); +void logicvc_mode_fini(struct logicvc_drm *logicvc); + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_of.c b/drivers/gpu/drm/logicvc/logicvc_of.c new file mode 100644 index 000000000000..e0687730e039 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_of.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#include + +#include "logicvc_drm.h" +#include "logicvc_layer.h" +#include "logicvc_of.h" + +static struct logicvc_of_property_sv logicvc_of_display_interface_sv[] = { + { "lvds-4bits", LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS }, + { "lvds-3bits", LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS }, + { }, +}; + +static struct logicvc_of_property_sv logicvc_of_display_colorspace_sv[] = { + { "rgb", LOGICVC_DISPLAY_COLORSPACE_RGB }, + { "yuv422", LOGICVC_DISPLAY_COLORSPACE_YUV422 }, + { "yuv444", LOGICVC_DISPLAY_COLORSPACE_YUV444 }, + { }, +}; + +static struct logicvc_of_property_sv logicvc_of_layer_colorspace_sv[] = { + { "rgb", LOGICVC_LAYER_COLORSPACE_RGB }, + { "yuv", LOGICVC_LAYER_COLORSPACE_YUV }, + { }, +}; + +static struct logicvc_of_property_sv logicvc_of_layer_alpha_mode_sv[] = { + { "layer", LOGICVC_LAYER_ALPHA_LAYER }, + { "pixel", LOGICVC_LAYER_ALPHA_PIXEL }, + { }, +}; + +static struct logicvc_of_property logicvc_of_properties[] = { + [LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE] = { + .name = "xylon,display-interface", + .sv = logicvc_of_display_interface_sv, + .range = { + LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS, + LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS, + }, + }, + [LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE] = { + .name = "xylon,display-colorspace", + .sv = logicvc_of_display_colorspace_sv, + .range = { + LOGICVC_DISPLAY_COLORSPACE_RGB, + LOGICVC_DISPLAY_COLORSPACE_YUV444, + }, + }, + [LOGICVC_OF_PROPERTY_DISPLAY_DEPTH] = { + .name = "xylon,display-depth", + .range = { 8, 24 }, + }, + [LOGICVC_OF_PROPERTY_ROW_STRIDE] = { + .name = "xylon,row-stride", + }, + [LOGICVC_OF_PROPERTY_DITHERING] = { + .name = "xylon,dithering", + .optional = true, + }, + [LOGICVC_OF_PROPERTY_BACKGROUND_LAYER] = { + .name = "xylon,background-layer", + .optional = true, + }, + [LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE] = { + .name = "xylon,layers-configurable", + .optional = true, + }, + [LOGICVC_OF_PROPERTY_LAYERS_COUNT] = { + .name = "xylon,layers-count", + }, + [LOGICVC_OF_PROPERTY_LAYER_DEPTH] = { + .name = "xylon,layer-depth", + .range = { 8, 24 }, + }, + [LOGICVC_OF_PROPERTY_LAYER_COLORSPACE] = { + .name = "xylon,layer-colorspace", + .sv = logicvc_of_layer_colorspace_sv, + .range = { + LOGICVC_LAYER_COLORSPACE_RGB, + LOGICVC_LAYER_COLORSPACE_RGB, + }, + }, + [LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE] = { + .name = "xylon,layer-alpha-mode", + .sv = logicvc_of_layer_alpha_mode_sv, + .range = { + LOGICVC_LAYER_ALPHA_LAYER, + LOGICVC_LAYER_ALPHA_PIXEL, + }, + }, + [LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET] = { + .name = "xylon,layer-base-offset", + }, + [LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET] = { + .name = "xylon,layer-buffer-offset", + }, + [LOGICVC_OF_PROPERTY_LAYER_PRIMARY] = { + .name = "xylon,layer-primary", + .optional = true, + }, +}; + +static int logicvc_of_property_sv_value(struct logicvc_of_property_sv *sv, + const char *string, u32 *value) +{ + unsigned int i = 0; + + while (sv[i].string) { + if (!strcmp(sv[i].string, string)) { + *value = sv[i].value; + return 0; + } + + i++; + } + + return -EINVAL; +} + +int logicvc_of_property_parse_u32(struct device_node *of_node, + unsigned int index, u32 *target) +{ + struct logicvc_of_property *property; + const char *string; + u32 value; + int ret; + + if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) + return -EINVAL; + + property = &logicvc_of_properties[index]; + + if (!property->optional && + !of_property_read_bool(of_node, property->name)) + return -ENODEV; + + if (property->sv) { + ret = of_property_read_string(of_node, property->name, &string); + if (ret) + return ret; + + ret = logicvc_of_property_sv_value(property->sv, string, + &value); + if (ret) + return ret; + } else { + ret = of_property_read_u32(of_node, property->name, &value); + if (ret) + return ret; + } + + if (property->range[0] || property->range[1]) + if (value < property->range[0] || value > property->range[1]) + return -ERANGE; + + *target = value; + + return 0; +} + +void logicvc_of_property_parse_bool(struct device_node *of_node, + unsigned int index, bool *target) +{ + struct logicvc_of_property *property; + + if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) { + /* Fallback. */ + *target = false; + return; + } + + property = &logicvc_of_properties[index]; + *target = of_property_read_bool(of_node, property->name); +} + +bool logicvc_of_node_is_layer(struct device_node *of_node) +{ + return !of_node_cmp(of_node->name, "layer"); +} diff --git a/drivers/gpu/drm/logicvc/logicvc_of.h b/drivers/gpu/drm/logicvc/logicvc_of.h new file mode 100644 index 000000000000..46036e461be9 --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_of.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + */ + +#ifndef _LOGICVC_OF_H_ +#define _LOGICVC_OF_H_ + +enum logicvc_of_property_index { + LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE = 0, + LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE, + LOGICVC_OF_PROPERTY_DISPLAY_DEPTH, + LOGICVC_OF_PROPERTY_ROW_STRIDE, + LOGICVC_OF_PROPERTY_DITHERING, + LOGICVC_OF_PROPERTY_BACKGROUND_LAYER, + LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE, + LOGICVC_OF_PROPERTY_LAYERS_COUNT, + LOGICVC_OF_PROPERTY_LAYER_DEPTH, + LOGICVC_OF_PROPERTY_LAYER_COLORSPACE, + LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE, + LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET, + LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET, + LOGICVC_OF_PROPERTY_LAYER_PRIMARY, + LOGICVC_OF_PROPERTY_MAXIMUM, +}; + +struct logicvc_of_property_sv { + const char *string; + u32 value; +}; + +struct logicvc_of_property { + char *name; + bool optional; + struct logicvc_of_property_sv *sv; + u32 range[2]; +}; + +int logicvc_of_property_parse_u32(struct device_node *of_node, + unsigned int index, u32 *target); +void logicvc_of_property_parse_bool(struct device_node *of_node, + unsigned int index, bool *target); +bool logicvc_of_node_is_layer(struct device_node *of_node); + +#endif diff --git a/drivers/gpu/drm/logicvc/logicvc_regs.h b/drivers/gpu/drm/logicvc/logicvc_regs.h new file mode 100644 index 000000000000..4aae27e9ba2b --- /dev/null +++ b/drivers/gpu/drm/logicvc/logicvc_regs.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2022 Bootlin + * Author: Paul Kocialkowski + * + * Copyright (C) 2014 Xylon d.o.o. + * Author: Davor Joja + */ + +#ifndef _LOGICVC_REGS_H_ +#define _LOGICVC_REGS_H_ + +#define LOGICVC_DIMENSIONS_MAX (BIT(16) - 1) + +#define LOGICVC_HSYNC_FRONT_PORCH_REG 0x00 +#define LOGICVC_HSYNC_REG 0x08 +#define LOGICVC_HSYNC_BACK_PORCH_REG 0x10 +#define LOGICVC_HRES_REG 0x18 +#define LOGICVC_VSYNC_FRONT_PORCH_REG 0x20 +#define LOGICVC_VSYNC_REG 0x28 +#define LOGICVC_VSYNC_BACK_PORCH_REG 0x30 +#define LOGICVC_VRES_REG 0x38 + +#define LOGICVC_CTRL_REG 0x40 +#define LOGICVC_CTRL_CLOCK_INVERT BIT(8) +#define LOGICVC_CTRL_PIXEL_INVERT BIT(7) +#define LOGICVC_CTRL_DE_INVERT BIT(5) +#define LOGICVC_CTRL_DE_ENABLE BIT(4) +#define LOGICVC_CTRL_VSYNC_INVERT BIT(3) +#define LOGICVC_CTRL_VSYNC_ENABLE BIT(2) +#define LOGICVC_CTRL_HSYNC_INVERT BIT(1) +#define LOGICVC_CTRL_HSYNC_ENABLE BIT(0) + +#define LOGICVC_DTYPE_REG 0x48 +#define LOGICVC_BACKGROUND_COLOR_REG 0x50 + +#define LOGICVC_BUFFER_SEL_REG 0x58 +#define LOGICVC_BUFFER_SEL_VALUE(i, v) \ + (BIT(10 + (i)) | ((v) << (2 * (i)))) +#define LOGICVC_BUFFER_SEL_MAX 2 + +#define LOGICVC_DOUBLE_CLUT_REG 0x60 + +#define LOGICVC_INT_STAT_REG 0x68 +#define LOGICVC_INT_STAT_V_SYNC BIT(5) + +#define LOGICVC_INT_MASK_REG 0x70 +#define LOGICVC_INT_MASK_V_SYNC BIT(5) + +#define LOGICVC_POWER_CTRL_REG 0x78 +#define LOGICVC_POWER_CTRL_BACKLIGHT_ENABLE BIT(0) +#define LOGICVC_POWER_CTRL_VDD_ENABLE BIT(1) +#define LOGICVC_POWER_CTRL_VEE_ENABLE BIT(2) +#define LOGICVC_POWER_CTRL_VIDEO_ENABLE BIT(3) + +#define LOGICVC_IP_VERSION_REG 0xf8 +#define LOGICVC_IP_VERSION_MAJOR_MASK GENMASK(16, 11) +#define LOGICVC_IP_VERSION_MINOR_MASK GENMASK(10, 5) +#define LOGICVC_IP_VERSION_LEVEL_MASK GENMASK(4, 0) + +#define LOGICVC_LAYER_ADDRESS_REG(i) (0x100 + (i) * 0x80) +#define LOGICVC_LAYER_HOFFSET_REG(i) (0x100 + (i) * 0x80) + +#define LOGICVC_LAYER_VOFFSET_REG(i) (0x108 + (i) * 0x80) +#define LOGICVC_LAYER_VOFFSET_MAX 4095 + +#define LOGICVC_LAYER_HPOSITION_REG(i) (0x110 + (i) * 0x80) +#define LOGICVC_LAYER_VPOSITION_REG(i) (0x118 + (i) * 0x80) +#define LOGICVC_LAYER_WIDTH_REG(i) (0x120 + (i) * 0x80) +#define LOGICVC_LAYER_HEIGHT_REG(i) (0x128 + (i) * 0x80) +#define LOGICVC_LAYER_ALPHA_REG(i) (0x130 + (i) * 0x80) + +#define LOGICVC_LAYER_CTRL_REG(i) (0x138 + (i) * 0x80) +#define LOGICVC_LAYER_CTRL_ENABLE BIT(0) +#define LOGICVC_LAYER_CTRL_COLOR_KEY_DISABLE BIT(1) +#define LOGICVC_LAYER_CTRL_PIXEL_FORMAT_INVERT BIT(4) + +#define LOGICVC_LAYER_COLOR_KEY_REG(i) (0x140 + (i) * 0x80) + +#endif From 9cc4853e4781bf0dd0f35355dc92d97c9da02f5d Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Tue, 7 Jun 2022 23:31:44 +0200 Subject: [PATCH 21/63] drm: adv7511: override i2c address of cec before accessing it Commit 680532c50bca ("drm: adv7511: Add support for i2c_new_secondary_device") allows a device tree node to override the default addresses of the secondary i2c devices. This is useful for solving address conflicts on the i2c bus. In adv7511_init_cec_regmap() the new i2c address of cec device is read from device tree and immediately accessed, well before it is written in the proper register to override the default address. This can cause an i2c error during probe and a consequent probe failure. Once the new i2c address is read from the device tree, override the default address before any attempt to access the cec. Tested with adv7533 and stm32mp157f. Signed-off-by: Antonio Borneo Fixes: 680532c50bca ("drm: adv7511: Add support for i2c_new_secondary_device") Reviewed-by: Kieran Bingham Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220607213144.427177-1-antonio.borneo@foss.st.com --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 5bb9300040dd..074c2e650cae 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1065,6 +1065,10 @@ static int adv7511_init_cec_regmap(struct adv7511 *adv) ADV7511_CEC_I2C_ADDR_DEFAULT); if (IS_ERR(adv->i2c_cec)) return PTR_ERR(adv->i2c_cec); + + regmap_write(adv->regmap, ADV7511_REG_CEC_I2C_ADDR, + adv->i2c_cec->addr << 1); + i2c_set_clientdata(adv->i2c_cec, adv); adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec, @@ -1271,9 +1275,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (ret) goto err_i2c_unregister_packet; - regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, - adv7511->i2c_cec->addr << 1); - INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work); if (i2c->irq) { From 7d188c521d9ec3650136e9b9c81c61b22a544ab3 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Sat, 11 Jun 2022 09:55:04 -0700 Subject: [PATCH 22/63] drm: Fix htmldocs indentation warning w/ DP AUX power requirements Two blank lines are needed to make the rst valid. Fixes: 69ef4a192bba ("drm: Document the power requirements for DP AUX transfers") Reported-by: Stephen Rothwell Signed-off-by: Douglas Anderson Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220611095445.1.I534072d346b1ebbf0db565b714de9b65cbb24651@changeid --- include/drm/display/drm_dp_helper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index dc3c02225fcf..c5f8f45511ed 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -372,8 +372,10 @@ struct drm_dp_aux { * Also note that this callback can be called no matter the * state @dev is in and also no matter what state the panel is * in. It's expected: + * * - If the @dev providing the AUX bus is currently unpowered then * it will power itself up for the transfer. + * * - If we're on eDP (using a drm_panel) and the panel is not in a * state where it can respond (it's not powered or it's in a * low power state) then this function may return an error, but From fc801750b197d0f00c09e01e59a7dcd240fddcb5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:12 +0200 Subject: [PATCH 23/63] drm: of: Add drm_of_get_data_lanes_count and drm_of_get_data_lanes_ep Add helper function to count and sanitize DT "data-lanes" property and return either error or the data-lanes count. This is useful for both DSI and (e)DP "data-lanes" property. The later version of the function is an extra wrapper which handles the endpoint look up by regs, that's what majority of the drivers duplicate too, but not all of them. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-1-marex@denx.de --- drivers/gpu/drm/drm_of.c | 61 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_of.h | 20 +++++++++++++ 2 files changed, 81 insertions(+) diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 9a2cfab3a177..2c1ee601f1d8 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -430,3 +430,64 @@ int drm_of_lvds_get_data_mapping(const struct device_node *port) return -EINVAL; } EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping); + +/** + * drm_of_get_data_lanes_count - Get DSI/(e)DP data lane count + * @endpoint: DT endpoint node of the DSI/(e)DP source or sink + * @min: minimum supported number of data lanes + * @max: maximum supported number of data lanes + * + * Count DT "data-lanes" property elements and check for validity. + * + * Return: + * * min..max - positive integer count of "data-lanes" elements + * * -ve - the "data-lanes" property is missing or invalid + * * -EINVAL - the "data-lanes" property is unsupported + */ +int drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max) +{ + int ret; + + ret = of_property_count_u32_elems(endpoint, "data-lanes"); + if (ret < 0) + return ret; + + if (ret < min || ret > max) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count); + +/** + * drm_of_get_data_lanes_count_ep - Get DSI/(e)DP data lane count by endpoint + * @port: DT port node of the DSI/(e)DP source or sink + * @port_reg: identifier (value of reg property) of the parent port node + * @reg: identifier (value of reg property) of the endpoint node + * @min: minimum supported number of data lanes + * @max: maximum supported number of data lanes + * + * Count DT "data-lanes" property elements and check for validity. + * This variant uses endpoint specifier. + * + * Return: + * * min..max - positive integer count of "data-lanes" elements + * * -EINVAL - the "data-mapping" property is unsupported + * * -ENODEV - the "data-mapping" property is missing + */ +int drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg, + const unsigned int min, + const unsigned int max) +{ + struct device_node *endpoint; + int ret; + + endpoint = of_graph_get_endpoint_by_regs(port, port_reg, reg); + ret = drm_of_get_data_lanes_count(endpoint, min, max); + of_node_put(endpoint); + + return ret; +} +EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count_ep); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 99f79ac8b4cd..92387eabcb6f 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -50,6 +50,12 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, const struct device_node *port2); int drm_of_lvds_get_data_mapping(const struct device_node *port); +int drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max); +int drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg, + const unsigned int min, + const unsigned int max); #else static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev, struct device_node *port) @@ -105,6 +111,20 @@ drm_of_lvds_get_data_mapping(const struct device_node *port) { return -EINVAL; } + +int drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max) +{ + return -EINVAL; +} + +int drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg + const unsigned int min, + const unsigned int max) +{ + return -EINVAL; +} #endif /* From 930e7cba1190a3251fcb01c8523ea9aed89d9a51 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:13 +0200 Subject: [PATCH 24/63] drm/bridge: anx7625: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg Cc: Xin Ji To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-2-marex@denx.de --- drivers/gpu/drm/bridge/analogix/anx7625.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index e92eb4a40745..bbdca16db0d6 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1637,16 +1637,16 @@ static int anx7625_parse_dt(struct device *dev, if (of_property_read_u32(ep0, "bus-type", &bus_type)) bus_type = 0; - mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes"); + mipi_lanes = drm_of_get_data_lanes_count(ep0, 1, MAX_LANES_SUPPORT); of_node_put(ep0); } if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */ pdata->is_dpi = 0; - pdata->mipi_lanes = mipi_lanes; - if (pdata->mipi_lanes > MAX_LANES_SUPPORT || pdata->mipi_lanes <= 0) - pdata->mipi_lanes = MAX_LANES_SUPPORT; + pdata->mipi_lanes = MAX_LANES_SUPPORT; + if (mipi_lanes > 0) + pdata->mipi_lanes = mipi_lanes; if (pdata->is_dpi) DRM_DEV_DEBUG_DRIVER(dev, "found MIPI DPI host node.\n"); From 4af48f1d1d0a6f050ffcfe83e939c9455d51267f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:14 +0200 Subject: [PATCH 25/63] drm/bridge: icn6211: Convert to drm_of_get_data_lanes_count_ep Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Jagan Teki Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-3-marex@denx.de --- drivers/gpu/drm/bridge/chipone-icn6211.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 2b8cf64cbd80..d25bc62bfebd 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -498,21 +498,18 @@ static int chipone_dsi_attach(struct chipone *icn) { struct mipi_dsi_device *dsi = icn->dsi; struct device *dev = icn->dev; - struct device_node *endpoint; int dsi_lanes, ret; - endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); - dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); - of_node_put(endpoint); + dsi_lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, 0, 1, 4); /* * If the 'data-lanes' property does not exist in DT or is invalid, * default to previously hard-coded behavior, which was 4 data lanes. */ - if (dsi_lanes >= 1 && dsi_lanes <= 4) - icn->dsi->lanes = dsi_lanes; - else + if (dsi_lanes < 0) icn->dsi->lanes = 4; + else + icn->dsi->lanes = dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | From 1db4b5264819a8e3aa62e38287bf1da2e1dc8894 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:15 +0200 Subject: [PATCH 26/63] drm/bridge: lt8912: Convert to drm_of_get_data_lanes_count_ep Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Adrien Grassein Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-4-marex@denx.de --- drivers/gpu/drm/bridge/lontium-lt8912b.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index c642d1e02b2f..c92515834ff2 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -607,7 +607,6 @@ static int lt8912_parse_dt(struct lt8912 *lt) int ret; int data_lanes; struct device_node *port_node; - struct device_node *endpoint; gp_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(gp_reset)) { @@ -618,16 +617,12 @@ static int lt8912_parse_dt(struct lt8912 *lt) } lt->gp_reset = gp_reset; - endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); - if (!endpoint) - return -ENODEV; - - data_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); - of_node_put(endpoint); + data_lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, -1, 1, 4); if (data_lanes < 0) { dev_err(lt->dev, "%s: Bad data-lanes property\n", __func__); return data_lanes; } + lt->data_lanes = data_lanes; lt->host_node = of_graph_get_remote_node(dev->of_node, 0, -1); From 5c57cbc390b166950c2e6c2f0c4edaeb0f47e97d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:16 +0200 Subject: [PATCH 27/63] drm/bridge: lt9211: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-5-marex@denx.de --- drivers/gpu/drm/bridge/lontium-lt9211.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index e92821fbc639..84d764b4139b 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -686,7 +686,7 @@ static int lt9211_host_attach(struct lt9211 *ctx) int ret; endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); - dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4); host_node = of_graph_get_remote_port_parent(endpoint); host = of_find_mipi_dsi_host_by_node(host_node); of_node_put(host_node); @@ -695,8 +695,8 @@ static int lt9211_host_attach(struct lt9211 *ctx) if (!host) return -EPROBE_DEFER; - if (dsi_lanes < 0 || dsi_lanes > 4) - return -EINVAL; + if (dsi_lanes < 0) + return dsi_lanes; dsi = devm_mipi_dsi_device_register_full(dev, host, &info); if (IS_ERR(dsi)) From d8609fd1e0742ba3ad2e51bba9dd489dd03d8599 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:17 +0200 Subject: [PATCH 28/63] drm/bridge: tc358767: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-6-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 6c6177028742..e4dd4f05f94b 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1890,18 +1890,18 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) int dsi_lanes, ret; endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); - dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4); host_node = of_graph_get_remote_port_parent(endpoint); host = of_find_mipi_dsi_host_by_node(host_node); of_node_put(host_node); of_node_put(endpoint); - if (dsi_lanes <= 0 || dsi_lanes > 4) - return -EINVAL; - if (!host) return -EPROBE_DEFER; + if (dsi_lanes < 0) + return dsi_lanes; + dsi = mipi_dsi_device_register_full(host, &info); if (IS_ERR(dsi)) return dev_err_probe(dev, PTR_ERR(dsi), From 56426faa1492289ff794620c4ed8c1847a420d0a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:18 +0200 Subject: [PATCH 29/63] drm/bridge: tc358775: Convert to drm_of_get_data_lanes_count_ep Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-7-marex@denx.de --- drivers/gpu/drm/bridge/tc358775.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 62a7ef352daa..5b1fb8e2f9a7 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -529,8 +529,7 @@ static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc) struct device_node *endpoint; struct device_node *parent; struct device_node *remote; - struct property *prop; - int len = 0; + int dsi_lanes; /* * To get the data-lanes of dsi, we need to access the dsi0_out of port1 @@ -544,25 +543,15 @@ static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc) of_node_put(endpoint); if (parent) { /* dsi0 port 1 */ - endpoint = of_graph_get_endpoint_by_regs(parent, 1, -1); + dsi_lanes = drm_of_get_data_lanes_count_ep(parent, 1, -1, 1, 4); of_node_put(parent); - if (endpoint) { - prop = of_find_property(endpoint, "data-lanes", - &len); - of_node_put(endpoint); - if (!prop) { - dev_err(tc->dev, - "failed to find data lane\n"); - return -EPROBE_DEFER; - } - } } } - tc->num_dsi_lanes = len / sizeof(u32); + if (dsi_lanes < 0) + return dsi_lanes; - if (tc->num_dsi_lanes < 1 || tc->num_dsi_lanes > 4) - return -EINVAL; + tc->num_dsi_lanes = dsi_lanes; tc->host_node = of_graph_get_remote_node(np, 0, 0); if (!tc->host_node) From 254a8d0c0071255eb19a81a32ff8d5a0beef1e34 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:20 +0200 Subject: [PATCH 30/63] drm/bridge: ti-sn65dsi86: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-9-marex@denx.de --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 8cad662de9bb..c2b9227f7042 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1142,8 +1142,8 @@ static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata, * mappings that the hardware supports. */ endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); - dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); - if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) { + dp_lanes = drm_of_get_data_lanes_count(endpoint, 1, SN_MAX_DP_LANES); + if (dp_lanes > 0) { of_property_read_u32_array(endpoint, "data-lanes", lane_assignments, dp_lanes); of_property_read_u32_array(endpoint, "lane-polarities", From 185443efa26a62b7d1401c89b83c89a1a2601350 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:21 +0200 Subject: [PATCH 31/63] drm/msm: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Reviewed-by: Dmitry Baryshkov Signed-off-by: Marek Vasut Cc: Abhinav Kumar Cc: Andrzej Hajda Cc: Dmitry Baryshkov Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Rob Clark Cc: Robert Foss Cc: Sam Ravnborg Cc: Sean Paul To: dri-devel@lists.freedesktop.org Reviewed-by: Abhinav Kumar Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-10-marex@denx.de --- drivers/gpu/drm/msm/dp/dp_parser.c | 6 ++---- drivers/gpu/drm/msm/dsi/dsi_host.c | 7 +++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index 1056b8d5755b..fabd18e7dc0d 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -102,11 +102,9 @@ static int dp_parser_ctrl_res(struct dp_parser *parser) static int dp_parser_misc(struct dp_parser *parser) { struct device_node *of_node = parser->pdev->dev.of_node; - int len = 0; - const char *data_lane_property = "data-lanes"; + int len; - len = of_property_count_elems_of_size(of_node, - data_lane_property, sizeof(u32)); + len = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES); if (len < 0) { DRM_WARN("Invalid property %s, default max DP lanes = %d\n", data_lane_property, DP_MAX_NUM_DP_LANES); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index d51e70fab93d..63969e464dbe 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1673,11 +1673,10 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, return 0; } - num_lanes = len / sizeof(u32); - - if (num_lanes < 1 || num_lanes > 4) { + num_lanes = drm_of_get_data_lanes_count(ep, 1, 4); + if (num_lanes < 0) { DRM_DEV_ERROR(dev, "bad number of data lanes\n"); - return -EINVAL; + return num_lanes; } msm_host->num_data_lanes = num_lanes; From d643daaf1694b7565fbe3982b630e1c7b95f1600 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 May 2022 03:05:22 +0200 Subject: [PATCH 32/63] drm/bridge: rcar: Convert to drm_of_get_data_lanes_count_ep Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220524010522.528569-11-marex@denx.de --- drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c index 891bb956fd61..67dce337098a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c @@ -683,19 +683,10 @@ static int rcar_mipi_dsi_parse_dt(struct rcar_mipi_dsi *dsi) u32 data_lanes[4]; int ret; - ep = of_graph_get_endpoint_by_regs(dsi->dev->of_node, 1, 0); - if (!ep) { - dev_dbg(dsi->dev, "unconnected port@1\n"); - return -ENODEV; - } - - ret = of_property_read_variable_u32_array(ep, "data-lanes", data_lanes, - 1, 4); - of_node_put(ep); - + ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4); if (ret < 0) { dev_err(dsi->dev, "missing or invalid data-lanes property\n"); - return -ENODEV; + return ret; } dsi->num_data_lanes = ret; From fc8adb13d844b44c147b4dced292c74a1ab5cb25 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 12:29:18 +0200 Subject: [PATCH 33/63] drm/bridge: ti-sn65dsi83: Do not cache dsi_lanes and host twice The DSI lane count can be accessed via the dsi device pointer, make use of that. The DSI host pointer is only used in sn65dsi83_host_attach(), move the code around so that the host does not have to be cached in the driver private data. This simplifies the code further. No functional change. This has the added bonus that lt9211, tc358767, sn65dsi83 now use very similar *_mipi_dsi_host_attach() which is ripe for deduplication. Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220612102918.13874-1-marex@denx.de --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 64 +++++++++------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index ac66f408b40c..8bf99b32776e 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -140,12 +140,10 @@ struct sn65dsi83 { struct drm_bridge bridge; struct device *dev; struct regmap *regmap; - struct device_node *host_node; struct mipi_dsi_device *dsi; struct drm_bridge *panel_bridge; struct gpio_desc *enable_gpio; struct regulator *vcc; - int dsi_lanes; bool lvds_dual_link; bool lvds_dual_link_even_odd_swap; }; @@ -306,7 +304,7 @@ static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx, */ return DIV_ROUND_UP(clamp((unsigned int)mode->clock * mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) / - ctx->dsi_lanes / 2, 40000U, 500000U), 5000U); + ctx->dsi->lanes / 2, 40000U, 500000U), 5000U); } static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx) @@ -314,7 +312,7 @@ static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx) /* The divider is (DSI_CLK / LVDS_CLK) - 1, which really is: */ unsigned int dsi_div = mipi_dsi_pixel_format_to_bpp(ctx->dsi->format); - dsi_div /= ctx->dsi_lanes; + dsi_div /= ctx->dsi->lanes; if (!ctx->lvds_dual_link) dsi_div /= 2; @@ -405,7 +403,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, /* Set number of DSI lanes and LVDS link config. */ regmap_write(ctx->regmap, REG_DSI_LANE, REG_DSI_LANE_DSI_CHANNEL_MODE_SINGLE | - REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi_lanes - 1)) | + REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi->lanes - 1)) | /* CHB is DSI85-only, set to default on DSI83/DSI84 */ REG_DSI_LANE_CHB_DSI_LANES(3)); /* No equalization. */ @@ -569,22 +567,6 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) { struct drm_bridge *panel_bridge; struct device *dev = ctx->dev; - struct device_node *endpoint; - int ret; - - endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); - ctx->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); - ctx->host_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - - if (ctx->dsi_lanes <= 0 || ctx->dsi_lanes > 4) { - ret = -EINVAL; - goto err_put_node; - } - if (!ctx->host_node) { - ret = -ENODEV; - goto err_put_node; - } ctx->lvds_dual_link = false; ctx->lvds_dual_link_even_odd_swap = false; @@ -610,10 +592,8 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) } panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0); - if (IS_ERR(panel_bridge)) { - ret = PTR_ERR(panel_bridge); - goto err_put_node; - } + if (IS_ERR(panel_bridge)) + return PTR_ERR(panel_bridge); ctx->panel_bridge = panel_bridge; @@ -623,15 +603,13 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) "Failed to get supply 'vcc'\n"); return 0; - -err_put_node: - of_node_put(ctx->host_node); - return ret; } static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) { struct device *dev = ctx->dev; + struct device_node *host_node; + struct device_node *endpoint; struct mipi_dsi_device *dsi; struct mipi_dsi_host *host; const struct mipi_dsi_device_info info = { @@ -639,13 +617,20 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) .channel = 0, .node = NULL, }; - int ret; + int dsi_lanes, ret; - host = of_find_mipi_dsi_host_by_node(ctx->host_node); - if (!host) { - dev_err(dev, "failed to find dsi host\n"); + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); + dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + host_node = of_graph_get_remote_port_parent(endpoint); + host = of_find_mipi_dsi_host_by_node(host_node); + of_node_put(host_node); + of_node_put(endpoint); + + if (!host) return -EPROBE_DEFER; - } + + if (dsi_lanes < 0) + return dsi_lanes; dsi = devm_mipi_dsi_device_register_full(dev, host, &info); if (IS_ERR(dsi)) @@ -654,7 +639,7 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) ctx->dsi = dsi; - dsi->lanes = ctx->dsi_lanes; + dsi->lanes = dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; @@ -701,10 +686,8 @@ static int sn65dsi83_probe(struct i2c_client *client, return ret; ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config); - if (IS_ERR(ctx->regmap)) { - ret = PTR_ERR(ctx->regmap); - goto err_put_node; - } + if (IS_ERR(ctx->regmap)) + return PTR_ERR(ctx->regmap); dev_set_drvdata(dev, ctx); i2c_set_clientdata(client, ctx); @@ -721,8 +704,6 @@ static int sn65dsi83_probe(struct i2c_client *client, err_remove_bridge: drm_bridge_remove(&ctx->bridge); -err_put_node: - of_node_put(ctx->host_node); return ret; } @@ -731,7 +712,6 @@ static int sn65dsi83_remove(struct i2c_client *client) struct sn65dsi83 *ctx = i2c_get_clientdata(client); drm_bridge_remove(&ctx->bridge); - of_node_put(ctx->host_node); return 0; } From 16bd48dc2b3b08d8b3160182b42e51eb2c274706 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 12:29:47 +0200 Subject: [PATCH 34/63] drm/bridge: ti-sn65dsi83: Convert to drm_of_get_data_lanes_count Convert driver to use this new helper to standardize OF "data-lanes" parsing. Reviewed-by: Andrzej Hajda Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220612102947.13912-1-marex@denx.de --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 8bf99b32776e..b27c0d7c451a 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -620,7 +620,7 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) int dsi_lanes, ret; endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); - dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4); host_node = of_graph_get_remote_port_parent(endpoint); host = of_find_mipi_dsi_host_by_node(host_node); of_node_put(host_node); From eb6b94db2f4a585d4a9b8e503d7377d2a23c1104 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 15:21:50 +0200 Subject: [PATCH 35/63] drm: of: Mark empty drm_of_get_data_lanes_count and drm_of_get_data_lanes_ep static Mark empty implementations of drm_of_get_data_lanes_count and drm_of_get_data_lanes_ep as static inline, just like the rest of empty implementations of various functions in drm_of.h . Add missing comma to drm_of_get_data_lanes_count_ep() . Fixes: fc801750b197 ("drm: of: Add drm_of_get_data_lanes_count and drm_of_get_data_lanes_ep") Reported-by: kernel test robot Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220612132152.91052-1-marex@denx.de --- include/drm/drm_of.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 92387eabcb6f..10ab58c40746 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -112,16 +112,18 @@ drm_of_lvds_get_data_mapping(const struct device_node *port) return -EINVAL; } -int drm_of_get_data_lanes_count(const struct device_node *endpoint, - const unsigned int min, const unsigned int max) +static inline int +drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max) { return -EINVAL; } -int drm_of_get_data_lanes_count_ep(const struct device_node *port, - int port_reg, int reg - const unsigned int min, - const unsigned int max) +static inline int +drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg, + const unsigned int min, + const unsigned int max) { return -EINVAL; } From c750c4ce1ad69d3df68359abce70ee3d0064c97a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 15:21:51 +0200 Subject: [PATCH 36/63] drm/bridge: tc358775: Fix drm_of_get_data_lanes_count_ep conversion Initialize dsi_lanes to -1, so that in case the endpoint is missing, probe would fail as it did before the conversion, instead of depending on uninitialized variable and thus undefined behavior. Fixes: 56426faa1492 ("drm/bridge: tc358775: Convert to drm_of_get_data_lanes_count_ep") Reported-by: kernel test robot Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220612132152.91052-2-marex@denx.de --- drivers/gpu/drm/bridge/tc358775.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 5b1fb8e2f9a7..e5d00a6e7880 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -529,7 +529,7 @@ static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc) struct device_node *endpoint; struct device_node *parent; struct device_node *remote; - int dsi_lanes; + int dsi_lanes = -1; /* * To get the data-lanes of dsi, we need to access the dsi0_out of port1 From 473c93b5b94f63de30f35b2935616457cca5c4d8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 15:21:52 +0200 Subject: [PATCH 37/63] drm/bridge: rcar: Drop unused variables due to drm_of_get_data_lanes_count_ep The rcar_mipi_dsi_parse_dt() now contains two uninitialized variables due to conversion to common drm_of_get_data_lanes_count_ep() helper. Drop them. Fixes: d643daaf1694 ("drm/bridge: rcar: Convert to drm_of_get_data_lanes_count_ep") Reported-by: kernel test robot Signed-off-by: Marek Vasut Cc: Andrzej Hajda Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Robert Foss Cc: Sam Ravnborg To: dri-devel@lists.freedesktop.org Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220612132152.91052-3-marex@denx.de --- drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c index 67dce337098a..31ed285073e0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c @@ -679,8 +679,6 @@ static const struct mipi_dsi_host_ops rcar_mipi_dsi_host_ops = { static int rcar_mipi_dsi_parse_dt(struct rcar_mipi_dsi *dsi) { - struct device_node *ep; - u32 data_lanes[4]; int ret; ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4); From 9c63e253485cd78ae74da2fcce67e7a1746ed905 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 3 Jun 2022 10:26:05 +0100 Subject: [PATCH 38/63] dt-bindings: gpu: v3d: Add BCM2711's compatible BCM2711, Raspberry Pi 4's SoC, contains a V3D core. So add its specific compatible to the bindings. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Peter Robinson Reviewed-by: Stefan Wahren Reviewed-by: Javier Martinez Canillas Acked-by: Rob Herring Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20220603092610.1909675-2-pbrobinson@gmail.com --- Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml index e6485f7b046f..217c42874f41 100644 --- a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml +++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml @@ -16,6 +16,7 @@ properties: compatible: enum: + - brcm,2711-v3d - brcm,7268-v3d - brcm,7278-v3d From 90a64adb0876f3b6ec49819df99edffed982e4f2 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 3 Jun 2022 10:26:06 +0100 Subject: [PATCH 39/63] drm/v3d: Get rid of pm code Runtime PM doesn't seem to work correctly on this driver. On top of that, commit 8b6864e3e138 ("drm/v3d/v3d_drv: Remove unused static variable 'v3d_v3d_pm_ops'") hints that it most likely never did as the driver's PM ops were not hooked-up. So, in order to support regular operation with V3D on BCM2711 (Raspberry Pi 4), get rid of the PM code. PM will be reinstated once we figure out the underlying issues. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Peter Robinson Reviewed-by: Javier Martinez Canillas Acked-by: Melissa Wen Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20220603092610.1909675-3-pbrobinson@gmail.com --- drivers/gpu/drm/v3d/v3d_debugfs.c | 18 +----------------- drivers/gpu/drm/v3d/v3d_drv.c | 11 ----------- drivers/gpu/drm/v3d/v3d_gem.c | 12 +----------- 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index 29fd13109e43..efbde124c296 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -131,11 +130,7 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; struct v3d_dev *v3d = to_v3d_dev(dev); u32 ident0, ident1, ident2, ident3, cores; - int ret, core; - - ret = pm_runtime_get_sync(v3d->drm.dev); - if (ret < 0) - return ret; + int core; ident0 = V3D_READ(V3D_HUB_IDENT0); ident1 = V3D_READ(V3D_HUB_IDENT1); @@ -188,9 +183,6 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused) (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0); } - pm_runtime_mark_last_busy(v3d->drm.dev); - pm_runtime_put_autosuspend(v3d->drm.dev); - return 0; } @@ -218,11 +210,6 @@ static int v3d_measure_clock(struct seq_file *m, void *unused) uint32_t cycles; int core = 0; int measure_ms = 1000; - int ret; - - ret = pm_runtime_get_sync(v3d->drm.dev); - if (ret < 0) - return ret; if (v3d->ver >= 40) { V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3, @@ -246,9 +233,6 @@ static int v3d_measure_clock(struct seq_file *m, void *unused) cycles / (measure_ms * 1000), (cycles / (measure_ms * 100)) % 10); - pm_runtime_mark_last_busy(v3d->drm.dev); - pm_runtime_put_autosuspend(v3d->drm.dev); - return 0; } diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 1afcd54fbbd5..56d5f831e48b 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -43,7 +42,6 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data, { struct v3d_dev *v3d = to_v3d_dev(dev); struct drm_v3d_get_param *args = data; - int ret; static const u32 reg_map[] = { [DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_UIFCFG, [DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_IDENT1, @@ -69,17 +67,12 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data, if (args->value != 0) return -EINVAL; - ret = pm_runtime_get_sync(v3d->drm.dev); - if (ret < 0) - return ret; if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 && args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) { args->value = V3D_CORE_READ(0, offset); } else { args->value = V3D_READ(offset); } - pm_runtime_mark_last_busy(v3d->drm.dev); - pm_runtime_put_autosuspend(v3d->drm.dev); return 0; } @@ -280,10 +273,6 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) return -ENOMEM; } - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 50); - pm_runtime_enable(dev); - ret = v3d_gem_init(drm); if (ret) goto dma_free; diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 2352e9640922..725a252e837b 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -372,9 +371,6 @@ v3d_job_free(struct kref *ref) dma_fence_put(job->irq_fence); dma_fence_put(job->done_fence); - pm_runtime_mark_last_busy(job->v3d->drm.dev); - pm_runtime_put_autosuspend(job->v3d->drm.dev); - if (job->perfmon) v3d_perfmon_put(job->perfmon); @@ -476,14 +472,10 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, job->v3d = v3d; job->free = free; - ret = pm_runtime_get_sync(v3d->drm.dev); - if (ret < 0) - goto fail; - ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], v3d_priv); if (ret) - goto fail_job; + goto fail; if (has_multisync) { if (se->in_sync_count && se->wait_stage == queue) { @@ -514,8 +506,6 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, fail_deps: drm_sched_job_cleanup(&job->base); -fail_job: - pm_runtime_put_autosuspend(v3d->drm.dev); fail: kfree(*container); *container = NULL; From e5a068983cf41bfee2c15656e62f401c5f8b0437 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 3 Jun 2022 10:26:07 +0100 Subject: [PATCH 40/63] drm/v3d: Add support for bcm2711 Add compatible string and Kconfig options and help for bcm2711. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Peter Robinson Reviewed-by: Stefan Wahren Reviewed-by: Javier Martinez Canillas Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20220603092610.1909675-4-pbrobinson@gmail.com --- drivers/gpu/drm/v3d/Kconfig | 5 +++-- drivers/gpu/drm/v3d/v3d_drv.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/v3d/Kconfig b/drivers/gpu/drm/v3d/Kconfig index e973ec487484..ce62c5908e1d 100644 --- a/drivers/gpu/drm/v3d/Kconfig +++ b/drivers/gpu/drm/v3d/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_V3D tristate "Broadcom V3D 3.x and newer" - depends on ARCH_BCM || ARCH_BRCMSTB || COMPILE_TEST + depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST depends on DRM depends on COMMON_CLK depends on MMU @@ -9,4 +9,5 @@ config DRM_V3D select DRM_GEM_SHMEM_HELPER help Choose this option if you have a system that has a Broadcom - V3D 3.x or newer GPU, such as BCM7268. + V3D 3.x or newer GPUs. SoCs supported include the BCM2711, + BCM7268 and BCM7278. diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 56d5f831e48b..8c7f910daa28 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -191,6 +191,7 @@ static const struct drm_driver v3d_drm_driver = { }; static const struct of_device_id v3d_of_match[] = { + { .compatible = "brcm,2711-v3d" }, { .compatible = "brcm,7268-v3d" }, { .compatible = "brcm,7278-v3d" }, {}, From 24f6fe3226c6f9f1b8406311a96b59c6e650b707 Mon Sep 17 00:00:00 2001 From: Tales Lelo da Aparecida Date: Fri, 15 Apr 2022 08:12:59 -0300 Subject: [PATCH 41/63] drm/vkms: check plane_composer->map[0] before using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a copypasta error. The caller of compose_plane() already checks primary_composer->map. In contrast, plane_composer->map is never verified here before handling. Fixes: 7938f4218168 ("dma-buf-map: Rename to iosys-map") Reviewed-by: André Almeida Signed-off-by: Tales Lelo da Aparecida Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20220415111300.61013-2-tales.aparecida@gmail.com --- drivers/gpu/drm/vkms/vkms_composer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 914c0ac7dc8b..0f5655f72640 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -157,7 +157,7 @@ static void compose_plane(struct vkms_composer *primary_composer, void *vaddr; void (*pixel_blend)(const u8 *p_src, u8 *p_dst); - if (WARN_ON(iosys_map_is_null(&primary_composer->map[0]))) + if (WARN_ON(iosys_map_is_null(&plane_composer->map[0]))) return; vaddr = plane_composer->map[0].vaddr; From 16490922a95f4f67eb9d918f94a4f5b5bdc9eb80 Mon Sep 17 00:00:00 2001 From: Igor Torrente Date: Mon, 4 Apr 2022 17:45:07 -0300 Subject: [PATCH 42/63] drm: vkms: Alloc the compose frame using vzalloc Currently, the memory to the composition frame is being allocated using the kzmalloc. This comes with the limitation of maximum size of one page size(which in the x86_64 is 4Kb and 4MB for default and hugepage respectively). Somes test of igt (e.g. kms_plane@pixel-format) uses more than 4MB when testing some pixel formats like ARGB16161616 and the following error were showing up when running kms_plane@plane-panning-bottom-right*: [drm:vkms_composer_worker [vkms]] *ERROR* Cannot allocate memory for output frame. This problem is addessed by allocating the memory using kvzalloc that circunvents this limitation. V5: Improve the commit message and drop the debugging issues in VKMS TO-DO(Melissa Wen). Reviewed-by: Melissa Wen Signed-off-by: Igor Torrente Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20220404204515.42144-2-igormtorrente@gmail.com --- Documentation/gpu/vkms.rst | 6 ------ drivers/gpu/drm/vkms/vkms_composer.c | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst index 9c873c3912cc..973e2d43108b 100644 --- a/Documentation/gpu/vkms.rst +++ b/Documentation/gpu/vkms.rst @@ -102,12 +102,6 @@ Debugging: - kms_plane: some test cases are failing due to timeout on capturing CRC; -- kms_flip: when running test cases in sequence, some successful individual - test cases are failing randomly; when individually, some successful test - cases display in the log the following error:: - - [drm:vkms_prepare_fb [vkms]] ERROR vmap failed: -4 - Virtual hardware (vblank-less) mode: - VKMS already has support for vblanks simulated via hrtimers, which can be diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 0f5655f72640..775b97766e08 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -180,7 +180,7 @@ static int compose_active_planes(void **vaddr_out, int i; if (!*vaddr_out) { - *vaddr_out = kzalloc(gem_obj->size, GFP_KERNEL); + *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL); if (!*vaddr_out) { DRM_ERROR("Cannot allocate memory for output frame."); return -ENOMEM; @@ -263,7 +263,7 @@ void vkms_composer_worker(struct work_struct *work) crtc_state); if (ret) { if (ret == -EINVAL && !wb_pending) - kfree(vaddr_out); + kvfree(vaddr_out); return; } @@ -275,7 +275,7 @@ void vkms_composer_worker(struct work_struct *work) crtc_state->wb_pending = false; spin_unlock_irq(&out->composer_lock); } else { - kfree(vaddr_out); + kvfree(vaddr_out); } /* From f4e3a12bf2d1983b8f352b7719794c52be7e0990 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 13 Jun 2022 14:34:54 +0800 Subject: [PATCH 43/63] drm/syncobj: add missing error return code in drm_syncobj_transfer_to_timeline() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If dma_fence_unwrap_merge() fails, it should return error code in drm_syncobj_transfer_to_timeline() Fixes: ec8d985ff26f ("drm: use dma_fence_unwrap_merge() in drm_syncobj") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Reviewed-by: Christian König Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220613063454.2609364-1-yangyingliang@huawei.com --- drivers/gpu/drm/drm_syncobj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index bbad9e4696e7..0c2be8360525 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -874,8 +874,10 @@ static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private, fence = dma_fence_unwrap_merge(tmp); dma_fence_put(tmp); - if (!fence) + if (!fence) { + ret = -ENOMEM; goto err_put_timeline; + } chain = dma_fence_chain_alloc(); if (!chain) { From fc67615f4ecc3b66bd458840927272aa131eface Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:31 +0300 Subject: [PATCH 44/63] drm/edid: fix CTA data block collection size for CTA version 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CTA Data Block Collection is valid only for CTA extension version 3. In versions 1 and 2, it is a reserved block, which we ignore. The DTD start offset (byte 2, or d in CTA-861 spec), which determines the CTA Data Block Collection size, is specified slightly differently for different versions: Version 1: d = offset for the byte following the reserved data block. If no data is provided in the reserved data block, then d=4. If no DTDs are provided, then d=0 Version 2: d = offset for the byte following the reserved data block. If no data is provided in the reserved data block, then d=4. If d=0, then no detailed timing descriptors are provided, and no data is provided in the reserved data block. Version 3: d = offset for the byte following the data block collection. If no data is provided in the data block collection, then d=4. If d=0, then no detailed timing descriptors are provided, and no data is provided in the data block collection. Ever since commit 9e50b9d55e9c ("drm: edid: Add some bounds checking"), we've interpreted 0 to mean there are no DTDs but it's all Data Blocks. Per the spec, Data Blocks are only valid for version 3, where we should interpret 0 to mean there are no data blocks. Follow the spec (and hope the EDIDs follow it too). Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/2a4c94417f024cbafc5d4ca0a74e4617fc4325d1.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 929fc0e46751..c57f6333ea7d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4498,8 +4498,6 @@ static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter) iter->index = 4; iter->end = ext[2]; - if (iter->end == 0) - iter->end = 127; if (iter->end < 4 || iter->end > 127) continue; From 11a8d095d86854bbc8697525f2c543de36bf5804 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:32 +0300 Subject: [PATCH 45/63] drm/edid: abstract cea data block collection size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to get the cea data block collection size. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/5339ab3249400a3c41001967e7ff2611b58e0425.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c57f6333ea7d..002816509fc8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4482,6 +4482,20 @@ __cea_db_iter_current_block(const struct cea_db_iter *iter) return NULL; } +/* + * References: + * - CTA-861-H section 7.3.3 CTA Extension Version 3 + */ +static int cea_db_collection_size(const u8 *cta) +{ + u8 d = cta[2]; + + if (d < 4 || d > 127) + return 0; + + return d - 4; +} + /* * References: * - VESA E-EDID v1.4 @@ -4492,15 +4506,19 @@ static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter) const u8 *ext; drm_edid_iter_for_each(ext, &iter->edid_iter) { + int size; + /* Only support CTA Extension revision 3+ */ if (ext[0] != CEA_EXT || cea_revision(ext) < 3) continue; - iter->index = 4; - iter->end = ext[2]; - if (iter->end < 4 || iter->end > 127) + size = cea_db_collection_size(ext); + if (!size) continue; + iter->index = 4; + iter->end = iter->index + size; + return ext; } From d9307f27efa531f034862bccf2985c5fa22c4b80 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:33 +0300 Subject: [PATCH 46/63] drm/edid: add block count and data helper functions for drm_edid Add drm_edid based block count and data access helper functions that take the EDID allocated size into account. At the moment, the allocated size should always match the EDID size indicated by the extension count, but this will change in the future. Signed-off-by: Jani Nikula Reviewed-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1a68c8667a88e7c451b001ad8bd86c8badb57fb8.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 42 +++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 002816509fc8..f44ada4bfa5b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1613,6 +1613,35 @@ static const void *edid_extension_block_data(const struct edid *edid, int index) return edid_block_data(edid, index + 1); } +static int drm_edid_block_count(const struct drm_edid *drm_edid) +{ + int num_blocks; + + /* Starting point */ + num_blocks = edid_block_count(drm_edid->edid); + + /* Limit by allocated size */ + num_blocks = min(num_blocks, (int)drm_edid->size / EDID_LENGTH); + + return num_blocks; +} + +static int drm_edid_extension_block_count(const struct drm_edid *drm_edid) +{ + return drm_edid_block_count(drm_edid) - 1; +} + +static const void *drm_edid_block_data(const struct drm_edid *drm_edid, int index) +{ + return edid_block_data(drm_edid->edid, index); +} + +static const void *drm_edid_extension_block_data(const struct drm_edid *drm_edid, + int index) +{ + return edid_extension_block_data(drm_edid->edid, index); +} + /* * Initializer helper for legacy interfaces, where we have no choice but to * trust edid size. Not for general purpose use. @@ -1665,8 +1694,8 @@ static const void *__drm_edid_iter_next(struct drm_edid_iter *iter) if (!iter->drm_edid) return NULL; - if (iter->index < edid_block_count(iter->drm_edid->edid)) - block = edid_block_data(iter->drm_edid->edid, iter->index++); + if (iter->index < drm_edid_block_count(iter->drm_edid)) + block = drm_edid_block_data(iter->drm_edid, iter->index++); return block; } @@ -3574,22 +3603,21 @@ static int add_detailed_modes(struct drm_connector *connector, const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index) { - const struct edid *edid = drm_edid ? drm_edid->edid : NULL; const u8 *edid_ext = NULL; int i; /* No EDID or EDID extensions */ - if (!edid || !edid_extension_block_count(edid)) + if (!drm_edid || !drm_edid_extension_block_count(drm_edid)) return NULL; /* Find CEA extension */ - for (i = *ext_index; i < edid_extension_block_count(edid); i++) { - edid_ext = edid_extension_block_data(edid, i); + for (i = *ext_index; i < drm_edid_extension_block_count(drm_edid); i++) { + edid_ext = drm_edid_extension_block_data(drm_edid, i); if (edid_block_tag(edid_ext) == ext_id) break; } - if (i >= edid_extension_block_count(edid)) + if (i >= drm_edid_extension_block_count(drm_edid)) return NULL; *ext_index = i + 1; From 407d63b3af881d17c39f16cdd8416adb0e4658bc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:34 +0300 Subject: [PATCH 47/63] drm/edid: keep track of alloc size in drm_do_get_edid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll want to return the allocated buffer size in the future. Keep track of it. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/8e4261d8c2947ea99240ea929f09a04878235f4e.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index f44ada4bfa5b..2beaa48301c1 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2021,13 +2021,16 @@ bool drm_edid_is_valid(struct edid *edid) EXPORT_SYMBOL(drm_edid_is_valid); static struct edid *edid_filter_invalid_blocks(const struct edid *edid, - int invalid_blocks) + int invalid_blocks, + size_t *alloc_size) { struct edid *new, *dest_block; int valid_extensions = edid->extensions - invalid_blocks; int i; - new = kmalloc(edid_size_by_blocks(valid_extensions + 1), GFP_KERNEL); + *alloc_size = edid_size_by_blocks(valid_extensions + 1); + + new = kmalloc(*alloc_size, GFP_KERNEL); if (!new) goto out; @@ -2140,7 +2143,8 @@ static void connector_bad_edid(struct drm_connector *connector, } /* Get override or firmware EDID */ -static struct edid *drm_get_override_edid(struct drm_connector *connector) +static struct edid *drm_get_override_edid(struct drm_connector *connector, + size_t *alloc_size) { struct edid *override = NULL; @@ -2150,6 +2154,10 @@ static struct edid *drm_get_override_edid(struct drm_connector *connector) if (!override) override = drm_load_edid_firmware(connector); + /* FIXME: Get alloc size from deeper down the stack */ + if (!IS_ERR_OR_NULL(override) && alloc_size) + *alloc_size = edid_size(override); + return IS_ERR(override) ? NULL : override; } @@ -2169,7 +2177,7 @@ int drm_add_override_edid_modes(struct drm_connector *connector) struct edid *override; int num_modes = 0; - override = drm_get_override_edid(connector); + override = drm_get_override_edid(connector, NULL); if (override) { drm_connector_update_edid_property(connector, override); num_modes = drm_add_edid_modes(connector, override); @@ -2245,12 +2253,13 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, enum edid_block_status status; int i, invalid_blocks = 0; struct edid *edid, *new; + size_t alloc_size = EDID_LENGTH; - edid = drm_get_override_edid(connector); + edid = drm_get_override_edid(connector, &alloc_size); if (edid) goto ok; - edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + edid = kmalloc(alloc_size, GFP_KERNEL); if (!edid) return NULL; @@ -2278,7 +2287,8 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, if (!edid_extension_block_count(edid)) goto ok; - new = krealloc(edid, edid_size(edid), GFP_KERNEL); + alloc_size = edid_size(edid); + new = krealloc(edid, alloc_size, GFP_KERNEL); if (!new) goto fail; edid = new; @@ -2300,7 +2310,8 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, if (invalid_blocks) { connector_bad_edid(connector, edid, edid_block_count(edid)); - edid = edid_filter_invalid_blocks(edid, invalid_blocks); + edid = edid_filter_invalid_blocks(edid, invalid_blocks, + &alloc_size); } ok: From 6537f79a2aae65748d402d6c46e79d9e473d42a9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:35 +0300 Subject: [PATCH 48/63] drm/edid: add new interfaces around struct drm_edid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new functions drm_edid_read(), drm_edid_read_ddc(), and drm_edid_read_custom() to replace drm_get_edid() and drm_do_get_edid() for reading the EDID. The transition is expected to happen over a fairly long time. Note that the new drm_edid_read*() functions do not do any of the connector updates anymore. The reading and parsing will be completely separated from each other. Add new functions drm_edid_alloc(), drm_edid_dup(), and drm_edid_free() for allocating and freeing drm_edid containers. Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/5a6532a94cad6a79424f6d1918dbe7b7d607ac03.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 245 +++++++++++++++++++++++++++++++++---- include/drm/drm_edid.h | 9 ++ 2 files changed, 230 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 2beaa48301c1..2bdaf1e34a9d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2226,29 +2226,9 @@ static enum edid_block_status edid_block_read(void *block, unsigned int block_nu return status; } -/** - * drm_do_get_edid - get EDID data using a custom EDID block read function - * @connector: connector we're probing - * @read_block: EDID block read function - * @context: private data passed to the block read function - * - * When the I2C adapter connected to the DDC bus is hidden behind a device that - * exposes a different interface to read EDID blocks this function can be used - * to get EDID data using a custom block read function. - * - * As in the general case the DDC bus is accessible by the kernel at the I2C - * level, drivers must make all reasonable efforts to expose it as an I2C - * adapter and use drm_get_edid() instead of abusing this function. - * - * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority - * order. Having either of them bypasses actual EDID reads. - * - * Return: Pointer to valid EDID or NULL if we couldn't find any. - */ -struct edid *drm_do_get_edid(struct drm_connector *connector, - read_block_fn read_block, - void *context) +static struct edid *_drm_do_get_edid(struct drm_connector *connector, + read_block_fn read_block, void *context, + size_t *size) { enum edid_block_status status; int i, invalid_blocks = 0; @@ -2315,14 +2295,125 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, } ok: + if (size) + *size = alloc_size; + return edid; fail: kfree(edid); return NULL; } + +/** + * drm_do_get_edid - get EDID data using a custom EDID block read function + * @connector: connector we're probing + * @read_block: EDID block read function + * @context: private data passed to the block read function + * + * When the I2C adapter connected to the DDC bus is hidden behind a device that + * exposes a different interface to read EDID blocks this function can be used + * to get EDID data using a custom block read function. + * + * As in the general case the DDC bus is accessible by the kernel at the I2C + * level, drivers must make all reasonable efforts to expose it as an I2C + * adapter and use drm_get_edid() instead of abusing this function. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * Return: Pointer to valid EDID or NULL if we couldn't find any. + */ +struct edid *drm_do_get_edid(struct drm_connector *connector, + read_block_fn read_block, + void *context) +{ + return _drm_do_get_edid(connector, read_block, context, NULL); +} EXPORT_SYMBOL_GPL(drm_do_get_edid); +/* Allocate struct drm_edid container *without* duplicating the edid data */ +static const struct drm_edid *_drm_edid_alloc(const void *edid, size_t size) +{ + struct drm_edid *drm_edid; + + if (!edid || !size || size < EDID_LENGTH) + return NULL; + + drm_edid = kzalloc(sizeof(*drm_edid), GFP_KERNEL); + if (drm_edid) { + drm_edid->edid = edid; + drm_edid->size = size; + } + + return drm_edid; +} + +/** + * drm_edid_alloc - Allocate a new drm_edid container + * @edid: Pointer to raw EDID data + * @size: Size of memory allocated for EDID + * + * Allocate a new drm_edid container. Do not calculate edid size from edid, pass + * the actual size that has been allocated for the data. There is no validation + * of the raw EDID data against the size, but at least the EDID base block must + * fit in the buffer. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: drm_edid container, or NULL on errors + */ +const struct drm_edid *drm_edid_alloc(const void *edid, size_t size) +{ + const struct drm_edid *drm_edid; + + if (!edid || !size || size < EDID_LENGTH) + return NULL; + + edid = kmemdup(edid, size, GFP_KERNEL); + if (!edid) + return NULL; + + drm_edid = _drm_edid_alloc(edid, size); + if (!drm_edid) + kfree(edid); + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_alloc); + +/** + * drm_edid_dup - Duplicate a drm_edid container + * @drm_edid: EDID to duplicate + * + * The returned pointer must be freed using drm_edid_free(). + * + * Returns: drm_edid container copy, or NULL on errors + */ +const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid) +{ + if (!drm_edid) + return NULL; + + return drm_edid_alloc(drm_edid->edid, drm_edid->size); +} +EXPORT_SYMBOL(drm_edid_dup); + +/** + * drm_edid_free - Free the drm_edid container + * @drm_edid: EDID to free + */ +void drm_edid_free(const struct drm_edid *drm_edid) +{ + if (!drm_edid) + return; + + kfree(drm_edid->edid); + kfree(drm_edid); +} +EXPORT_SYMBOL(drm_edid_free); + /** * drm_probe_ddc() - probe DDC presence * @adapter: I2C adapter to probe @@ -2359,12 +2450,118 @@ struct edid *drm_get_edid(struct drm_connector *connector, if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) return NULL; - edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); drm_connector_update_edid_property(connector, edid); return edid; } EXPORT_SYMBOL(drm_get_edid); +/** + * drm_edid_read_custom - Read EDID data using given EDID block read function + * @connector: Connector to use + * @read_block: EDID block read function + * @context: Private data passed to the block read function + * + * When the I2C adapter connected to the DDC bus is hidden behind a device that + * exposes a different interface to read EDID blocks this function can be used + * to get EDID data using a custom block read function. + * + * As in the general case the DDC bus is accessible by the kernel at the I2C + * level, drivers must make all reasonable efforts to expose it as an I2C + * adapter and use drm_edid_read() or drm_edid_read_ddc() instead of abusing + * this function. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, + read_block_fn read_block, + void *context) +{ + const struct drm_edid *drm_edid; + struct edid *edid; + size_t size = 0; + + edid = _drm_do_get_edid(connector, read_block, context, &size); + if (!edid) + return NULL; + + /* Sanity check for now */ + drm_WARN_ON(connector->dev, !size); + + drm_edid = _drm_edid_alloc(edid, size); + if (!drm_edid) + kfree(edid); + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_custom); + +/** + * drm_edid_read_ddc - Read EDID data using given I2C adapter + * @connector: Connector to use + * @adapter: I2C adapter to use for DDC + * + * Read EDID using the given I2C adapter. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * Prefer initializing connector->ddc with drm_connector_init_with_ddc() and + * using drm_edid_read() instead of this function. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, + struct i2c_adapter *adapter) +{ + const struct drm_edid *drm_edid; + + if (connector->force == DRM_FORCE_OFF) + return NULL; + + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) + return NULL; + + drm_edid = drm_edid_read_custom(connector, drm_do_probe_ddc_edid, adapter); + + /* Note: Do *not* call connector updates here. */ + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_ddc); + +/** + * drm_edid_read - Read EDID data using connector's I2C adapter + * @connector: Connector to use + * + * Read EDID using the connector's I2C adapter. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read(struct drm_connector *connector) +{ + if (drm_WARN_ON(connector->dev, !connector->ddc)) + return NULL; + + return drm_edid_read_ddc(connector, connector->ddc); +} +EXPORT_SYMBOL(drm_edid_read); + static u32 edid_extract_panel_id(const struct edid *edid) { /* diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 1d5950b5b407..c592d2c1afed 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -594,6 +594,15 @@ drm_display_mode_from_cea_vic(struct drm_device *dev, u8 video_code); /* Interface based on struct drm_edid */ +const struct drm_edid *drm_edid_alloc(const void *edid, size_t size); +const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid); +void drm_edid_free(const struct drm_edid *drm_edid); +const struct drm_edid *drm_edid_read(struct drm_connector *connector); +const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, + struct i2c_adapter *adapter); +const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, + int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len), + void *context); const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index); From 019fd800cf4a4514bbb502a52d376b01f02cc347 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:37 +0300 Subject: [PATCH 49/63] drm/probe-helper: abstract .get_modes() connector helper call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract the .get_modes() connector helper call, including the override/firmware EDID fallback, for clarity. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/a4de51efc246e4f5bcbf7b84d66bb49aaf7fd974.1654674560.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_probe_helper.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 75a71649b64d..a8d26b29bfa0 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -354,6 +354,24 @@ drm_helper_probe_detect(struct drm_connector *connector, } EXPORT_SYMBOL(drm_helper_probe_detect); +static int drm_helper_probe_get_modes(struct drm_connector *connector) +{ + const struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + int count; + + count = connector_funcs->get_modes(connector); + + /* + * Fallback for when DDC probe failed in drm_get_edid() and thus skipped + * override/firmware EDID. + */ + if (count == 0 && connector->status == connector_status_connected) + count = drm_add_override_edid_modes(connector); + + return count; +} + static int __drm_helper_update_and_validate(struct drm_connector *connector, uint32_t maxX, uint32_t maxY, struct drm_modeset_acquire_ctx *ctx) @@ -473,8 +491,6 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, { struct drm_device *dev = connector->dev; struct drm_display_mode *mode; - const struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; int count = 0, ret; enum drm_connector_status old_status; struct drm_modeset_acquire_ctx ctx; @@ -559,14 +575,7 @@ retry: goto exit; } - count = (*connector_funcs->get_modes)(connector); - - /* - * Fallback for when DDC probe failed in drm_get_edid() and thus skipped - * override/firmware EDID. - */ - if (count == 0 && connector->status == connector_status_connected) - count = drm_add_override_edid_modes(connector); + count = drm_helper_probe_get_modes(connector); if (count == 0 && (connector->status == connector_status_connected || connector->status == connector_status_unknown)) { From 53b93c0ff71ac6f97c1ab2ad84ee8b540f40cb95 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 12 Jun 2022 16:33:49 +0200 Subject: [PATCH 50/63] drm/msm: Fix convert to drm_of_get_data_lanes_count Add missing header file into dsi_host.c and encode data-lanes string directly into the warning message in the driver to avoid build issues detected by lkp. Fixes: 185443efa26a ("drm/msm: Convert to drm_of_get_data_lanes_count") Reported-by: kernel test robot Signed-off-by: Marek Vasut Cc: Abhinav Kumar Cc: Andrzej Hajda Cc: Dmitry Baryshkov Cc: Laurent Pinchart Cc: Lucas Stach Cc: Maxime Ripard Cc: Rob Clark Cc: Robert Foss Cc: Sam Ravnborg Cc: Sean Paul To: dri-devel@lists.freedesktop.org Reviewed-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/20220612143349.105766-1-marex@denx.de --- drivers/gpu/drm/msm/dp/dp_parser.c | 4 ++-- drivers/gpu/drm/msm/dsi/dsi_host.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index fabd18e7dc0d..6088d70f22e5 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -106,8 +106,8 @@ static int dp_parser_misc(struct dp_parser *parser) len = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES); if (len < 0) { - DRM_WARN("Invalid property %s, default max DP lanes = %d\n", - data_lane_property, DP_MAX_NUM_DP_LANES); + DRM_WARN("Invalid property \"data-lanes\", default max DP lanes = %d\n", + DP_MAX_NUM_DP_LANES); len = DP_MAX_NUM_DP_LANES; } diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 63969e464dbe..c4a24ae00d94 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -21,6 +21,8 @@ #include