From 21613f67ede11e495281b4a6dde72cd7db3ada4e Mon Sep 17 00:00:00 2001 From: Sebastian Brzezinka Date: Tue, 3 Mar 2026 13:26:42 +0100 Subject: [PATCH 01/62] drm/ttm: fix NULL deref in ttm_bo_flush_all_fences() after fence ops detach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 541c8f2468b9 ("dma-buf: detach fence ops on signal v3"), fence->ops may be set to NULL via RCU when a fence signals and has no release/wait ops. ttm_bo_flush_all_fences() was not updated to handle this and directly dereferences fence->ops->signaled, leading to a NULL pointer dereference crash: ``` BUG: kernel NULL pointer dereference, address: 0000000000000018 RIP: 0010:ttm_bo_release+0x1bc/0x330 [ttm] ``` Since dma_fence_enable_sw_signaling() already handles the signaled case internally (it checks DMA_FENCE_FLAG_SIGNALED_BIT before doing anything), the ops->signaled pre-check is redundant. Simply remove it and call dma_fence_enable_sw_signaling() unconditionally for each fence. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15759 Fixes: 541c8f2468b9 ("dma-buf: detach fence ops on signal v3") Cc: Christian König Signed-off-by: Sebastian Brzezinka Reviewed-by: Christian König Reviewed-by: Andi Shyti Signed-off-by: Christian König Link: https://lore.kernel.org/r/1c2f34351b6fb70ab576aeac07987542a4d480b2.1772540459.git.sebastian.brzezinka@intel.com --- drivers/gpu/drm/ttm/ttm_bo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index acb9197db879..0485ad00a3df 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -222,10 +222,8 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) struct dma_fence *fence; dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP); - dma_resv_for_each_fence_unlocked(&cursor, fence) { - if (!fence->ops->signaled) - dma_fence_enable_sw_signaling(fence); - } + dma_resv_for_each_fence_unlocked(&cursor, fence) + dma_fence_enable_sw_signaling(fence); dma_resv_iter_end(&cursor); } From e597a809a2b97e927060ba182f58eb3e6101bc70 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 23 Feb 2026 16:53:39 +0100 Subject: [PATCH 02/62] drm/vc4: platform_get_irq_byname() returns an int MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit platform_get_irq_byname() will return a negative value if an error happens, so it should be checked and not just passed directly into devm_request_threaded_irq() hoping all will be ok. Cc: Maxime Ripard Cc: Dave Stevenson Cc: Maíra Canal Cc: Raspberry Pi Kernel Maintenance Cc: Maarten Lankhorst Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: stable Assisted-by: gkh_clanker_2000 Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026022339-cornflake-t-shirt-2471@gregkh Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_hdmi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 9ce90a694c3c..9898e5451a07 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2449,17 +2449,23 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi) int ret; if (vc4_hdmi->variant->external_irq_controller) { - unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected"); - unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed"); + int hpd = platform_get_irq_byname(pdev, "hpd-connected"); - ret = devm_request_threaded_irq(&pdev->dev, hpd_con, + if (hpd < 0) + return hpd; + + ret = devm_request_threaded_irq(&pdev->dev, hpd, NULL, vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, "vc4 hdmi hpd connected", vc4_hdmi); if (ret) return ret; - ret = devm_request_threaded_irq(&pdev->dev, hpd_rm, + hpd = platform_get_irq_byname(pdev, "hpd-removed"); + if (hpd < 0) + return hpd; + + ret = devm_request_threaded_irq(&pdev->dev, hpd, NULL, vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, "vc4 hdmi hpd disconnected", vc4_hdmi); From 493740d790cce709d285cd1022d16d05439b7d5b Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Fri, 6 Mar 2026 11:31:54 +0530 Subject: [PATCH 03/62] drm/buddy: Improve offset-aligned allocation handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Large alignment requests previously forced the buddy allocator to search by alignment order, which often caused higher-order free blocks to be split even when a suitably aligned smaller region already existed within them. This led to excessive fragmentation, especially for workloads requesting small sizes with large alignment constraints. This change prioritizes the requested allocation size during the search and uses an augmented RB-tree field (subtree_max_alignment) to efficiently locate free blocks that satisfy both size and offset-alignment requirements. As a result, the allocator can directly select an aligned sub-region without splitting larger blocks unnecessarily. A practical example is the VKCTS test dEQP-VK.memory.allocation.basic.size_8KiB.reverse.count_4000, which repeatedly allocates 8 KiB buffers with a 256 KiB alignment. Previously, such allocations caused large blocks to be split aggressively, despite smaller aligned regions being sufficient. With this change, those aligned regions are reused directly, significantly reducing fragmentation. This improvement is visible in the amdgpu VRAM buddy allocator state (/sys/kernel/debug/dri/1/amdgpu_vram_mm). After the change, higher-order blocks are preserved and the number of low-order fragments is substantially reduced. Before: order- 5 free: 1936 MiB, blocks: 15490 order- 4 free: 967 MiB, blocks: 15486 order- 3 free: 483 MiB, blocks: 15485 order- 2 free: 241 MiB, blocks: 15486 order- 1 free: 241 MiB, blocks: 30948 After: order- 5 free: 493 MiB, blocks: 3941 order- 4 free: 246 MiB, blocks: 3943 order- 3 free: 123 MiB, blocks: 4101 order- 2 free: 61 MiB, blocks: 4101 order- 1 free: 61 MiB, blocks: 8018 By avoiding unnecessary splits, this change improves allocator efficiency and helps maintain larger contiguous free regions under heavy offset-aligned allocation workloads. v2:(Matthew) - Update augmented information along the path to the inserted node. v3: - Move the patch to gpu/buddy.c file. v4:(Matthew) - Use the helper instead of calling _ffs directly - Remove gpu_buddy_block_order(block) >= order check and drop order - Drop !node check as all callers handle this already - Return larger than any other possible alignment for __ffs64(0) - Replace __ffs with __ffs64 v5:(Matthew) - Drop subtree_max_alignment initialization at gpu_block_alloc() Signed-off-by: Arunpravin Paneer Selvam Suggested-by: Christian König Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260306060155.2114-1-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/buddy.c | 274 +++++++++++++++++++++++++++++++------- include/linux/gpu_buddy.h | 2 + 2 files changed, 230 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index da5a1222f46b..52686672e99f 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -53,6 +53,25 @@ gpu_buddy_block_is_split(struct gpu_buddy_block *block) return gpu_buddy_block_state(block) == GPU_BUDDY_SPLIT; } +static unsigned int gpu_buddy_block_offset_alignment(struct gpu_buddy_block *block) +{ + u64 offset = gpu_buddy_block_offset(block); + + if (!offset) + /* + * __ffs64(0) is undefined; offset 0 is maximally aligned, so return + * a value greater than any possible alignment. + */ + return 64 + 1; + + return __ffs64(offset); +} + +RB_DECLARE_CALLBACKS_MAX(static, gpu_buddy_augment_cb, + struct gpu_buddy_block, rb, + unsigned int, subtree_max_alignment, + gpu_buddy_block_offset_alignment); + static struct gpu_buddy_block *gpu_block_alloc(struct gpu_buddy *mm, struct gpu_buddy_block *parent, unsigned int order, @@ -106,26 +125,42 @@ static bool rbtree_is_empty(struct rb_root *root) return RB_EMPTY_ROOT(root); } -static bool gpu_buddy_block_offset_less(const struct gpu_buddy_block *block, - const struct gpu_buddy_block *node) -{ - return gpu_buddy_block_offset(block) < gpu_buddy_block_offset(node); -} - -static bool rbtree_block_offset_less(struct rb_node *block, - const struct rb_node *node) -{ - return gpu_buddy_block_offset_less(rbtree_get_free_block(block), - rbtree_get_free_block(node)); -} - static void rbtree_insert(struct gpu_buddy *mm, struct gpu_buddy_block *block, enum gpu_buddy_free_tree tree) { - rb_add(&block->rb, - &mm->free_trees[tree][gpu_buddy_block_order(block)], - rbtree_block_offset_less); + struct rb_node **link, *parent = NULL; + unsigned int block_alignment, order; + struct gpu_buddy_block *node; + struct rb_root *root; + + order = gpu_buddy_block_order(block); + block_alignment = gpu_buddy_block_offset_alignment(block); + + root = &mm->free_trees[tree][order]; + link = &root->rb_node; + + while (*link) { + parent = *link; + node = rbtree_get_free_block(parent); + /* + * Manual augmentation update during insertion traversal. Required + * because rb_insert_augmented() only calls rotate callback during + * rotations. This ensures all ancestors on the insertion path have + * correct subtree_max_alignment values. + */ + if (node->subtree_max_alignment < block_alignment) + node->subtree_max_alignment = block_alignment; + + if (gpu_buddy_block_offset(block) < gpu_buddy_block_offset(node)) + link = &parent->rb_left; + else + link = &parent->rb_right; + } + + block->subtree_max_alignment = block_alignment; + rb_link_node(&block->rb, parent, link); + rb_insert_augmented(&block->rb, root, &gpu_buddy_augment_cb); } static void rbtree_remove(struct gpu_buddy *mm, @@ -138,7 +173,7 @@ static void rbtree_remove(struct gpu_buddy *mm, tree = get_block_tree(block); root = &mm->free_trees[tree][order]; - rb_erase(&block->rb, root); + rb_erase_augmented(&block->rb, root, &gpu_buddy_augment_cb); RB_CLEAR_NODE(&block->rb); } @@ -811,6 +846,127 @@ alloc_from_freetree(struct gpu_buddy *mm, return ERR_PTR(err); } +static bool +gpu_buddy_can_offset_align(u64 size, u64 min_block_size) +{ + return size < min_block_size && is_power_of_2(size); +} + +static bool gpu_buddy_subtree_can_satisfy(struct rb_node *node, + unsigned int alignment) +{ + struct gpu_buddy_block *block; + + block = rbtree_get_free_block(node); + return block->subtree_max_alignment >= alignment; +} + +static struct gpu_buddy_block * +gpu_buddy_find_block_aligned(struct gpu_buddy *mm, + enum gpu_buddy_free_tree tree, + unsigned int order, + unsigned int alignment, + unsigned long flags) +{ + struct rb_root *root = &mm->free_trees[tree][order]; + struct rb_node *rb = root->rb_node; + + while (rb) { + struct gpu_buddy_block *block = rbtree_get_free_block(rb); + struct rb_node *left_node = rb->rb_left, *right_node = rb->rb_right; + + if (right_node) { + if (gpu_buddy_subtree_can_satisfy(right_node, alignment)) { + rb = right_node; + continue; + } + } + + if (gpu_buddy_block_offset_alignment(block) >= alignment) + return block; + + if (left_node) { + if (gpu_buddy_subtree_can_satisfy(left_node, alignment)) { + rb = left_node; + continue; + } + } + + break; + } + + return NULL; +} + +static struct gpu_buddy_block * +gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, + u64 size, + u64 min_block_size, + unsigned long flags) +{ + struct gpu_buddy_block *block = NULL; + unsigned int order, tmp, alignment; + struct gpu_buddy_block *buddy; + enum gpu_buddy_free_tree tree; + unsigned long pages; + int err; + + alignment = ilog2(min_block_size); + pages = size >> ilog2(mm->chunk_size); + order = fls(pages) - 1; + + tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ? + GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; + + for (tmp = order; tmp <= mm->max_order; ++tmp) { + block = gpu_buddy_find_block_aligned(mm, tree, tmp, + alignment, flags); + if (!block) { + tree = (tree == GPU_BUDDY_CLEAR_TREE) ? + GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; + block = gpu_buddy_find_block_aligned(mm, tree, tmp, + alignment, flags); + } + + if (block) + break; + } + + if (!block) + return ERR_PTR(-ENOSPC); + + while (gpu_buddy_block_order(block) > order) { + struct gpu_buddy_block *left, *right; + + err = split_block(mm, block); + if (unlikely(err)) + goto err_undo; + + left = block->left; + right = block->right; + + if (gpu_buddy_block_offset_alignment(right) >= alignment) + block = right; + else + block = left; + } + + return block; + +err_undo: + /* + * We really don't want to leave around a bunch of split blocks, since + * bigger is better, so make sure we merge everything back before we + * free the allocated blocks. + */ + buddy = __get_buddy(block); + if (buddy && + (gpu_buddy_block_is_free(block) && + gpu_buddy_block_is_free(buddy))) + __gpu_buddy_free(mm, block, false); + return ERR_PTR(err); +} + static int __alloc_range(struct gpu_buddy *mm, struct list_head *dfs, u64 start, u64 size, @@ -1080,6 +1236,7 @@ EXPORT_SYMBOL(gpu_buddy_block_trim); static struct gpu_buddy_block * __gpu_buddy_alloc_blocks(struct gpu_buddy *mm, u64 start, u64 end, + u64 size, u64 min_block_size, unsigned int order, unsigned long flags) { @@ -1087,6 +1244,11 @@ __gpu_buddy_alloc_blocks(struct gpu_buddy *mm, /* Allocate traversing within the range */ return __gpu_buddy_alloc_range_bias(mm, start, end, order, flags); + else if (size < min_block_size) + /* Allocate from an offset-aligned region without size rounding */ + return gpu_buddy_offset_aligned_allocation(mm, size, + min_block_size, + flags); else /* Allocate from freetree */ return alloc_from_freetree(mm, order, flags); @@ -1158,8 +1320,11 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) { size = roundup_pow_of_two(size); min_block_size = size; - /* Align size value to min_block_size */ - } else if (!IS_ALIGNED(size, min_block_size)) { + /* + * Normalize the requested size to min_block_size for regular allocations. + * Offset-aligned allocations intentionally skip size rounding. + */ + } else if (!gpu_buddy_can_offset_align(size, min_block_size)) { size = round_up(size, min_block_size); } @@ -1179,43 +1344,60 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, do { order = min(order, (unsigned int)fls(pages) - 1); BUG_ON(order > mm->max_order); - BUG_ON(order < min_order); + /* + * Regular allocations must not allocate blocks smaller than min_block_size. + * Offset-aligned allocations deliberately bypass this constraint. + */ + BUG_ON(size >= min_block_size && order < min_order); do { + unsigned int fallback_order; + block = __gpu_buddy_alloc_blocks(mm, start, end, + size, + min_block_size, order, flags); if (!IS_ERR(block)) break; - if (order-- == min_order) { - /* Try allocation through force merge method */ - if (mm->clear_avail && - !__force_merge(mm, start, end, min_order)) { - block = __gpu_buddy_alloc_blocks(mm, start, - end, - min_order, - flags); - if (!IS_ERR(block)) { - order = min_order; - break; - } - } - - /* - * Try contiguous block allocation through - * try harder method. - */ - if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION && - !(flags & GPU_BUDDY_RANGE_ALLOCATION)) - return __alloc_contig_try_harder(mm, - original_size, - original_min_size, - blocks); - err = -ENOSPC; - goto err_free; + if (size < min_block_size) { + fallback_order = order; + } else if (order == min_order) { + fallback_order = min_order; + } else { + order--; + continue; } + + /* Try allocation through force merge method */ + if (mm->clear_avail && + !__force_merge(mm, start, end, fallback_order)) { + block = __gpu_buddy_alloc_blocks(mm, start, + end, + size, + min_block_size, + fallback_order, + flags); + if (!IS_ERR(block)) { + order = fallback_order; + break; + } + } + + /* + * Try contiguous block allocation through + * try harder method. + */ + if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION && + !(flags & GPU_BUDDY_RANGE_ALLOCATION)) + return __alloc_contig_try_harder(mm, + original_size, + original_min_size, + blocks); + err = -ENOSPC; + goto err_free; } while (1); mark_allocated(mm, block); diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index f1fb6eff604a..5fa917ba5450 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -11,6 +11,7 @@ #include #include #include +#include /** * GPU_BUDDY_RANGE_ALLOCATION - Allocate within a specific address range @@ -128,6 +129,7 @@ struct gpu_buddy_block { }; /* private: */ struct list_head tmp_link; + unsigned int subtree_max_alignment; }; /* Order-zero must be at least SZ_4K */ From f5bd7da05a5988506dedcb3e67aecb3a13a4cdf0 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Fri, 6 Mar 2026 11:31:55 +0530 Subject: [PATCH 04/62] drm/buddy: Add KUnit test for offset-aligned allocations Add KUnit test to validate offset-aligned allocations in the DRM buddy allocator. Validate offset-aligned allocation: The test covers allocations with sizes smaller than the alignment constraint and verifies correct size preservation, offset alignment, and behavior across multiple allocation sizes. It also exercises fragmentation by freeing alternating blocks and confirms that allocation fails once all aligned offsets are consumed. Stress subtree_max_alignment propagation: Exercise subtree_max_alignment tracking by allocating blocks with descending alignment constraints and freeing them in reverse order. This verifies that free-tree augmentation correctly propagates the maximum offset alignment present in each subtree at every stage. v2: - Move the patch to gpu/tests/gpu_buddy_test.c file. v3: - Fixed build warnings reported by kernel test robot v4:(Matthew) - Use IS_ALIGNED() instead of manual alignment checks - Simplify order iteration loop for readability - Remove extra newline Signed-off-by: Arunpravin Paneer Selvam Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260306060155.2114-2-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/tests/gpu_buddy_test.c | 166 +++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c index 5429010f34d3..6fdb51ba4602 100644 --- a/drivers/gpu/tests/gpu_buddy_test.c +++ b/drivers/gpu/tests/gpu_buddy_test.c @@ -21,6 +21,170 @@ static inline u64 get_size(int order, u64 chunk_size) return (1 << order) * chunk_size; } +static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test) +{ + struct gpu_buddy_block *block; + struct rb_node *node = NULL; + const u64 mm_size = SZ_2M; + const u64 alignments[] = { + SZ_1M, + SZ_512K, + SZ_256K, + SZ_128K, + SZ_64K, + SZ_32K, + SZ_16K, + SZ_8K, + }; + struct list_head allocated[ARRAY_SIZE(alignments)]; + unsigned int i, max_subtree_align = 0; + int ret, tree, order; + struct gpu_buddy mm; + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), + "buddy_init failed\n"); + + for (i = 0; i < ARRAY_SIZE(allocated); i++) + INIT_LIST_HEAD(&allocated[i]); + + /* + * Exercise subtree_max_alignment tracking by allocating blocks with descending + * alignment constraints and freeing them in reverse order. This verifies that + * free-tree augmentation correctly propagates the maximum offset alignment + * present in each subtree at every stage. + */ + + for (i = 0; i < ARRAY_SIZE(alignments); i++) { + struct gpu_buddy_block *root = NULL; + unsigned int expected; + u64 align; + + align = alignments[i]; + expected = ilog2(align) - 1; + + for (;;) { + ret = gpu_buddy_alloc_blocks(&mm, + 0, mm_size, + SZ_4K, align, + &allocated[i], + 0); + if (ret) + break; + + block = list_last_entry(&allocated[i], + struct gpu_buddy_block, + link); + KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), align)); + } + + for (order = mm.max_order; order >= 0 && !root; order--) { + for (tree = 0; tree < 2; tree++) { + node = mm.free_trees[tree][order].rb_node; + if (node) { + root = container_of(node, + struct gpu_buddy_block, + rb); + break; + } + } + } + + KUNIT_ASSERT_NOT_NULL(test, root); + KUNIT_EXPECT_EQ(test, root->subtree_max_alignment, expected); + } + + for (i = ARRAY_SIZE(alignments); i-- > 0; ) { + gpu_buddy_free_list(&mm, &allocated[i], 0); + + for (order = 0; order <= mm.max_order; order++) { + for (tree = 0; tree < 2; tree++) { + node = mm.free_trees[tree][order].rb_node; + if (!node) + continue; + + block = container_of(node, struct gpu_buddy_block, rb); + max_subtree_align = max(max_subtree_align, + block->subtree_max_alignment); + } + } + + KUNIT_EXPECT_GE(test, max_subtree_align, ilog2(alignments[i])); + } + + gpu_buddy_fini(&mm); +} + +static void gpu_test_buddy_offset_aligned_allocation(struct kunit *test) +{ + struct gpu_buddy_block *block, *tmp; + int num_blocks, i, count = 0; + LIST_HEAD(allocated); + struct gpu_buddy mm; + u64 mm_size = SZ_4M; + LIST_HEAD(freed); + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), + "buddy_init failed\n"); + + num_blocks = mm_size / SZ_256K; + /* + * Allocate multiple sizes under a fixed offset alignment. + * Ensures alignment handling is independent of allocation size and + * exercises subtree max-alignment pruning for small requests. + */ + for (i = 0; i < num_blocks; i++) + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_256K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_8K); + + list_for_each_entry(block, &allocated, link) { + /* Ensure the allocated block uses the expected 8 KB size */ + KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_8K); + /* Ensure the block starts at a 256 KB-aligned offset for proper alignment */ + KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K)); + } + gpu_buddy_free_list(&mm, &allocated, 0); + + for (i = 0; i < num_blocks; i++) + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_16K); + + list_for_each_entry(block, &allocated, link) { + /* Ensure the allocated block uses the expected 16 KB size */ + KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_16K); + /* Ensure the block starts at a 256 KB-aligned offset for proper alignment */ + KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K)); + } + + /* + * Free alternating aligned blocks to introduce fragmentation. + * Ensures offset-aligned allocations remain valid after frees and + * verifies subtree max-alignment metadata is correctly maintained. + */ + list_for_each_entry_safe(block, tmp, &allocated, link) { + if (count % 2 == 0) + list_move_tail(&block->link, &freed); + count++; + } + gpu_buddy_free_list(&mm, &freed, 0); + + for (i = 0; i < num_blocks / 2; i++) + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_16K); + + /* + * Allocate with offset alignment after all slots are used; must fail. + * Confirms that no aligned offsets remain. + */ + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, + &allocated, 0), + "buddy_alloc hit an error size=%u\n", SZ_16K); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); +} + static void gpu_test_buddy_fragmentation_performance(struct kunit *test) { struct gpu_buddy_block *block, *tmp; @@ -912,6 +1076,8 @@ static struct kunit_case gpu_buddy_tests[] = { KUNIT_CASE(gpu_test_buddy_alloc_range_bias), KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance), KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order), + KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation), + KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress), {} }; From bdca18a60ed7a332777c655d43bc99d0152b41c2 Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Mon, 2 Mar 2026 20:39:46 +0530 Subject: [PATCH 05/62] gpu/tests/gpu_buddy: Add gpu_test_buddy_alloc_range for exact-range allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new kunit test gpu_test_buddy_alloc_range() that exercises the __gpu_buddy_alloc_range() exact-range allocation path, triggered when start + size == end with flags=0. The test covers: - Basic exact-range allocation of the full mm - Exact-range allocation of equal sub-ranges (quarters) - Minimum chunk-size exact ranges at start, middle, and end offsets - Non power-of-two mm size with multiple roots, including cross-root exact-range allocation - Randomized exact-range allocations of N contiguous page-aligned slices in random order - Negative: partially allocated range must reject overlapping exact alloc - Negative: checkerboard allocation pattern rejects exact range over partially occupied pairs - Negative: misaligned start, unaligned size, and out-of-bounds end - Free and re-allocate the same exact range across multiple iterations - Various power-of-two exact ranges at natural alignment Cc: Christian König Cc: Arunpravin Paneer Selvam Suggested-by: Matthew Auld Signed-off-by: Sanjay Yadav Reviewed-by: Matthew Auld Signed-off-by: Arunpravin Paneer Selvam Link: https://patch.msgid.link/20260302150947.47535-2-sanjay.kumar.yadav@intel.com --- drivers/gpu/tests/gpu_buddy_test.c | 327 +++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c index 6fdb51ba4602..7df5c2ae83bb 100644 --- a/drivers/gpu/tests/gpu_buddy_test.c +++ b/drivers/gpu/tests/gpu_buddy_test.c @@ -526,6 +526,332 @@ static void gpu_test_buddy_alloc_range_bias(struct kunit *test) gpu_buddy_fini(&mm); } +static void gpu_test_buddy_alloc_range(struct kunit *test) +{ + GPU_RND_STATE(prng, random_seed); + struct gpu_buddy_block *block; + struct gpu_buddy mm; + u32 mm_size, total; + LIST_HEAD(blocks); + LIST_HEAD(tmp); + u32 ps = SZ_4K; + int ret; + + mm_size = SZ_16M; + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps), + "buddy_init failed\n"); + + /* + * Basic exact-range allocation. + * Allocate the entire mm as one exact range (start + size == end). + * This is the simplest case exercising __gpu_buddy_alloc_range. + */ + ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &blocks, 0); + KUNIT_ASSERT_EQ_MSG(test, ret, 0, + "exact-range alloc of full mm failed\n"); + + total = 0; + list_for_each_entry(block, &blocks, link) { + u64 offset = gpu_buddy_block_offset(block); + u64 bsize = gpu_buddy_block_size(&mm, block); + + KUNIT_EXPECT_TRUE_MSG(test, offset + bsize <= (u64)mm_size, + "block [%llx, %llx) outside mm\n", offset, offset + bsize); + total += (u32)bsize; + } + KUNIT_EXPECT_EQ(test, total, mm_size); + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + + /* Full mm should be exhausted */ + ret = gpu_buddy_alloc_blocks(&mm, 0, ps, ps, ps, &tmp, 0); + KUNIT_EXPECT_NE_MSG(test, ret, 0, "alloc should fail when mm is full\n"); + + gpu_buddy_free_list(&mm, &blocks, 0); + KUNIT_EXPECT_EQ(test, mm.avail, (u64)mm_size); + gpu_buddy_fini(&mm); + + /* + * Exact-range allocation of sub-ranges. + * Split the mm into four equal quarters and allocate each as an exact + * range. Validates splitting and non-overlapping exact allocations. + */ + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + { + u32 quarter = mm_size / 4; + int i; + + for (i = 0; i < 4; i++) { + u32 start = i * quarter; + u32 end = start + quarter; + + ret = gpu_buddy_alloc_blocks(&mm, start, end, quarter, ps, &blocks, 0); + KUNIT_ASSERT_EQ_MSG(test, ret, 0, + "exact-range alloc quarter %d [%x, %x) failed\n", + i, start, end); + } + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + gpu_buddy_free_list(&mm, &blocks, 0); + } + + gpu_buddy_fini(&mm); + + /* + * Minimum chunk-size exact range at various offsets. + * Allocate single-page exact ranges at the start, middle and end. + */ + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + ret = gpu_buddy_alloc_blocks(&mm, 0, ps, ps, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = gpu_buddy_alloc_blocks(&mm, mm_size / 2, mm_size / 2 + ps, ps, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = gpu_buddy_alloc_blocks(&mm, mm_size - ps, mm_size, ps, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + total = 0; + list_for_each_entry(block, &blocks, link) + total += (u32)gpu_buddy_block_size(&mm, block); + KUNIT_EXPECT_EQ(test, total, 3 * ps); + + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); + + /* + * Non power-of-two mm size (multiple roots). + * Exact-range allocations that span root boundaries must still work. + */ + mm_size = SZ_4M + SZ_2M + SZ_1M; /* 7 MiB, three roots */ + + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_GT(test, mm.n_roots, 1U); + + /* Allocate first 4M root exactly */ + ret = gpu_buddy_alloc_blocks(&mm, 0, SZ_4M, SZ_4M, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate second root (4M-6M) exactly */ + ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_2M, SZ_2M, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate third root (6M-7M) exactly */ + ret = gpu_buddy_alloc_blocks(&mm, SZ_4M + SZ_2M, mm_size, SZ_1M, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + gpu_buddy_free_list(&mm, &blocks, 0); + + /* Cross-root exact-range: the entire non-pot mm */ + ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); + + /* + * Randomized exact-range allocations. + * Divide the mm into N random-sized, contiguous, page-aligned slices + * and allocate each as an exact range in random order. + */ + mm_size = SZ_16M; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + { +#define N_RAND_RANGES 16 + u32 ranges[N_RAND_RANGES + 1]; /* boundaries */ + u32 order_arr[N_RAND_RANGES]; + u32 remaining = mm_size; + int i; + + ranges[0] = 0; + for (i = 0; i < N_RAND_RANGES - 1; i++) { + u32 max_chunk = remaining - (N_RAND_RANGES - 1 - i) * ps; + u32 sz = max(round_up(prandom_u32_state(&prng) % max_chunk, ps), ps); + + ranges[i + 1] = ranges[i] + sz; + remaining -= sz; + } + ranges[N_RAND_RANGES] = mm_size; + + /* Create a random order */ + for (i = 0; i < N_RAND_RANGES; i++) + order_arr[i] = i; + for (i = N_RAND_RANGES - 1; i > 0; i--) { + u32 j = prandom_u32_state(&prng) % (i + 1); + u32 tmp_val = order_arr[i]; + + order_arr[i] = order_arr[j]; + order_arr[j] = tmp_val; + } + + for (i = 0; i < N_RAND_RANGES; i++) { + u32 idx = order_arr[i]; + u32 start = ranges[idx]; + u32 end = ranges[idx + 1]; + u32 sz = end - start; + + ret = gpu_buddy_alloc_blocks(&mm, start, end, sz, ps, &blocks, 0); + KUNIT_ASSERT_EQ_MSG(test, ret, 0, + "random exact-range [%x, %x) sz=%x failed\n", + start, end, sz); + } + + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + gpu_buddy_free_list(&mm, &blocks, 0); +#undef N_RAND_RANGES + } + + gpu_buddy_fini(&mm); + + /* + * Negative case - partially allocated range. + * Allocate the first half, then try to exact-range allocate the full + * mm. This must fail because the first half is already occupied. + */ + mm_size = SZ_16M; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size / 2, mm_size / 2, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &tmp, 0); + KUNIT_EXPECT_NE_MSG(test, ret, 0, + "exact-range alloc should fail when range is partially used\n"); + + /* Also try the already-occupied sub-range directly */ + ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size / 2, mm_size / 2, ps, &tmp, 0); + KUNIT_EXPECT_NE_MSG(test, ret, 0, + "double alloc of same exact range should fail\n"); + + /* The free second half should still be allocatable */ + ret = gpu_buddy_alloc_blocks(&mm, mm_size / 2, mm_size, mm_size / 2, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); + + /* + * Negative case - checkerboard partial allocation. + * Allocate every other page-sized chunk in a small mm, then try to + * exact-range allocate a range covering two pages (one allocated, one + * free). This must fail. + */ + mm_size = SZ_64K; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + { + u32 off; + + for (off = 0; off < mm_size; off += 2 * ps) { + ret = gpu_buddy_alloc_blocks(&mm, off, off + ps, ps, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + } + + /* Try exact range over a pair [allocated, free] */ + ret = gpu_buddy_alloc_blocks(&mm, 0, 2 * ps, 2 * ps, ps, &tmp, 0); + KUNIT_EXPECT_NE_MSG(test, ret, 0, + "exact-range over partially allocated pair should fail\n"); + + /* The free pages individually should still work */ + ret = gpu_buddy_alloc_blocks(&mm, ps, 2 * ps, ps, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + gpu_buddy_free_list(&mm, &blocks, 0); + } + + gpu_buddy_fini(&mm); + + /* Negative case - misaligned start/end/size */ + mm_size = SZ_16M; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + /* start not aligned to chunk_size */ + ret = gpu_buddy_alloc_blocks(&mm, ps / 2, ps / 2 + ps, ps, ps, &tmp, 0); + KUNIT_EXPECT_NE(test, ret, 0); + + /* size not aligned */ + ret = gpu_buddy_alloc_blocks(&mm, 0, ps + 1, ps + 1, ps, &tmp, 0); + KUNIT_EXPECT_NE(test, ret, 0); + + /* end exceeds mm size */ + ret = gpu_buddy_alloc_blocks(&mm, mm_size, mm_size + ps, ps, ps, &tmp, 0); + KUNIT_EXPECT_NE(test, ret, 0); + + gpu_buddy_fini(&mm); + + /* + * Free and re-allocate the same exact range. + * This exercises merge-on-free followed by exact-range re-split. + */ + mm_size = SZ_16M; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + { + int i; + + for (i = 0; i < 5; i++) { + ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_2M, + SZ_2M, ps, &blocks, 0); + KUNIT_ASSERT_EQ_MSG(test, ret, 0, + "re-alloc iteration %d failed\n", i); + + total = 0; + list_for_each_entry(block, &blocks, link) { + u64 offset = gpu_buddy_block_offset(block); + u64 bsize = gpu_buddy_block_size(&mm, block); + + KUNIT_EXPECT_GE(test, offset, (u64)SZ_4M); + KUNIT_EXPECT_LE(test, offset + bsize, (u64)(SZ_4M + SZ_2M)); + total += (u32)bsize; + } + KUNIT_EXPECT_EQ(test, total, SZ_2M); + + gpu_buddy_free_list(&mm, &blocks, 0); + } + + KUNIT_EXPECT_EQ(test, mm.avail, (u64)mm_size); + } + + gpu_buddy_fini(&mm); + + /* + * Various power-of-two exact ranges within a large mm. + * Allocate non-overlapping power-of-two exact ranges at their natural + * alignment, validating that the allocator handles different orders. + */ + mm_size = SZ_16M; + KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + + /* Allocate 4K at offset 0 */ + ret = gpu_buddy_alloc_blocks(&mm, 0, SZ_4K, SZ_4K, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate 64K at offset 64K */ + ret = gpu_buddy_alloc_blocks(&mm, SZ_64K, SZ_64K + SZ_64K, SZ_64K, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate 1M at offset 1M */ + ret = gpu_buddy_alloc_blocks(&mm, SZ_1M, SZ_1M + SZ_1M, SZ_1M, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate 4M at offset 4M */ + ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_4M, SZ_4M, ps, &blocks, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + total = 0; + list_for_each_entry(block, &blocks, link) + total += (u32)gpu_buddy_block_size(&mm, block); + KUNIT_EXPECT_EQ(test, total, SZ_4K + SZ_64K + SZ_1M + SZ_4M); + + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); +} + static void gpu_test_buddy_alloc_clear(struct kunit *test) { unsigned long n_pages, total, i = 0; @@ -1073,6 +1399,7 @@ static struct kunit_case gpu_buddy_tests[] = { KUNIT_CASE(gpu_test_buddy_alloc_pathological), KUNIT_CASE(gpu_test_buddy_alloc_contiguous), KUNIT_CASE(gpu_test_buddy_alloc_clear), + KUNIT_CASE(gpu_test_buddy_alloc_range), KUNIT_CASE(gpu_test_buddy_alloc_range_bias), KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance), KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order), From 81e62e7bf8b9309bf0febdf00940818f98bc23d8 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Thu, 5 Mar 2026 15:22:26 +0100 Subject: [PATCH 06/62] accel/ivpu: Remove boot params address setting via MMIO register The NPU 60XX uses the default boot params location specified in the firmware image header, consistent with earlier generations. Remove the unnecessary MMIO register write, freeing the AON register for future use. Fixes: 44e4c88951fa ("accel/ivpu: Implement warm boot flow for NPU6 and unify boot handling") Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260305142226.194995-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_hw_40xx_reg.h | 6 ------ drivers/accel/ivpu/ivpu_hw_ip.c | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h index 421242acb184..fc0ee8d637f9 100644 --- a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h +++ b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h @@ -121,12 +121,6 @@ #define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu #define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0) -#define VPU_40XX_HOST_SS_AON_RETENTION0 0x0003000cu -#define VPU_40XX_HOST_SS_AON_RETENTION1 0x00030010u -#define VPU_40XX_HOST_SS_AON_RETENTION2 0x00030014u -#define VPU_40XX_HOST_SS_AON_RETENTION3 0x00030018u -#define VPU_40XX_HOST_SS_AON_RETENTION4 0x0003001cu - #define VPU_40XX_HOST_SS_AON_IDLE_GEN 0x00030200u #define VPU_40XX_HOST_SS_AON_IDLE_GEN_EN_MASK BIT_MASK(0) #define VPU_40XX_HOST_SS_AON_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1) diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c index 959984c54341..37f95a0551ed 100644 --- a/drivers/accel/ivpu/ivpu_hw_ip.c +++ b/drivers/accel/ivpu/ivpu_hw_ip.c @@ -931,7 +931,6 @@ static int soc_cpu_boot_40xx(struct ivpu_device *vdev) static int soc_cpu_boot_60xx(struct ivpu_device *vdev) { - REGV_WR64(VPU_40XX_HOST_SS_AON_RETENTION1, vdev->fw->mem_bp->vpu_addr); soc_cpu_set_entry_point_40xx(vdev, vdev->fw->cold_boot_entry_point); return 0; From 153308a2f966adc7d1e72cad5557d621708a3a18 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Mon, 9 Mar 2026 10:27:55 +0100 Subject: [PATCH 07/62] accel/ivpu: Test for imported buffers with drm_gem_is_imported() Instead of testing import_attach for imported GEM buffers, invoke drm_gem_is_imported() to do the test. The test itself does not change. Suggested-by: Thomas Zimmermann Reviewed-by: Thomas Zimmermann Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260309092755.3165130-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c index 98b9ce26962b..8009965286e0 100644 --- a/drivers/accel/ivpu/ivpu_gem.c +++ b/drivers/accel/ivpu/ivpu_gem.c @@ -48,7 +48,7 @@ static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct { struct sg_table *sgt; - drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach); + drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base)); ivpu_bo_lock(bo); @@ -157,7 +157,7 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo) } if (bo->base.sgt) { - if (bo->base.base.import_attach) { + if (drm_gem_is_imported(&bo->base.base)) { dma_buf_unmap_attachment(bo->base.base.import_attach, bo->base.sgt, DMA_BIDIRECTIONAL); } else { From fc6aa072bdbd26dfc94269fa87ca40ab4f867236 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Thu, 5 Mar 2026 09:24:15 -0800 Subject: [PATCH 08/62] MAINTAINERS: Remove bouncing Keem Bay maintainer This maintainer's email no longer works. Remove it from MAINTAINERS. There is still one listed maintainer for the "INTEL KEEM BAY DRM DRIVER". Signed-off-by: Dave Hansen Acked-by: Anitha Chrisanthus Cc: Anitha Chrisanthus Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: dri-devel@lists.freedesktop.org Link: https://patch.msgid.link/20260305172415.3116438-1-dave.hansen@linux.intel.com Signed-off-by: Maxime Ripard --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8a5b27b061da..75fb0fded243 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12989,7 +12989,6 @@ F: drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c INTEL KEEM BAY DRM DRIVER M: Anitha Chrisanthus -M: Edmund Dea S: Maintained F: Documentation/devicetree/bindings/display/intel,keembay-display.yaml F: drivers/gpu/drm/kmb/ From 43d6508ddbf9fb974fbc359a033154f78c9d4c8b Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 9 Dec 2025 17:33:27 +0530 Subject: [PATCH 09/62] drm/bridge: cadence: cdns-mhdp8546-core: Set the mhdp connector earlier in atomic_enable() In case if we get errors in cdns_mhdp_link_up() or cdns_mhdp_reg_read() in atomic_enable, we will go to cdns_mhdp_modeset_retry_fn() and will hit NULL pointer while trying to access the mutex. We need the connector to be set before that. Unlike in legacy cases with flag !DRM_BRIDGE_ATTACH_NO_CONNECTOR, we do not have connector initialised in bridge_attach(), so add the mhdp->connector_ptr in device structure to handle both cases with DRM_BRIDGE_ATTACH_NO_CONNECTOR and !DRM_BRIDGE_ATTACH_NO_CONNECTOR, set it in atomic_enable() earlier to avoid possible NULL pointer dereference in recovery paths like modeset_retry_fn() with the DRM_BRIDGE_ATTACH_NO_CONNECTOR flag set. Fixes: c932ced6b585 ("drm/tidss: Update encoder/bridge chain connect model") Signed-off-by: Jayesh Choudhary Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Reviewed-by: Tomi Valkeinen Link: https://patch.msgid.link/20251209120332.3559893-2-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- .../drm/bridge/cadence/cdns-mhdp8546-core.c | 29 ++++++++++--------- .../drm/bridge/cadence/cdns-mhdp8546-core.h | 1 + .../drm/bridge/cadence/cdns-mhdp8546-hdcp.c | 18 +++++++++--- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 9392c226ff5b..3379194e4ea6 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -740,7 +740,7 @@ static void cdns_mhdp_fw_cb(const struct firmware *fw, void *context) bridge_attached = mhdp->bridge_attached; spin_unlock(&mhdp->start_lock); if (bridge_attached) { - if (mhdp->connector.dev) + if (mhdp->connector_ptr) drm_kms_helper_hotplug_event(mhdp->bridge.dev); else drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); @@ -1636,6 +1636,7 @@ static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp) return ret; } + mhdp->connector_ptr = conn; drm_connector_helper_add(conn, &cdns_mhdp_conn_helper_funcs); ret = drm_display_info_set_bus_formats(&conn->display_info, @@ -1915,17 +1916,25 @@ static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge, struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); struct cdns_mhdp_bridge_state *mhdp_state; struct drm_crtc_state *crtc_state; - struct drm_connector *connector; struct drm_connector_state *conn_state; struct drm_bridge_state *new_state; const struct drm_display_mode *mode; u32 resp; - int ret; + int ret = 0; dev_dbg(mhdp->dev, "bridge enable\n"); mutex_lock(&mhdp->link_mutex); + mhdp->connector_ptr = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!mhdp->connector_ptr)) + goto out; + + conn_state = drm_atomic_get_new_connector_state(state, mhdp->connector_ptr); + if (WARN_ON(!conn_state)) + goto out; + if (mhdp->plugged && !mhdp->link_up) { ret = cdns_mhdp_link_up(mhdp); if (ret < 0) @@ -1945,15 +1954,6 @@ static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge, cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR, resp | CDNS_VIF_CLK_EN | CDNS_VIF_CLK_RSTN); - connector = drm_atomic_get_new_connector_for_encoder(state, - bridge->encoder); - if (WARN_ON(!connector)) - goto out; - - conn_state = drm_atomic_get_new_connector_state(state, connector); - if (WARN_ON(!conn_state)) - goto out; - if (mhdp->hdcp_supported && mhdp->hw_state == MHDP_HW_READY && conn_state->content_protection == @@ -2030,6 +2030,7 @@ static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge, if (mhdp->info && mhdp->info->ops && mhdp->info->ops->disable) mhdp->info->ops->disable(mhdp); + mhdp->connector_ptr = NULL; mutex_unlock(&mhdp->link_mutex); } @@ -2296,7 +2297,7 @@ static void cdns_mhdp_modeset_retry_fn(struct work_struct *work) mhdp = container_of(work, typeof(*mhdp), modeset_retry_work); - conn = &mhdp->connector; + conn = mhdp->connector_ptr; /* Grab the locks before changing connector property */ mutex_lock(&conn->dev->mode_config.mutex); @@ -2373,7 +2374,7 @@ static void cdns_mhdp_hpd_work(struct work_struct *work) int ret; ret = cdns_mhdp_update_link_status(mhdp); - if (mhdp->connector.dev) { + if (mhdp->connector_ptr) { if (ret < 0) schedule_work(&mhdp->modeset_retry_work); else diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index bad2fc0c7306..a76775c76895 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h @@ -376,6 +376,7 @@ struct cdns_mhdp_device { struct mutex link_mutex; struct drm_connector connector; + struct drm_connector *connector_ptr; struct drm_bridge bridge; struct cdns_mhdp_link link; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c index 42248f179b69..21a7d2fb266e 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c @@ -394,7 +394,7 @@ static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) int ret; dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n", - mhdp->connector.name, mhdp->connector.base.id); + mhdp->connector_ptr->name, mhdp->connector_ptr->base.id); ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false); @@ -436,6 +436,10 @@ static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) int ret = 0; mutex_lock(&mhdp->hdcp.mutex); + + if (!mhdp->connector_ptr) + goto out; + if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) goto out; @@ -445,7 +449,7 @@ static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) dev_err(mhdp->dev, "[%s:%d] HDCP link failed, retrying authentication\n", - mhdp->connector.name, mhdp->connector.base.id); + mhdp->connector_ptr->name, mhdp->connector_ptr->base.id); ret = _cdns_mhdp_hdcp_disable(mhdp); if (ret) { @@ -487,13 +491,19 @@ static void cdns_mhdp_hdcp_prop_work(struct work_struct *work) struct cdns_mhdp_device *mhdp = container_of(hdcp, struct cdns_mhdp_device, hdcp); - struct drm_device *dev = mhdp->connector.dev; + struct drm_device *dev = NULL; struct drm_connector_state *state; + if (mhdp->connector_ptr) + dev = mhdp->connector_ptr->dev; + + if (!dev) + return; + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); mutex_lock(&mhdp->hdcp.mutex); if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - state = mhdp->connector.state; + state = mhdp->connector_ptr->state; state->content_protection = mhdp->hdcp.value; } mutex_unlock(&mhdp->hdcp.mutex); From 6dbff34016052b099558b76632e4983e2df13fed Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 9 Dec 2025 17:33:28 +0530 Subject: [PATCH 10/62] drm/bridge: cadence: cdns-mhdp8546-core: Add mode_valid hook to drm_bridge_funcs Add cdns_mhdp_bridge_mode_valid() to check if specific mode is valid for this bridge or not. In the legacy usecase with !DRM_BRIDGE_ATTACH_NO_CONNECTOR we were using the hook from drm_connector_helper_funcs but with DRM_BRIDGE_ATTACH_NO_CONNECTOR we need to have mode_valid() in drm_bridge_funcs. Without this patch, when using DRM_BRIDGE_ATTACH_NO_CONNECTOR flag, the cdns_mhdp_bandwidth_ok() function would not be called during mode validation, potentially allowing modes that exceed the bridge's bandwidth capabilities to be incorrectly marked as valid. Fixes: c932ced6b585 ("drm/tidss: Update encoder/bridge chain connect model") Reviewed-by: Tomi Valkeinen Signed-off-by: Jayesh Choudhary Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20251209120332.3559893-3-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- .../drm/bridge/cadence/cdns-mhdp8546-core.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 3379194e4ea6..3f5cc4e90927 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2162,6 +2162,25 @@ static const struct drm_edid *cdns_mhdp_bridge_edid_read(struct drm_bridge *brid return cdns_mhdp_edid_read(mhdp, connector); } +static enum drm_mode_status +cdns_mhdp_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); + + mutex_lock(&mhdp->link_mutex); + + if (!cdns_mhdp_bandwidth_ok(mhdp, mode, mhdp->link.num_lanes, + mhdp->link.rate)) { + mutex_unlock(&mhdp->link_mutex); + return MODE_CLOCK_HIGH; + } + + mutex_unlock(&mhdp->link_mutex); + return MODE_OK; +} + static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = { .atomic_enable = cdns_mhdp_atomic_enable, .atomic_disable = cdns_mhdp_atomic_disable, @@ -2176,6 +2195,7 @@ static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = { .edid_read = cdns_mhdp_bridge_edid_read, .hpd_enable = cdns_mhdp_bridge_hpd_enable, .hpd_disable = cdns_mhdp_bridge_hpd_disable, + .mode_valid = cdns_mhdp_bridge_mode_valid, }; static bool cdns_mhdp_detect_hpd(struct cdns_mhdp_device *mhdp, bool *hpd_pulse) From 4a8edd658489ec2a3d7e20482fa9e8d366153d8d Mon Sep 17 00:00:00 2001 From: Harikrishna Shenoy Date: Tue, 9 Dec 2025 17:33:29 +0530 Subject: [PATCH 11/62] drm/bridge: cadence: cdns-mhdp8546-core: Handle HDCP state in bridge atomic check Now that we have DRM_BRIDGE_ATTACH_NO_CONNECTOR framework, handle the HDCP state change in bridge atomic check as well to enable correct functioning for HDCP in both DRM_BRIDGE_ATTACH_NO_CONNECTOR and !DRM_BRIDGE_ATTACH_NO_CONNECTOR case. Without this patch, when using DRM_BRIDGE_ATTACH_NO_CONNECTOR flag, HDCP state changes would not be properly handled during atomic commits, potentially leading to HDCP authentication failures or incorrect protection status for content requiring HDCP encryption. Fixes: 6a3608eae6d33 ("drm: bridge: cdns-mhdp8546: Enable HDCP") Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Reviewed-by: Tomi Valkeinen Link: https://patch.msgid.link/20251209120332.3559893-4-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- .../drm/bridge/cadence/cdns-mhdp8546-core.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 3f5cc4e90927..c7cd0234d168 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2123,6 +2123,10 @@ static int cdns_mhdp_atomic_check(struct drm_bridge *bridge, { struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); const struct drm_display_mode *mode = &crtc_state->adjusted_mode; + struct drm_connector_state *old_state, *new_state; + struct drm_atomic_state *state = crtc_state->state; + struct drm_connector *conn = mhdp->connector_ptr; + u64 old_cp, new_cp; mutex_lock(&mhdp->link_mutex); @@ -2142,6 +2146,25 @@ static int cdns_mhdp_atomic_check(struct drm_bridge *bridge, if (mhdp->info) bridge_state->input_bus_cfg.flags = *mhdp->info->input_bus_flags; + if (conn && mhdp->hdcp_supported) { + old_state = drm_atomic_get_old_connector_state(state, conn); + new_state = drm_atomic_get_new_connector_state(state, conn); + old_cp = old_state->content_protection; + new_cp = new_state->content_protection; + + if (old_state->hdcp_content_type != new_state->hdcp_content_type && + new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); + crtc_state->mode_changed = true; + } + + if (!new_state->crtc) { + if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) + new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + } + } + mutex_unlock(&mhdp->link_mutex); return 0; } From 35cf2554520c8ec59f12e08ded72ed11895fffc6 Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 9 Dec 2025 17:33:30 +0530 Subject: [PATCH 12/62] drm/bridge: cadence: cdns-mhdp8546-core: Remove legacy support for connector initialisation in bridge Now that this bridge supports DRM_BRIDGE_ATTACH_NO_CONNECTOR, and only TI K3 platforms consume this driver and tidss (their display controller) has DRM_BRIDGE_ATTACH_NO_CONNECTOR flag set,we can remove the legacy code for the non-DRM_BRIDGE_ATTACH_NO_CONNECTOR case. Reviewed-by: Dmitry Baryshkov Signed-off-by: Jayesh Choudhary Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Reviewed-by: Tomi Valkeinen Link: https://patch.msgid.link/20251209120332.3559893-5-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- .../drm/bridge/cadence/cdns-mhdp8546-core.c | 187 +----------------- 1 file changed, 9 insertions(+), 178 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index c7cd0234d168..243cb61dcde1 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -739,12 +739,8 @@ static void cdns_mhdp_fw_cb(const struct firmware *fw, void *context) spin_lock(&mhdp->start_lock); bridge_attached = mhdp->bridge_attached; spin_unlock(&mhdp->start_lock); - if (bridge_attached) { - if (mhdp->connector_ptr) - drm_kms_helper_hotplug_event(mhdp->bridge.dev); - else - drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); - } + if (bridge_attached) + drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); } static int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp) @@ -1444,56 +1440,6 @@ static const struct drm_edid *cdns_mhdp_edid_read(struct cdns_mhdp_device *mhdp, return drm_edid_read_custom(connector, cdns_mhdp_get_edid_block, mhdp); } -static int cdns_mhdp_get_modes(struct drm_connector *connector) -{ - struct cdns_mhdp_device *mhdp = connector_to_mhdp(connector); - const struct drm_edid *drm_edid; - int num_modes; - - if (!mhdp->plugged) - return 0; - - drm_edid = cdns_mhdp_edid_read(mhdp, connector); - - drm_edid_connector_update(connector, drm_edid); - - if (!drm_edid) { - dev_err(mhdp->dev, "Failed to read EDID\n"); - return 0; - } - - num_modes = drm_edid_connector_add_modes(connector); - drm_edid_free(drm_edid); - - /* - * HACK: Warn about unsupported display formats until we deal - * with them correctly. - */ - if (connector->display_info.color_formats && - !(connector->display_info.color_formats & - mhdp->display_fmt.color_format)) - dev_warn(mhdp->dev, - "%s: No supported color_format found (0x%08x)\n", - __func__, connector->display_info.color_formats); - - if (connector->display_info.bpc && - connector->display_info.bpc < mhdp->display_fmt.bpc) - dev_warn(mhdp->dev, "%s: Display bpc only %d < %d\n", - __func__, connector->display_info.bpc, - mhdp->display_fmt.bpc); - - return num_modes; -} - -static int cdns_mhdp_connector_detect(struct drm_connector *conn, - struct drm_modeset_acquire_ctx *ctx, - bool force) -{ - struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn); - - return cdns_mhdp_detect(mhdp); -} - static u32 cdns_mhdp_get_bpp(struct cdns_mhdp_display_fmt *fmt) { u32 bpp; @@ -1547,115 +1493,6 @@ bool cdns_mhdp_bandwidth_ok(struct cdns_mhdp_device *mhdp, return true; } -static -enum drm_mode_status cdns_mhdp_mode_valid(struct drm_connector *conn, - const struct drm_display_mode *mode) -{ - struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn); - - mutex_lock(&mhdp->link_mutex); - - if (!cdns_mhdp_bandwidth_ok(mhdp, mode, mhdp->link.num_lanes, - mhdp->link.rate)) { - mutex_unlock(&mhdp->link_mutex); - return MODE_CLOCK_HIGH; - } - - mutex_unlock(&mhdp->link_mutex); - return MODE_OK; -} - -static int cdns_mhdp_connector_atomic_check(struct drm_connector *conn, - struct drm_atomic_state *state) -{ - struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn); - struct drm_connector_state *old_state, *new_state; - struct drm_crtc_state *crtc_state; - u64 old_cp, new_cp; - - if (!mhdp->hdcp_supported) - return 0; - - old_state = drm_atomic_get_old_connector_state(state, conn); - new_state = drm_atomic_get_new_connector_state(state, conn); - old_cp = old_state->content_protection; - new_cp = new_state->content_protection; - - if (old_state->hdcp_content_type != new_state->hdcp_content_type && - new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; - goto mode_changed; - } - - if (!new_state->crtc) { - if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) - new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; - return 0; - } - - if (old_cp == new_cp || - (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && - new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) - return 0; - -mode_changed: - crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); - crtc_state->mode_changed = true; - - return 0; -} - -static const struct drm_connector_helper_funcs cdns_mhdp_conn_helper_funcs = { - .detect_ctx = cdns_mhdp_connector_detect, - .get_modes = cdns_mhdp_get_modes, - .mode_valid = cdns_mhdp_mode_valid, - .atomic_check = cdns_mhdp_connector_atomic_check, -}; - -static const struct drm_connector_funcs cdns_mhdp_conn_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .reset = drm_atomic_helper_connector_reset, - .destroy = drm_connector_cleanup, -}; - -static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp) -{ - u32 bus_format = MEDIA_BUS_FMT_RGB121212_1X36; - struct drm_connector *conn = &mhdp->connector; - struct drm_bridge *bridge = &mhdp->bridge; - int ret; - - conn->polled = DRM_CONNECTOR_POLL_HPD; - - ret = drm_connector_init(bridge->dev, conn, &cdns_mhdp_conn_funcs, - DRM_MODE_CONNECTOR_DisplayPort); - if (ret) { - dev_err(mhdp->dev, "Failed to initialize connector with drm\n"); - return ret; - } - - mhdp->connector_ptr = conn; - drm_connector_helper_add(conn, &cdns_mhdp_conn_helper_funcs); - - ret = drm_display_info_set_bus_formats(&conn->display_info, - &bus_format, 1); - if (ret) - return ret; - - ret = drm_connector_attach_encoder(conn, bridge->encoder); - if (ret) { - dev_err(mhdp->dev, "Failed to attach connector to encoder\n"); - return ret; - } - - if (mhdp->hdcp_supported) - ret = drm_connector_attach_content_protection_property(conn, true); - - return ret; -} - static int cdns_mhdp_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) @@ -1672,9 +1509,11 @@ static int cdns_mhdp_attach(struct drm_bridge *bridge, return ret; if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { - ret = cdns_mhdp_connector_init(mhdp); - if (ret) - goto aux_unregister; + ret = -EINVAL; + dev_err(mhdp->dev, + "Connector initialisation not supported in bridge_attach %d\n", + ret); + goto aux_unregister; } spin_lock(&mhdp->start_lock); @@ -2414,17 +2253,9 @@ static void cdns_mhdp_hpd_work(struct work_struct *work) struct cdns_mhdp_device *mhdp = container_of(work, struct cdns_mhdp_device, hpd_work); - int ret; + cdns_mhdp_update_link_status(mhdp); - ret = cdns_mhdp_update_link_status(mhdp); - if (mhdp->connector_ptr) { - if (ret < 0) - schedule_work(&mhdp->modeset_retry_work); - else - drm_kms_helper_hotplug_event(mhdp->bridge.dev); - } else { - drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); - } + drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); } static int cdns_mhdp_probe(struct platform_device *pdev) From 0d9e84d304575029815681e06f755075c64ee85d Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 9 Dec 2025 17:33:31 +0530 Subject: [PATCH 13/62] cadence: cdns-mhdp8546*: Change drm_connector from structure to pointer Now that we have dropped the legacy code which became redundant with introduction of DRM_BRIDGE_ATTACH_NO_CONNECTOR usecase in driver,we can cleanly switch to drm_connector pointer instead of structure. Rename the connector_ptr member variable to connector for clarity and consistency. The driver was using both connector and connector_ptr members, but connector_ptr was the only one actually used throughout the code. This change removes the unused connector struct member and renames connector_ptr to connector for better readability. This is purely a code cleanup change with no functional impact. All references to connector_ptr are updated to use the renamed connector variable throughout the driver. Reviewed-by: Dmitry Baryshkov Signed-off-by: Jayesh Choudhary Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Reviewed-by: Tomi Valkeinen Link: https://patch.msgid.link/20251209120332.3559893-6-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- .../gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 14 +++++++------- .../gpu/drm/bridge/cadence/cdns-mhdp8546-core.h | 3 +-- .../gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c | 12 ++++++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 243cb61dcde1..89cf8cb37cd3 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -1765,12 +1765,12 @@ static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge, mutex_lock(&mhdp->link_mutex); - mhdp->connector_ptr = drm_atomic_get_new_connector_for_encoder(state, - bridge->encoder); - if (WARN_ON(!mhdp->connector_ptr)) + mhdp->connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!mhdp->connector)) goto out; - conn_state = drm_atomic_get_new_connector_state(state, mhdp->connector_ptr); + conn_state = drm_atomic_get_new_connector_state(state, mhdp->connector); if (WARN_ON(!conn_state)) goto out; @@ -1869,7 +1869,7 @@ static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge, if (mhdp->info && mhdp->info->ops && mhdp->info->ops->disable) mhdp->info->ops->disable(mhdp); - mhdp->connector_ptr = NULL; + mhdp->connector = NULL; mutex_unlock(&mhdp->link_mutex); } @@ -1964,7 +1964,7 @@ static int cdns_mhdp_atomic_check(struct drm_bridge *bridge, const struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct drm_connector_state *old_state, *new_state; struct drm_atomic_state *state = crtc_state->state; - struct drm_connector *conn = mhdp->connector_ptr; + struct drm_connector *conn = mhdp->connector; u64 old_cp, new_cp; mutex_lock(&mhdp->link_mutex); @@ -2179,7 +2179,7 @@ static void cdns_mhdp_modeset_retry_fn(struct work_struct *work) mhdp = container_of(work, typeof(*mhdp), modeset_retry_work); - conn = mhdp->connector_ptr; + conn = mhdp->connector; /* Grab the locks before changing connector property */ mutex_lock(&conn->dev->mode_config.mutex); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index a76775c76895..b297db53ba28 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h @@ -375,8 +375,7 @@ struct cdns_mhdp_device { */ struct mutex link_mutex; - struct drm_connector connector; - struct drm_connector *connector_ptr; + struct drm_connector *connector; struct drm_bridge bridge; struct cdns_mhdp_link link; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c index 21a7d2fb266e..5cd0b873e16f 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c @@ -394,7 +394,7 @@ static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) int ret; dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n", - mhdp->connector_ptr->name, mhdp->connector_ptr->base.id); + mhdp->connector->name, mhdp->connector->base.id); ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false); @@ -437,7 +437,7 @@ static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) mutex_lock(&mhdp->hdcp.mutex); - if (!mhdp->connector_ptr) + if (!mhdp->connector) goto out; if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) @@ -449,7 +449,7 @@ static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) dev_err(mhdp->dev, "[%s:%d] HDCP link failed, retrying authentication\n", - mhdp->connector_ptr->name, mhdp->connector_ptr->base.id); + mhdp->connector->name, mhdp->connector->base.id); ret = _cdns_mhdp_hdcp_disable(mhdp); if (ret) { @@ -494,8 +494,8 @@ static void cdns_mhdp_hdcp_prop_work(struct work_struct *work) struct drm_device *dev = NULL; struct drm_connector_state *state; - if (mhdp->connector_ptr) - dev = mhdp->connector_ptr->dev; + if (mhdp->connector) + dev = mhdp->connector->dev; if (!dev) return; @@ -503,7 +503,7 @@ static void cdns_mhdp_hdcp_prop_work(struct work_struct *work) drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); mutex_lock(&mhdp->hdcp.mutex); if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - state = mhdp->connector_ptr->state; + state = mhdp->connector->state; state->content_protection = mhdp->hdcp.value; } mutex_unlock(&mhdp->hdcp.mutex); From bfb18fd193e2413f02ad934e46887f415f0ce4ec Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 9 Dec 2025 17:33:32 +0530 Subject: [PATCH 14/62] drm/bridge: cadence: cdns-mhdp8546-core: Reduce log level for DPCD read/write Reduce the log level for cdns_mhdp_dpcd_read and cdns_mhdp_dpcd_write errors in cdns_mhdp_transfer function as in case of failure, there is flooding of these prints along with other indicators like EDID failure logs which are fairly intuitive in themselves rendering these error logs useless. Also, the caller functions for the cdns_mhdp_transfer in drm_dp_helper.c (which calls it 32 times), has debug log level in case transfer fails. So having a superseding log level in cdns_mhdp_transfer seems bad. Reviewed-by: Tomi Valkeinen Signed-off-by: Jayesh Choudhary Signed-off-by: Harikrishna Shenoy Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20251209120332.3559893-7-h-shenoy@ti.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 89cf8cb37cd3..945bb47c172b 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -778,7 +778,7 @@ static ssize_t cdns_mhdp_transfer(struct drm_dp_aux *aux, if (!ret) continue; - dev_err(mhdp->dev, + dev_dbg(mhdp->dev, "Failed to write DPCD addr %u\n", msg->address + i); @@ -788,7 +788,7 @@ static ssize_t cdns_mhdp_transfer(struct drm_dp_aux *aux, ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer, msg->size); if (ret) { - dev_err(mhdp->dev, + dev_dbg(mhdp->dev, "Failed to read DPCD addr %u\n", msg->address); From d31b23723ca0e3e340a1a68558a0f958569dd4a8 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 4 Mar 2026 15:04:25 +0100 Subject: [PATCH 15/62] drm: lcdif: Use dev_err_probe() Use dev_err_probe() to add a reason for deferred probe. This can especially happen on lcdif3 which uses hdmi_tx_phy for 'pix' clock Reviewed-by: Liu Ying Reviewed-by: Marco Felsch Reviewed-by: Frank Li Signed-off-by: Alexander Stein Signed-off-by: Liu Ying Link: https://lore.kernel.org/r/20260304140426.1499446-1-alexander.stein@ew.tq-group.com --- drivers/gpu/drm/mxsfb/lcdif_drv.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c index fcb2a7517377..47da1d9336b9 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c @@ -149,15 +149,17 @@ static int lcdif_load(struct drm_device *drm) lcdif->clk = devm_clk_get(drm->dev, "pix"); if (IS_ERR(lcdif->clk)) - return PTR_ERR(lcdif->clk); + return dev_err_probe(drm->dev, PTR_ERR(lcdif->clk), "Failed to get pix clock\n"); lcdif->clk_axi = devm_clk_get(drm->dev, "axi"); if (IS_ERR(lcdif->clk_axi)) - return PTR_ERR(lcdif->clk_axi); + return dev_err_probe(drm->dev, PTR_ERR(lcdif->clk_axi), + "Failed to get axi clock\n"); lcdif->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi"); if (IS_ERR(lcdif->clk_disp_axi)) - return PTR_ERR(lcdif->clk_disp_axi); + return dev_err_probe(drm->dev, PTR_ERR(lcdif->clk_disp_axi), + "Failed to get disp_axi clock\n"); platform_set_drvdata(pdev, drm); From c58724f415a21222b43f9e9385216252be4cedc7 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 19 Jan 2026 14:44:42 +0100 Subject: [PATCH 16/62] dt-bindings: display: simple: add EDT ET057023UDBA panel Add EDT ET057023UDBA 5.7" 24-bit 640x480 DPI panel. Signed-off-by: Marco Felsch Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260119-v6-18-topic-panel-simple-et057023udba-v2-1-3c73f0c9d87a@pengutronix.de --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 106ae91ff474..088c183c204d 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -103,6 +103,8 @@ properties: - dlc,dlc1010gig # Emerging Display Technology Corp. 3.5" QVGA TFT LCD panel - edt,et035012dm6 + # Emerging Display Technology Corp. 5.7" 24-bit VGA TFT LCD panel + - edt,et057023udba # Emerging Display Technology Corp. 5.7" VGA TFT LCD panel - edt,et057090dhu - edt,et070080dh6 From 43c3d68e64898f61e3b76c56b20af227fa49ca3f Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 19 Jan 2026 14:44:43 +0100 Subject: [PATCH 17/62] drm/panel: simple: add EDT ET057023UDBA panel Add support for the EDT ET057023UDBA 5.7" 24-bit 640x480 DPI panel. Reviewed-by: Neil Armstrong Signed-off-by: Marco Felsch Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260119-v6-18-topic-panel-simple-et057023udba-v2-2-3c73f0c9d87a@pengutronix.de --- drivers/gpu/drm/panel/panel-simple.c | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index e5fc9b072404..59603579bd50 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2096,6 +2096,35 @@ static const struct panel_desc edt_et057090dhu = { .connector_type = DRM_MODE_CONNECTOR_DPI, }; +static const struct display_timing edt_et057023udba_timing = { + .pixelclock = { 23200000, 24190000, 39640000 }, + .hactive = { 640, 640, 640 }, + .hfront_porch = { 20, 40, 200 }, + .hback_porch = { 87, 40, 1 }, + .hsync_len = { 1, 48, 87 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 5, 13, 200 }, + .vback_porch = { 31, 31, 29 }, + .vsync_len = { 1, 1, 3 }, + .flags = DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_HSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_SYNC_POSEDGE, +}; + +static const struct panel_desc edt_et057023udba = { + .timings = &edt_et057023udba_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 115, + .height = 86, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | + DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct drm_display_mode edt_etm0700g0dh6_mode = { .clock = 33260, .hdisplay = 800, @@ -5225,6 +5254,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "edt,etm0430g0dh6", .data = &edt_etm0430g0dh6, + }, { + .compatible = "edt,et057023udba", + .data = &edt_et057023udba, }, { .compatible = "edt,et057090dhu", .data = &edt_et057090dhu, From c1a4c55d75c0471710f444f56527f5bce9739c7e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 15 Feb 2026 10:51:35 +0200 Subject: [PATCH 18/62] drm/panel: r61307: align with schema property Rename renesas,inversion to renesas,column-inversion according to schema. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260215085140.20499-2-clamor95@gmail.com --- drivers/gpu/drm/panel/panel-renesas-r61307.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index 319415194839..873ef856184b 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -259,7 +259,7 @@ static int renesas_r61307_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset gpios\n"); - if (device_property_read_bool(dev, "renesas,inversion")) + if (device_property_read_bool(dev, "renesas,column-inversion")) priv->inversion = true; if (device_property_read_bool(dev, "renesas,contrast")) From 839ed86975721a428f4798c52cf167349164b2fb Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 15 Feb 2026 10:51:36 +0200 Subject: [PATCH 19/62] drm/panel: r61307/r69328: convert to drm_connector_helper_get_modes_fixed Switch to use of drm_connector_helper_get_modes_fixed helper for get_modes operation. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260215085140.20499-3-clamor95@gmail.com --- drivers/gpu/drm/panel/Kconfig | 2 ++ drivers/gpu/drm/panel/panel-renesas-r61307.c | 17 +++-------------- drivers/gpu/drm/panel/panel-renesas-r69328.c | 17 +++-------------- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 79264f7bbd0e..14912b543ac3 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -689,6 +689,7 @@ config DRM_PANEL_RENESAS_R61307 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS help Say Y here if you want to enable support for KOE tx13d100vm0eaa IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 @@ -702,6 +703,7 @@ config DRM_PANEL_RENESAS_R69328 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS help Say Y here if you want to enable support for JDI dx12d100vm0eaa IPS-LCD module with Renesas R69328 IC. The panel has a 720x1280 diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index 873ef856184b..45afa44af114 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -14,6 +14,7 @@ #include #include #include +#include #define R61307_MACP 0xb0 /* Manufacturer CMD Protect */ #define R61307_MACP_ON 0x03 @@ -202,25 +203,13 @@ static const struct drm_display_mode renesas_r61307_mode = { .vtotal = 1024 + 24 + 8 + 2, .width_mm = 76, .height_mm = 101, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, }; static int renesas_r61307_get_modes(struct drm_panel *panel, struct drm_connector *connector) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, &renesas_r61307_mode); - if (!mode) - return -ENOMEM; - - drm_mode_set_name(mode); - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - drm_mode_probed_add(connector, mode); - - return 1; + return drm_connector_helper_get_modes_fixed(connector, &renesas_r61307_mode); } static const struct drm_panel_funcs renesas_r61307_panel_funcs = { diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c index 46287ab04c30..b40bfd702fac 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r69328.c +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -14,6 +14,7 @@ #include #include #include +#include #define R69328_MACP 0xb0 /* Manufacturer Access CMD Protect */ #define R69328_MACP_ON 0x03 @@ -167,25 +168,13 @@ static const struct drm_display_mode renesas_r69328_mode = { .vtotal = 1280 + 6 + 3 + 1, .width_mm = 59, .height_mm = 105, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, }; static int renesas_r69328_get_modes(struct drm_panel *panel, struct drm_connector *connector) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, &renesas_r69328_mode); - if (!mode) - return -ENOMEM; - - drm_mode_set_name(mode); - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - drm_mode_probed_add(connector, mode); - - return 1; + return drm_connector_helper_get_modes_fixed(connector, &renesas_r69328_mode); } static const struct drm_panel_funcs renesas_r69328_panel_funcs = { From 1f8ba5e3508d17fef2602cb2826426afe593de78 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 15 Feb 2026 10:51:37 +0200 Subject: [PATCH 20/62] drm/panel: r61307/r69328: remove redundant checks This is now done by the DRM framework itself. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260215085140.20499-4-clamor95@gmail.com --- drivers/gpu/drm/panel/panel-renesas-r61307.c | 10 ---------- drivers/gpu/drm/panel/panel-renesas-r69328.c | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index 45afa44af114..2cc3bd66d6d4 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -35,8 +35,6 @@ struct renesas_r61307 { struct gpio_desc *reset_gpio; - bool prepared; - bool dig_cont_adj; bool inversion; u32 gamma; @@ -92,9 +90,6 @@ static int renesas_r61307_prepare(struct drm_panel *panel) struct device *dev = &priv->dsi->dev; int ret; - if (priv->prepared) - return 0; - ret = regulator_enable(priv->vcc_supply); if (ret) { dev_err(dev, "failed to enable vcc power supply\n"); @@ -113,7 +108,6 @@ static int renesas_r61307_prepare(struct drm_panel *panel) renesas_r61307_reset(priv); - priv->prepared = true; return 0; } @@ -175,9 +169,6 @@ static int renesas_r61307_unprepare(struct drm_panel *panel) { struct renesas_r61307 *priv = to_renesas_r61307(panel); - if (!priv->prepared) - return 0; - usleep_range(10000, 11000); gpiod_set_value_cansleep(priv->reset_gpio, 1); @@ -187,7 +178,6 @@ static int renesas_r61307_unprepare(struct drm_panel *panel) usleep_range(2000, 3000); regulator_disable(priv->vcc_supply); - priv->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c index b40bfd702fac..b5fe127634ae 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r69328.c +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -33,8 +33,6 @@ struct renesas_r69328 { struct regulator *vdd_supply; struct regulator *vddio_supply; struct gpio_desc *reset_gpio; - - bool prepared; }; static inline struct renesas_r69328 *to_renesas_r69328(struct drm_panel *panel) @@ -56,9 +54,6 @@ static int renesas_r69328_prepare(struct drm_panel *panel) struct device *dev = &priv->dsi->dev; int ret; - if (priv->prepared) - return 0; - ret = regulator_enable(priv->vdd_supply); if (ret) { dev_err(dev, "failed to enable vdd power supply\n"); @@ -77,7 +72,6 @@ static int renesas_r69328_prepare(struct drm_panel *panel) renesas_r69328_reset(priv); - priv->prepared = true; return 0; } @@ -142,9 +136,6 @@ static int renesas_r69328_unprepare(struct drm_panel *panel) { struct renesas_r69328 *priv = to_renesas_r69328(panel); - if (!priv->prepared) - return 0; - gpiod_set_value_cansleep(priv->reset_gpio, 1); usleep_range(5000, 6000); @@ -152,7 +143,6 @@ static int renesas_r69328_unprepare(struct drm_panel *panel) regulator_disable(priv->vddio_supply); regulator_disable(priv->vdd_supply); - priv->prepared = false; return 0; } From a4ad5575ce195d2a49a302e823cd42aa5c4133d5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 15 Feb 2026 10:51:38 +0200 Subject: [PATCH 21/62] drm/panel: r61307/r69328: return accumulated errors Return accumulated errors from dsi function calls. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260215085140.20499-5-clamor95@gmail.com --- drivers/gpu/drm/panel/panel-renesas-r61307.c | 4 ++-- drivers/gpu/drm/panel/panel-renesas-r69328.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index 2cc3bd66d6d4..70bda074dd0d 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -150,7 +150,7 @@ static int renesas_r61307_enable(struct drm_panel *panel) mipi_dsi_dcs_set_display_on_multi(&ctx); mipi_dsi_msleep(&ctx, 50); - return 0; + return ctx.accum_err; } static int renesas_r61307_disable(struct drm_panel *panel) @@ -162,7 +162,7 @@ static int renesas_r61307_disable(struct drm_panel *panel) mipi_dsi_msleep(&ctx, 100); mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); - return 0; + return ctx.accum_err; } static int renesas_r61307_unprepare(struct drm_panel *panel) diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c index b5fe127634ae..0ed143f77e50 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r69328.c +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -117,7 +117,7 @@ static int renesas_r69328_enable(struct drm_panel *panel) mipi_dsi_dcs_set_display_on_multi(&ctx); mipi_dsi_msleep(&ctx, 50); - return 0; + return ctx.accum_err; } static int renesas_r69328_disable(struct drm_panel *panel) @@ -129,7 +129,7 @@ static int renesas_r69328_disable(struct drm_panel *panel) mipi_dsi_msleep(&ctx, 60); mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); - return 0; + return ctx.accum_err; } static int renesas_r69328_unprepare(struct drm_panel *panel) From 3efe01ff79041fe64a6f6637191b69abf310fd0d Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 15 Feb 2026 10:51:39 +0200 Subject: [PATCH 22/62] drm/panel: r61307/r69328: convert to devm_mipi_dsi_attach Switch to device managed version of mipi_dsi_attach. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260215085140.20499-6-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-2-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-3-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-5-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-6-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-7-clamor95@gmail.com Link: https://patch.msgid.link/20251204060627.4727-8-clamor95@gmail.com --- drivers/gpu/drm/panel/panel-renesas-r61307.c | 7 +------ drivers/gpu/drm/panel/panel-renesas-r69328.c | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index 70bda074dd0d..d8185cc1b5d6 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -261,7 +261,7 @@ static int renesas_r61307_probe(struct mipi_dsi_device *dsi) drm_panel_add(&priv->panel); - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret) { drm_panel_remove(&priv->panel); return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); @@ -273,11 +273,6 @@ static int renesas_r61307_probe(struct mipi_dsi_device *dsi) static void renesas_r61307_remove(struct mipi_dsi_device *dsi) { struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&priv->panel); } diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c index 0ed143f77e50..bfe2787f8f53 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r69328.c +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -217,7 +217,7 @@ static int renesas_r69328_probe(struct mipi_dsi_device *dsi) drm_panel_add(&priv->panel); - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret) { drm_panel_remove(&priv->panel); return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); @@ -229,11 +229,6 @@ static int renesas_r69328_probe(struct mipi_dsi_device *dsi) static void renesas_r69328_remove(struct mipi_dsi_device *dsi) { struct renesas_r69328 *priv = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&priv->panel); } From 8e9661f4127ffe17768f9c24a16d9a281315d571 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 16 Feb 2026 18:50:18 +0100 Subject: [PATCH 23/62] drm/panel: mantix: Drop the shutdown callback It's the responsibility of the DRM modeset driver to unprepare and disable the panel. Doing so here leads to regulator underflows. Signed-off-by: Sebastian Krzyszkowiak Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260216-mantix-shutdown-v1-1-d2e93a8ccc4c@puri.sm --- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 13352cb4ad77..02e203e9f41a 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -295,20 +295,10 @@ static int mantix_probe(struct mipi_dsi_device *dsi) return 0; } -static void mantix_shutdown(struct mipi_dsi_device *dsi) -{ - struct mantix *ctx = mipi_dsi_get_drvdata(dsi); - - drm_panel_unprepare(&ctx->panel); - drm_panel_disable(&ctx->panel); -} - static void mantix_remove(struct mipi_dsi_device *dsi) { struct mantix *ctx = mipi_dsi_get_drvdata(dsi); - mantix_shutdown(dsi); - mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); } @@ -323,7 +313,6 @@ MODULE_DEVICE_TABLE(of, mantix_of_match); static struct mipi_dsi_driver mantix_driver = { .probe = mantix_probe, .remove = mantix_remove, - .shutdown = mantix_shutdown, .driver = { .name = DRV_NAME, .of_match_table = mantix_of_match, From eb2ed3de6ae9006a81e964ecd0ca86919b13f37d Mon Sep 17 00:00:00 2001 From: Florijan Plohl Date: Tue, 17 Feb 2026 13:37:58 +0100 Subject: [PATCH 24/62] dt-bindings: display: simple: Add Powertip PH800480T032-ZHC19 panel Add Powertip PH800480T032-ZHC19 7" LCD-TFT RGB panel compatible string. Signed-off-by: Florijan Plohl Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260217123759.169317-1-florijan.plohl@norik.com --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 088c183c204d..e98ca672ba49 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -268,6 +268,8 @@ properties: - powertip,ph128800t006-zhc01 # POWERTIP PH800480T013-IDF2 7.0" WVGA TFT LCD panel - powertip,ph800480t013-idf02 + # POWERTIP PH800480T032-ZHC19 7.0" WVGA TFT LCD panel + - powertip,ph800480t032-zhc19 # PrimeView PM070WL4 7.0" 800x480 TFT LCD panel - primeview,pm070wl4 # QiaoDian XianShi Corporation 4"3 TFT LCD panel From 4a3a4cb8198a5853e940cc6620c7b7c4e215a899 Mon Sep 17 00:00:00 2001 From: Florijan Plohl Date: Tue, 17 Feb 2026 13:37:59 +0100 Subject: [PATCH 25/62] drm/panel: simple: Add Powertip PH800480T032-ZHC19 panel Add support for the Powertip PH800480T032-ZHC19 7" (800x480) parallel LCD-TFT panel. Signed-off-by: Florijan Plohl Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260217123759.169317-2-florijan.plohl@norik.com --- drivers/gpu/drm/panel/panel-simple.c | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 59603579bd50..f79796387c58 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4075,6 +4075,33 @@ static const struct panel_desc powertip_ph800480t013_idf02 = { .connector_type = DRM_MODE_CONNECTOR_DPI, }; +static const struct drm_display_mode powertip_ph800480t032_zhc19_mode = { + .clock = 27200, + .hdisplay = 800, + .hsync_start = 800 + 52, + .hsync_end = 800 + 52 + 2, + .htotal = 800 + 52 + 2 + 44, + .vdisplay = 480, + .vsync_start = 480 + 7, + .vsync_end = 480 + 7 + 2, + .vtotal = 480 + 7 + 2 + 2, +}; + +static const struct panel_desc powertip_ph800480t032_zhc19 = { + .modes = &powertip_ph800480t032_zhc19_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 152, + .height = 91, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct drm_display_mode primeview_pm070wl4_mode = { .clock = 32000, .hdisplay = 800, @@ -5488,6 +5515,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "powertip,ph800480t013-idf02", .data = &powertip_ph800480t013_idf02, + }, { + .compatible = "powertip,ph800480t032-zhc19", + .data = &powertip_ph800480t032_zhc19, }, { .compatible = "primeview,pm070wl4", .data = &primeview_pm070wl4, From fd77ff14801570f546a777c52fbe22ac8c3ea0fb Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Wed, 18 Feb 2026 20:47:23 +0100 Subject: [PATCH 26/62] drm/panel: mantix: Set the pixel clocks for 60 Hz These are 60 Hz panels, but the currently set pixel clocks result in modes with refresh rates of 63 Hz and ~63.41 Hz. Adjust the clocks to target 60 Hz. Signed-off-by: Sebastian Krzyszkowiak Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260218-mantix-pixel-clock-v1-1-c22c034ba5c1@puri.sm --- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 02e203e9f41a..27e188bb2d7f 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -168,7 +168,7 @@ static const struct drm_display_mode default_mode_mantix = { .vsync_start = 1440 + 130, .vsync_end = 1440 + 130 + 8, .vtotal = 1440 + 130 + 8 + 106, - .clock = 85298, + .clock = 81237, .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, .width_mm = 65, .height_mm = 130, @@ -183,7 +183,7 @@ static const struct drm_display_mode default_mode_ys = { .vsync_start = 1440 + 175, .vsync_end = 1440 + 175 + 8, .vtotal = 1440 + 175 + 8 + 50, - .clock = 85298, + .clock = 80706, .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, .width_mm = 65, .height_mm = 130, From 312e23275b5bfa61e6cad9f6648d048257273742 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Wed, 18 Feb 2026 10:12:04 +0100 Subject: [PATCH 27/62] dt-bindings: vendor-prefixes: Add lxd Add vendor prefix for LXD Research & Display, LLC. Link: https://www.lxdinc.com/ Signed-off-by: Rouven Czerwinski Acked-by: Krzysztof Kozlowski Signed-off-by: Michael Tretter Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260218-drm-panel-ek79007ad3-v4-1-8ec448bf3ede@pengutronix.de --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 1ef679f88203..2af68463c685 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -973,6 +973,8 @@ patternProperties: description: Liebherr-Werk Nenzing GmbH "^lxa,.*": description: Linux Automation GmbH + "^lxd,.*": + description: LXD Research & Display, LLC "^m5stack,.*": description: M5Stack "^macnica,.*": From 59986dad1bd075de129334c4a4cf4ee9b29fb221 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Wed, 18 Feb 2026 10:12:05 +0100 Subject: [PATCH 28/62] dt-bindings: display: panel: add LXD M9189A The LXD M9189A is a 1024x600 MIPI-DSI panel. Signed-off-by: Rouven Czerwinski Signed-off-by: Michael Tretter Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260218-drm-panel-ek79007ad3-v4-2-8ec448bf3ede@pengutronix.de --- .../bindings/display/panel/lxd,m9189a.yaml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/lxd,m9189a.yaml diff --git a/Documentation/devicetree/bindings/display/panel/lxd,m9189a.yaml b/Documentation/devicetree/bindings/display/panel/lxd,m9189a.yaml new file mode 100644 index 000000000000..226974a4077f --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/lxd,m9189a.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/lxd,m9189a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LXD M9189A DSI Display Panel + +maintainers: + - Michael Tretter + +allOf: + - $ref: panel-common.yaml + +properties: + compatible: + const: lxd,m9189a + + reg: + maxItems: 1 + + standby-gpios: + description: GPIO used for the standby pin + maxItems: 1 + + reset-gpios: true + power-supply: true + backlight: true + port: true + +required: + - compatible + - reg + - standby-gpios + - reset-gpios + - power-supply + - backlight + - port + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "lxd,m9189a"; + reg = <0>; + backlight = <&backlight>; + reset-gpios = <&gpio3 25 GPIO_ACTIVE_LOW>; + standby-gpios = <&gpio5 22 GPIO_ACTIVE_LOW>; + power-supply = <®_display_3v3>; + + port { + mipi_panel_in: endpoint { + remote-endpoint = <&mipi_dsi_out>; + }; + }; + }; + }; From 1f772de164e6417e58c7b53fdad7f3c04d7f49ab Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Wed, 18 Feb 2026 10:12:06 +0100 Subject: [PATCH 29/62] drm/panel: add LXD M9189A panel driver The LXD M9189A panel is based on the EK79007AD3 DSI display controller. It currently supports only 4 lane operation. Signed-off-by: Rouven Czerwinski Signed-off-by: Michael Tretter Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260218-drm-panel-ek79007ad3-v4-3-8ec448bf3ede@pengutronix.de --- MAINTAINERS | 6 + drivers/gpu/drm/panel/Kconfig | 9 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-lxd-m9189a.c | 243 +++++++++++++++++++++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-lxd-m9189a.c diff --git a/MAINTAINERS b/MAINTAINERS index 75fb0fded243..0a21109e5274 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8062,6 +8062,12 @@ F: Documentation/devicetree/bindings/display/lvds.yaml F: Documentation/devicetree/bindings/display/panel/panel-lvds.yaml F: drivers/gpu/drm/panel/panel-lvds.c +DRM DRIVER FOR LXD M9189A PANELS +M: Michael Tretter +S: Maintained +F: Documentation/devicetree/bindings/display/panel/lxd,m9189a.yaml +F: drivers/gpu/drm/panel/panel-lxd-m9189a.c + DRM DRIVER FOR MANTIX MLAF057WE51 PANELS M: Guido Günther R: Purism Kernel Team diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 14912b543ac3..a59bca750ee2 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -443,6 +443,15 @@ config DRM_PANEL_LG_SW43408 pixel. It provides a MIPI DSI interface to the host and has a built-in LED backlight. +config DRM_PANEL_LXD_M9189A + tristate "LXD M9189A MIPI-DSI LCD panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for the LXD M9189A 4-Lane + 1024x600 MIPI DSI panel. + config DRM_PANEL_MAGNACHIP_D53E6EA8966 tristate "Magnachip D53E6EA8966 DSI panel" depends on OF && SPI diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index aeffaa95666d..a93c54abe968 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o obj-$(CONFIG_DRM_PANEL_LG_LD070WX3) += panel-lg-ld070wx3.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o +obj-$(CONFIG_DRM_PANEL_LXD_M9189A) += panel-lxd-m9189a.o obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o diff --git a/drivers/gpu/drm/panel/panel-lxd-m9189a.c b/drivers/gpu/drm/panel/panel-lxd-m9189a.c new file mode 100644 index 000000000000..68019e1e43a9 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-lxd-m9189a.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree. + * Copyright (c) 2024 Luca Weiss + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Manufacturer specific DSI commands */ +#define EK79007AD3_GAMMA1 0x80 +#define EK79007AD3_GAMMA2 0x81 +#define EK79007AD3_GAMMA3 0x82 +#define EK79007AD3_GAMMA4 0x83 +#define EK79007AD3_GAMMA5 0x84 +#define EK79007AD3_GAMMA6 0x85 +#define EK79007AD3_GAMMA7 0x86 +#define EK79007AD3_PANEL_CTRL3 0xB2 + +struct m9189_panel { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator *supply; + struct gpio_desc *reset_gpio; + struct gpio_desc *standby_gpio; +}; + +static inline struct m9189_panel *to_m9189_panel(struct drm_panel *panel) +{ + return container_of(panel, struct m9189_panel, panel); +} + +static void m9189_reset(struct m9189_panel *m9189) +{ + gpiod_set_value_cansleep(m9189->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(m9189->reset_gpio, 1); + msleep(30); + gpiod_set_value_cansleep(m9189->reset_gpio, 0); + msleep(55); +} + +static int m9189_on(struct m9189_panel *m9189) +{ + struct mipi_dsi_multi_context ctx = { .dsi = m9189->dsi }; + + ctx.dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + /* Gamma 2.2 */ + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA1, 0x48); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA2, 0xB8); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA3, 0x88); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA4, 0x88); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA5, 0x58); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA6, 0xD2); + mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA7, 0x88); + mipi_dsi_msleep(&ctx, 50); + + /* 4 Lanes */ + mipi_dsi_generic_write_multi(&ctx, (u8[]){ EK79007AD3_PANEL_CTRL3, 0x70 }, 2); + + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); + mipi_dsi_msleep(&ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&ctx); + mipi_dsi_msleep(&ctx, 120); + + return ctx.accum_err; +} + +static int m9189_disable(struct drm_panel *panel) +{ + struct m9189_panel *m9189 = to_m9189_panel(panel); + struct mipi_dsi_multi_context ctx = { .dsi = m9189->dsi }; + + ctx.dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); + mipi_dsi_msleep(&ctx, 120); + + gpiod_set_value_cansleep(m9189->standby_gpio, 1); + + return ctx.accum_err; +} + +static int m9189_prepare(struct drm_panel *panel) +{ + struct m9189_panel *m9189 = to_m9189_panel(panel); + struct device *dev = &m9189->dsi->dev; + int ret; + + ret = regulator_enable(m9189->supply); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(m9189->standby_gpio, 0); + msleep(20); + m9189_reset(m9189); + + ret = m9189_on(m9189); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(m9189->reset_gpio, 1); + regulator_disable(m9189->supply); + return ret; + } + + return 0; +} + +static int m9189_unprepare(struct drm_panel *panel) +{ + struct m9189_panel *m9189 = to_m9189_panel(panel); + + gpiod_set_value_cansleep(m9189->standby_gpio, 1); + msleep(50); + + gpiod_set_value_cansleep(m9189->reset_gpio, 1); + regulator_disable(m9189->supply); + + return 0; +} + +static const struct drm_display_mode m9189_mode = { + .clock = (1024 + 160 + 160 + 10) * (600 + 12 + 23 + 1) * 60 / 1000, + .hdisplay = 1024, + .hsync_start = 1024 + 160, + .hsync_end = 1024 + 160 + 160, + .htotal = 1024 + 160 + 160 + 10, + .vdisplay = 600, + .vsync_start = 600 + 12, + .vsync_end = 600 + 12 + 23, + .vtotal = 600 + 12 + 23 + 1, + .width_mm = 154, + .height_mm = 86, +}; + +static int m9189_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + return drm_connector_helper_get_modes_fixed(connector, &m9189_mode); +} + +static const struct drm_panel_funcs m9189_panel_funcs = { + .prepare = m9189_prepare, + .unprepare = m9189_unprepare, + .disable = m9189_disable, + .get_modes = m9189_get_modes, +}; + +static int lxd_m9189_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct m9189_panel *m9189; + int ret; + + m9189 = devm_kzalloc(dev, sizeof(*m9189), GFP_KERNEL); + if (!m9189) + return -ENOMEM; + + m9189->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(m9189->supply)) + return dev_err_probe(dev, PTR_ERR(m9189->supply), + "Failed to get power-supply\n"); + + m9189->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(m9189->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(m9189->reset_gpio), + "Failed to get reset-gpios\n"); + + m9189->standby_gpio = devm_gpiod_get(dev, "standby", GPIOD_OUT_LOW); + if (IS_ERR(m9189->standby_gpio)) + return dev_err_probe(dev, PTR_ERR(m9189->standby_gpio), + "Failed to get standby-gpios\n"); + + m9189->dsi = dsi; + mipi_dsi_set_drvdata(dsi, m9189); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; + + drm_panel_init(&m9189->panel, dev, &m9189_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + m9189->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&m9189->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&m9189->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); + drm_panel_remove(&m9189->panel); + return ret; + } + + return 0; +} + +static void lxd_m9189_remove(struct mipi_dsi_device *dsi) +{ + struct m9189_panel *m9189 = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&m9189->panel); +} + +static const struct of_device_id lxd_m9189_of_match[] = { + { .compatible = "lxd,m9189a" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lxd_m9189_of_match); + +static struct mipi_dsi_driver lxd_m9189_driver = { + .probe = lxd_m9189_probe, + .remove = lxd_m9189_remove, + .driver = { + .name = "panel-lxd-m9189a", + .of_match_table = lxd_m9189_of_match, + }, +}; +module_mipi_dsi_driver(lxd_m9189_driver); + +MODULE_DESCRIPTION("DRM driver for LXD M9189A MIPI-DSI panels"); +MODULE_LICENSE("GPL"); From 65fb1ca580461a4fb4529a22b0d6f50c13e4c3cc Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Sat, 14 Feb 2026 16:54:07 +0800 Subject: [PATCH 30/62] dt-bindings: vendor: add taiguanck Add the "taiguanck" vendor prefix for Shenzhen Top Group Technology Co., Ltd. The prefix is derived from the vendor's website domain: www.taiguanck.com/en/, which uses "taiguanck" as the primary identifier of the company. Signed-off-by: Langyan Ye Acked-by: Conor Dooley Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260214085409.3489057-2-yelangyan@huaqin.corp-partner.google.com --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 2af68463c685..3ec0951c09c1 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1612,6 +1612,8 @@ patternProperties: "^synopsys,.*": description: Synopsys, Inc. (deprecated, use snps) deprecated: true + "^taiguanck,.*": + description: Shenzhen Top Group Technology Co., Ltd. "^taos,.*": description: Texas Advanced Optoelectronic Solutions Inc. "^tbs,.*": From 75a703c6810fa566e5a64dc0c3e45d2d891307e2 Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Sat, 14 Feb 2026 16:54:08 +0800 Subject: [PATCH 31/62] dt-bindings: display: panel: Add compatible for TAIGUAN XTI05101-01A Add a new compatible for the panel TAIGUAN XTI05101-01A. This panel uses JD9365DA-H3 IC, so add the compatible to the jd9365da-h3 binding files. Signed-off-by: Langyan Ye Acked-by: Rob Herring (Arm) Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260214085409.3489057-3-yelangyan@huaqin.corp-partner.google.com --- .../devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml index 5802fb3c9ffe..0bb4980555e2 100644 --- a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml +++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml @@ -23,6 +23,7 @@ properties: - melfas,lmfbx101117480 - radxa,display-10hd-ad001 - radxa,display-8hd-ad002 + - taiguanck,xti05101-01a - const: jadard,jd9365da-h3 reg: From 5c19c4ed5ae45553380afa6de7cd17d4e5bef827 Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Sat, 14 Feb 2026 16:54:09 +0800 Subject: [PATCH 32/62] drm/panel: panel-jadard-jd9365da-h3: support for taiguan-xti05101-01a MIPI-DSI panel The taiguan-xti05101-01a is a 10.1" TFT panel. The MIPI controller on this panel is the same as the other panels here, so add this panel to this driver. Signed-off-by: Langyan Ye Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260214085409.3489057-4-yelangyan@huaqin.corp-partner.google.com --- .../gpu/drm/panel/panel-jadard-jd9365da-h3.c | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index 5386a06fcd08..c33c611e03c0 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -1366,6 +1366,246 @@ static const struct jadard_panel_desc anbernic_rgds_display_desc = { MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, }; +static int taiguan_xti05101_01a_init_cmds(struct jadard *jadard) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; + + jd9365da_switch_page(&dsi_ctx, 0x00); + jadard_enable_standard_cmds(&dsi_ctx); + + jd9365da_switch_page(&dsi_ctx, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xd7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xd7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x6a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x2e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x1a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x15); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x58); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x39); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x27); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x2b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x16); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x2e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x2f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x4d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x3c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x31); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x24); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x58); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x39); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x27); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x2b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x16); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x2e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x2f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x4d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x3c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x43); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x36); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x31); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x24); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x02); + + jd9365da_switch_page(&dsi_ctx, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x4e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x4c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x4a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x53); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x51); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x4f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x4d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x4b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x0a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x75); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xb4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x6c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xbb); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2a); + + jd9365da_switch_page(&dsi_ctx, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58); + + jd9365da_switch_page(&dsi_ctx, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 20); + + return dsi_ctx.accum_err; +}; + +static const struct jadard_panel_desc taiguan_xti05101_01a_desc = { + .mode = { + .clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000, + + .hdisplay = 800, + .hsync_start = 800 + 24, + .hsync_end = 800 + 24 + 24, + .htotal = 800 + 24 + 24 + 24, + + .vdisplay = 1280, + .vsync_start = 1280 + 30, + .vsync_end = 1280 + 30 + 4, + .vtotal = 1280 + 30 + 4 + 8, + + .width_mm = 135, + .height_mm = 216, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .init = taiguan_xti05101_01a_init_cmds, + .lp11_before_reset = true, + .reset_before_power_off_vcioo = true, + .vcioo_to_lp11_delay_ms = 5, + .lp11_to_reset_delay_ms = 10, + .backlight_off_to_display_off_delay_ms = 3, + .display_off_to_enter_sleep_delay_ms = 50, + .enter_sleep_to_reset_down_delay_ms = 100, +}; + static int jadard_dsi_probe(struct mipi_dsi_device *dsi) { struct device *dev = &dsi->dev; @@ -1463,6 +1703,10 @@ static const struct of_device_id jadard_of_match[] = { .compatible = "radxa,display-8hd-ad002", .data = &radxa_display_8hd_ad002_desc }, + { + .compatible = "taiguanck,xti05101-01a", + .data = &taiguan_xti05101_01a_desc + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, jadard_of_match); From b26c17c924ff9ecae6312e1947cc6c1b561b40a7 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Tue, 3 Mar 2026 15:21:24 +0000 Subject: [PATCH 33/62] drm/panel: ilitek-ili9882t: use gpiod_set_value_cansleep() All of these GPIO calls are in process context where they can sleep. Use the appropriate function call to allow use of this driver with GPIO controllers that might sleep. Signed-off-by: John Keeping Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260303152124.187791-1-jkeeping@inmusicbrands.com --- drivers/gpu/drm/panel/panel-ilitek-ili9882t.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index 8b2bfb7d3638..5f4e0d82ee67 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -592,7 +592,7 @@ static int ili9882t_unprepare(struct drm_panel *panel) { struct ili9882t *ili = to_ili9882t(panel); - gpiod_set_value(ili->enable_gpio, 0); + gpiod_set_value_cansleep(ili->enable_gpio, 0); usleep_range(1000, 2000); regulator_disable(ili->avee); regulator_disable(ili->avdd); @@ -608,7 +608,7 @@ static int ili9882t_prepare(struct drm_panel *panel) struct ili9882t *ili = to_ili9882t(panel); int ret; - gpiod_set_value(ili->enable_gpio, 0); + gpiod_set_value_cansleep(ili->enable_gpio, 0); usleep_range(1000, 1500); ret = regulator_enable(ili->pp3300); @@ -638,11 +638,11 @@ static int ili9882t_prepare(struct drm_panel *panel) } usleep_range(1000, 2000); - gpiod_set_value(ili->enable_gpio, 1); + gpiod_set_value_cansleep(ili->enable_gpio, 1); usleep_range(1000, 2000); - gpiod_set_value(ili->enable_gpio, 0); + gpiod_set_value_cansleep(ili->enable_gpio, 0); msleep(50); - gpiod_set_value(ili->enable_gpio, 1); + gpiod_set_value_cansleep(ili->enable_gpio, 1); usleep_range(6000, 10000); ret = ili->desc->init(ili); @@ -652,7 +652,7 @@ static int ili9882t_prepare(struct drm_panel *panel) return 0; poweroff: - gpiod_set_value(ili->enable_gpio, 0); + gpiod_set_value_cansleep(ili->enable_gpio, 0); regulator_disable(ili->avee); poweroffavdd: regulator_disable(ili->avdd); @@ -793,7 +793,7 @@ static int ili9882t_add(struct ili9882t *ili) return PTR_ERR(ili->enable_gpio); } - gpiod_set_value(ili->enable_gpio, 0); + gpiod_set_value_cansleep(ili->enable_gpio, 0); err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); if (err < 0) { From 46be11b678e0a9a5deaf132cb21afd1259e32d6e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 17 Jan 2026 01:49:48 +0100 Subject: [PATCH 34/62] drm/panel: simple: Add Waveshare 13.3" panel support Add WaveShare 13.3inch 1920x1080 DSI Capacitive Touch Display support. While the panel is described as DPI panel, it is part of a larger unit in non-removable metal casing, so the actual internal configuration is not known. The panel is attached to "waveshare,dsi2dpi" bridge via DT. It is likely that internally, this panel is an LVDS panel, connected to ICN6211 DSI-to-DPI bridge and then another unknown DPI-to-LVDS bridge. Current device link is at https://www.waveshare.com/13.3inch-dsi-lcd.htm Signed-off-by: Marek Vasut Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260117005028.126361-1-marek.vasut+renesas@mailbox.org --- drivers/gpu/drm/panel/panel-simple.c | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index f79796387c58..e1d15434ea54 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5032,6 +5032,33 @@ static const struct panel_desc vl050_8048nt_c01 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, }; +static const struct drm_display_mode waveshare_133inch_mode = { + .clock = 148500, + .hdisplay = 1920, + .hsync_start = 1920 + 88, + .hsync_end = 1920 + 88 + 44, + .htotal = 1920 + 88 + 44 + 148, + .vdisplay = 1080, + .vsync_start = 1080 + 4, + .vsync_end = 1080 + 4 + 5, + .vtotal = 1080 + 4 + 5 + 36, + .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC, +}; + +static const struct panel_desc waveshare_133inch = { + .modes = &waveshare_133inch_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 293, + .height = 163, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .connector_type = DRM_MODE_CONNECTOR_DPI, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE, +}; + static const struct drm_display_mode winstar_wf35ltiacd_mode = { .clock = 6410, .hdisplay = 320, @@ -5635,6 +5662,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "vxt,vl050-8048nt-c01", .data = &vl050_8048nt_c01, + }, { + .compatible = "waveshare,13.3inch-panel", + .data = &waveshare_133inch, }, { .compatible = "winstar,wf35ltiacd", .data = &winstar_wf35ltiacd, From 4063a4d39f97b02cb7bbdb47f0b0050b7821e187 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Mon, 9 Mar 2026 18:03:36 +0000 Subject: [PATCH 35/62] dt-bindings: display: panel: Document the rotation property The Jadard jd9365da-h3 driver already allows DRM to get the panel orientation via the device tree rotation property (described in panel-common.yaml), but it's currently not documented. Describe it in the driver documentation to fix a dtbs_check error in Xiaomi Mi Smart Clock x04g, where the panel is landscape-oriented. Signed-off-by: Luca Leonardo Scorcia Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260309180353.8220-1-l.scorcia@gmail.com --- .../devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml index 0bb4980555e2..e39efb44ed42 100644 --- a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml +++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml @@ -36,9 +36,8 @@ properties: description: supply regulator for VCCIO, usually 1.8V reset-gpios: true - backlight: true - + rotation: true port: true required: From 8bcf6264b73f5cfa24e734c0027a69f207e26445 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 9 Mar 2026 20:26:02 -0400 Subject: [PATCH 36/62] dt-bindings: arm: qcom: document google,bonito-tianma board The Pixel 3a XL is code-named by Google as "bonito". The google,bonito-tianma compatible represents a variant of the Pixel 3a XL board with a Tianma/Novatek NT37700F panel. Document the google,bonito board compatible and the google,bonito-tianma variant in the bindings. Signed-off-by: Richard Acayan Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260310002606.16413-2-mailingradian@gmail.com --- Documentation/devicetree/bindings/arm/qcom.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index d48c625d3fc4..1a955d1b14bf 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -844,6 +844,12 @@ properties: - google,sargo - const: qcom,sdm670 + - items: + - enum: + - google,bonito-tianma + - const: google,bonito + - const: qcom,sdm670 + - items: - enum: - qcom,sdx55-mtp From e0102c94ec12640a6b66037adc6e9ae5c9c0dc27 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 9 Mar 2026 20:26:03 -0400 Subject: [PATCH 37/62] dt-bindings: panel-simple-dsi: add nt37700f compatible Add the compatible for the NT37700F panel found on the Pixel 3a XL. Signed-off-by: Richard Acayan Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260310002606.16413-3-mailingradian@gmail.com --- .../devicetree/bindings/display/panel/panel-simple-dsi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index 2f90c887b7b8..cc8d795df732 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -49,6 +49,8 @@ properties: - lg,lh500wx1-sd03 # Lincoln LCD197 5" 1080x1920 LCD panel - lincolntech,lcd197 + # Novatek NT37700F 1080x2160 AMOLED panel + - novatek,nt37700f # One Stop Displays OSD101T2587-53TS 10.1" 1920x1200 panel - osddisplays,osd101t2587-53ts # Panasonic 10" WUXGA TFT LCD panel From 2db443036fe9daf170e5919face475307d486641 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 9 Mar 2026 20:26:04 -0400 Subject: [PATCH 38/62] drm/panel: Add Novatek/Tianma NT37700F panel Some Pixel 3a XL devices have a Tianma panel. Add support for it, with the aid of linux-mdss-dsi-panel-driver-generator. Link: https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator Signed-off-by: Richard Acayan Tested-by: Yifei Zhan Reviewed-by: Dmitry Baryshkov Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260310002606.16413-4-mailingradian@gmail.com --- drivers/gpu/drm/panel/Kconfig | 9 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-novatek-nt37700f.c | 299 ++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-novatek-nt37700f.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a59bca750ee2..555991132083 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -563,6 +563,15 @@ config DRM_PANEL_NOVATEK_NT36672E LCD panel module. The panel has a resolution of 1080x2408 and uses 24 bit RGB per pixel. +config DRM_PANEL_NOVATEK_NT37700F + tristate "Novatek NT37700F DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Novatek NT37700F DSI + panel module. The panel has a resolution of 1080x2160. + config DRM_PANEL_NOVATEK_NT37801 tristate "Novatek NT37801/NT37810 AMOLED DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index a93c54abe968..deddff9359bc 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o diff --git a/drivers/gpu/drm/panel/panel-novatek-nt37700f.c b/drivers/gpu/drm/panel/panel-novatek-nt37700f.c new file mode 100644 index 000000000000..74f46a268c0f --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt37700f.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, The Linux Foundation. All rights reserved. + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include