Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* [PATCH v2] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Saurabh Sengar @ 2025-02-25  8:56 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, deller, akpm, linux-hyperv,
	linux-fbdev, dri-devel, linux-kernel
  Cc: ssengar, mhklinux

When a Hyper-V framebuffer device is unbind, hyperv_fb driver tries to
release the framebuffer forcefully. If this framebuffer is in use it
produce the following WARN and hence this framebuffer is never released.

[   44.111220] WARNING: CPU: 35 PID: 1882 at drivers/video/fbdev/core/fb_info.c:70 framebuffer_release+0x2c/0x40
< snip >
[   44.111289] Call Trace:
[   44.111290]  <TASK>
[   44.111291]  ? show_regs+0x6c/0x80
[   44.111295]  ? __warn+0x8d/0x150
[   44.111298]  ? framebuffer_release+0x2c/0x40
[   44.111300]  ? report_bug+0x182/0x1b0
[   44.111303]  ? handle_bug+0x6e/0xb0
[   44.111306]  ? exc_invalid_op+0x18/0x80
[   44.111308]  ? asm_exc_invalid_op+0x1b/0x20
[   44.111311]  ? framebuffer_release+0x2c/0x40
[   44.111313]  ? hvfb_remove+0x86/0xa0 [hyperv_fb]
[   44.111315]  vmbus_remove+0x24/0x40 [hv_vmbus]
[   44.111323]  device_remove+0x40/0x80
[   44.111325]  device_release_driver_internal+0x20b/0x270
[   44.111327]  ? bus_find_device+0xb3/0xf0

Fix this by moving the release of framebuffer and assosiated memory
to fb_ops.fb_destroy function, so that framebuffer framework handles
it gracefully.

While we fix this, also replace manual registrations/unregistration of
framebuffer with devm_register_framebuffer.

Fixes: 68a2d20b79b1 ("drivers/video: add Hyper-V Synthetic Video Frame Buffer Driver")
Signed-off-by: Saurabh Sengar <ssengar@linux.microsoft.com>
---
V2 : Move hvfb_putmem into destroy()

 drivers/video/fbdev/hyperv_fb.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index 363e4ccfcdb7..89ee49f1c3dc 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -282,6 +282,8 @@ static uint screen_depth;
 static uint screen_fb_size;
 static uint dio_fb_size; /* FB size for deferred IO */
 
+static void hvfb_putmem(struct hv_device *hdev, struct fb_info *info);
+
 /* Send message to Hyper-V host */
 static inline int synthvid_send(struct hv_device *hdev,
 				struct synthvid_msg *msg)
@@ -862,6 +864,24 @@ static void hvfb_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width,
 		hvfb_ondemand_refresh_throttle(par, x, y, width, height);
 }
 
+/*
+ * fb_ops.fb_destroy is called by the last put_fb_info() call at the end
+ * of unregister_framebuffer() or fb_release(). Do any cleanup related to
+ * framebuffer here.
+ */
+static void hvfb_destroy(struct fb_info *info)
+{
+	struct hv_device *hdev;
+	struct device *dev;
+	void *driver_data = (void *)info;
+
+	dev = container_of(driver_data, struct device, driver_data);
+	hdev = container_of(dev, struct hv_device, device);
+
+	hvfb_putmem(hdev, info);
+	framebuffer_release(info);
+}
+
 /*
  * TODO: GEN1 codepaths allocate from system or DMA-able memory. Fix the
  *       driver to use the _SYSMEM_ or _DMAMEM_ helpers in these cases.
@@ -877,6 +897,7 @@ static const struct fb_ops hvfb_ops = {
 	.fb_set_par = hvfb_set_par,
 	.fb_setcolreg = hvfb_setcolreg,
 	.fb_blank = hvfb_blank,
+	.fb_destroy	= hvfb_destroy,
 };
 
 /* Get options from kernel paramenter "video=" */
@@ -1172,7 +1193,7 @@ static int hvfb_probe(struct hv_device *hdev,
 	if (ret)
 		goto error;
 
-	ret = register_framebuffer(info);
+	ret = devm_register_framebuffer(&hdev->device, info);
 	if (ret) {
 		pr_err("Unable to register framebuffer\n");
 		goto error;
@@ -1220,14 +1241,9 @@ static void hvfb_remove(struct hv_device *hdev)
 
 	fb_deferred_io_cleanup(info);
 
-	unregister_framebuffer(info);
 	cancel_delayed_work_sync(&par->dwork);
 
 	vmbus_close(hdev->channel);
-	hv_set_drvdata(hdev, NULL);
-
-	hvfb_putmem(hdev, info);
-	framebuffer_release(info);
 }
 
 static int hvfb_suspend(struct hv_device *hdev)
-- 
2.43.0


^ permalink raw reply related

* [syzbot] [fbdev?] KASAN: slab-out-of-bounds Read in fbcon_prepare_logo
From: syzbot @ 2025-02-25  7:06 UTC (permalink / raw)
  To: deller, dri-devel, linux-fbdev, linux-kernel, simona,
	syzkaller-bugs

Hello,

syzbot found the following issue on:

HEAD commit:    d082ecbc71e9 Linux 6.14-rc4
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=100c97a4580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=b1635bf4c5557b92
dashboard link: https://syzkaller.appspot.com/bug?extid=0c815b25cdb3678e7083
compiler:       gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40

Unfortunately, I don't have any reproducer for this issue yet.

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/323a5d590eec/disk-d082ecbc.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/f7c4b6e33fd9/vmlinux-d082ecbc.xz
kernel image: https://storage.googleapis.com/syzbot-assets/c518bbd55334/bzImage-d082ecbc.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+0c815b25cdb3678e7083@syzkaller.appspotmail.com

==================================================================
BUG: KASAN: slab-out-of-bounds in scr_memcpyw include/linux/vt_buffer.h:38 [inline]
BUG: KASAN: slab-out-of-bounds in fbcon_prepare_logo+0xa15/0xc80 drivers/video/fbdev/core/fbcon.c:614
Read of size 256 at addr ffff888033e96f60 by task syz.0.317/7285

CPU: 1 UID: 0 PID: 7285 Comm: syz.0.317 Not tainted 6.14.0-rc4-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2025
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:408 [inline]
 print_report+0xc3/0x670 mm/kasan/report.c:521
 kasan_report+0xd9/0x110 mm/kasan/report.c:634
 check_region_inline mm/kasan/generic.c:183 [inline]
 kasan_check_range+0xef/0x1a0 mm/kasan/generic.c:189
 __asan_memcpy+0x23/0x60 mm/kasan/shadow.c:105
 scr_memcpyw include/linux/vt_buffer.h:38 [inline]
 fbcon_prepare_logo+0xa15/0xc80 drivers/video/fbdev/core/fbcon.c:614
 fbcon_init+0xd41/0x1890 drivers/video/fbdev/core/fbcon.c:1146
 visual_init+0x31d/0x620 drivers/tty/vt/vt.c:1011
 do_bind_con_driver.isra.0+0x57a/0xbf0 drivers/tty/vt/vt.c:3831
 vt_bind drivers/tty/vt/vt.c:3987 [inline]
 store_bind+0x61d/0x760 drivers/tty/vt/vt.c:4059
 dev_attr_store+0x55/0x80 drivers/base/core.c:2439
 sysfs_kf_write+0x117/0x170 fs/sysfs/file.c:139
 kernfs_fop_write_iter+0x33d/0x500 fs/kernfs/file.c:334
 new_sync_write fs/read_write.c:586 [inline]
 vfs_write+0x5ae/0x1150 fs/read_write.c:679
 ksys_write+0x12b/0x250 fs/read_write.c:731
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f44b6f8d169
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f44b7dc3038 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f44b71a5fa0 RCX: 00007f44b6f8d169
RDX: 0000000000000002 RSI: 0000000000000000 RDI: 0000000000000003
RBP: 00007f44b700e2a0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 0000000000000000 R14: 00007f44b71a5fa0 R15: 00007ffee813d3a8
 </TASK>

The buggy address belongs to the physical page:
page: refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff888033e94340 pfn:0x33e94
head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000040 0000000000000000 dead000000000122 0000000000000000
raw: ffff888033e94340 0000000000000000 00000001ffffffff 0000000000000000
head: 00fff00000000040 0000000000000000 dead000000000122 0000000000000000
head: ffff888033e94340 0000000000000000 00000001ffffffff 0000000000000000
head: 00fff00000000002 ffffea0000cfa501 ffffffffffffffff 0000000000000000
head: 0000000000000004 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 2, migratetype Unmovable, gfp_mask 0x140dc0(GFP_USER|__GFP_COMP|__GFP_ZERO), pid 7285, tgid 7284 (syz.0.317), ts 193263994848, free_ts 193235941647
 set_page_owner include/linux/page_owner.h:32 [inline]
 post_alloc_hook+0x181/0x1b0 mm/page_alloc.c:1551
 prep_new_page mm/page_alloc.c:1559 [inline]
 get_page_from_freelist+0xfce/0x2f80 mm/page_alloc.c:3477
 __alloc_frozen_pages_noprof+0x221/0x2470 mm/page_alloc.c:4739
 __alloc_pages_noprof+0xb/0x1b0 mm/page_alloc.c:4773
 __alloc_pages_node_noprof include/linux/gfp.h:265 [inline]
 alloc_pages_node_noprof include/linux/gfp.h:292 [inline]
 ___kmalloc_large_node+0x84/0x1b0 mm/slub.c:4239
 __kmalloc_large_node_noprof+0x1c/0x70 mm/slub.c:4266
 __do_kmalloc_node mm/slub.c:4282 [inline]
 __kmalloc_noprof.cold+0xc/0x61 mm/slub.c:4306
 kmalloc_noprof include/linux/slab.h:905 [inline]
 kzalloc_noprof include/linux/slab.h:1037 [inline]
 vc_do_resize+0x1e3/0x10f0 drivers/tty/vt/vt.c:1174
 vc_resize include/linux/vt_kern.h:49 [inline]
 fbcon_init+0xd1d/0x1890 drivers/video/fbdev/core/fbcon.c:1143
 visual_init+0x31d/0x620 drivers/tty/vt/vt.c:1011
 do_bind_con_driver.isra.0+0x57a/0xbf0 drivers/tty/vt/vt.c:3831
 vt_bind drivers/tty/vt/vt.c:3987 [inline]
 store_bind+0x61d/0x760 drivers/tty/vt/vt.c:4059
 dev_attr_store+0x55/0x80 drivers/base/core.c:2439
 sysfs_kf_write+0x117/0x170 fs/sysfs/file.c:139
 kernfs_fop_write_iter+0x33d/0x500 fs/kernfs/file.c:334
 new_sync_write fs/read_write.c:586 [inline]
 vfs_write+0x5ae/0x1150 fs/read_write.c:679
page last free pid 7285 tgid 7284 stack trace:
 reset_page_owner include/linux/page_owner.h:25 [inline]
 free_pages_prepare mm/page_alloc.c:1127 [inline]
 free_frozen_pages+0x6db/0xfb0 mm/page_alloc.c:2660
 __folio_put+0x32a/0x450 mm/swap.c:112
 vc_do_resize+0xe31/0x10f0 drivers/tty/vt/vt.c:1194
 vc_resize include/linux/vt_kern.h:49 [inline]
 fbcon_startup+0x406/0xb70 drivers/video/fbdev/core/fbcon.c:997
 do_bind_con_driver.isra.0+0x207/0xbf0 drivers/tty/vt/vt.c:3794
 vt_bind drivers/tty/vt/vt.c:3987 [inline]
 store_bind+0x61d/0x760 drivers/tty/vt/vt.c:4059
 dev_attr_store+0x55/0x80 drivers/base/core.c:2439
 sysfs_kf_write+0x117/0x170 fs/sysfs/file.c:139
 kernfs_fop_write_iter+0x33d/0x500 fs/kernfs/file.c:334
 new_sync_write fs/read_write.c:586 [inline]
 vfs_write+0x5ae/0x1150 fs/read_write.c:679
 ksys_write+0x12b/0x250 fs/read_write.c:731
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
 ffff888033e96f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffff888033e96f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff888033e97000: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
                   ^
 ffff888033e97080: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
 ffff888033e97100: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
==================================================================


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

^ permalink raw reply

* Re: [PATCH RESEND 00/13] fbdev: core: Deduplicate cfb/sys drawing fbops
From: Kajtár Zsolt @ 2025-02-24 21:52 UTC (permalink / raw)
  To: Thomas Zimmermann, linux-fbdev, dri-devel
In-Reply-To: <e07fbd60-e362-482d-8233-409a366c9b2f@suse.de>


[-- Attachment #1.1: Type: text/plain, Size: 3390 bytes --]

Hello Thomas!

Wanted to answer earlier but things took time, and a lot more than expected.

> First of all, commit 779121e9f175 ("fbdev: Support for byte-reversed
> framebuffer formats") isn't super complicated AFAICT. I can be
> implemented in the sys_ helpers as well. It seems like you initially did
> that.

Meanwhile I found out that this implementation had corner cases. I also
expected original implementations be a bit more complete.

> About the series at hand: generating code by macro expansion is good for
> simple cases. I've done that in several places within fbdev myself, such
> as [1]. But if the generated code requires Turing-completeness, it
> becomes much harder to see through the macros and understand what is
> going on. This makes code undiscoverable; and discoverability is a
> requirement for maintenance.

In the new version I resorted to only generate tables with them, in
close proximity. The mentioned part made me think when I first run into
it, btw.

> Then there's type-safety and type-casting. The current series defeats it
> by casting various pointers to whatever the macros define. For example,
> looking at the copyarea patches, they use screen_base [2] from struct
> fb_info. The thing is, using screen_base is wrong for sys_copyarea().
> The function should use 'screen_buffer' instead. It works because both
> fields share the same bits of a union. Using screen_base is a bug in the
> current implementation that should be fixed, while this patch series
> would set it in stone.

I've noticed the screen base vs. buffer issue back then and was already
corrected. But it's handled more cleanly now.

> Next, if you look through the commit history, you'll find that there are
> several commits with performance improvements. Memory access in the sys
> variants is not guaranteed to be 32-bit aligned by default. The compiler
> has to assume unaligned access, which results in slower code. Hence,
> some manual intervention has to be done. It's too easy to accidentally
> mess this up by using nontransparent macros for access.

In the new version I made it very hard to get the alignment wrong.

> If you want to do meaningful work here, please do actual refactoring
> instead of throwing unrelated code together. First of all, never use
> macros, but functions. You can supply callback functions to access the
> framebuffer. Each callback should know whether it operates on
> screen_base or screen_buffer.

I've used such callbacks but not for the read/writes as that would have
made the parameter list huge, in terms of lines. Not to mention passing
them down to lowest level.

> But using callbacks for individual reads and writes can have runtime
> overhead. It's better to operate on complete scanlines. The current
> helpers are already organized that way. Again, from the copyarea helper:

If done slightly differently the compiler inlines these and there's no
overhead.

> The inner helper do_something_...() has to be written for various cfb
> and sys cases and can be given as function pointer to a generic helper.
The vertical loops are small, but I kept them separate from the scanline
rendering part.

Thanks for the tips, that was really helpful and used them when applicable.

While the updated version is not quite so as described I hope it isn't
too bad either.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]

^ permalink raw reply

* [PATCH 0/1] Refactoring of framebuffer drawing routines
From: Zsolt Kajtar @ 2025-02-24 20:59 UTC (permalink / raw)
  To: linux-fbdev, dri-devel; +Cc: Zsolt Kajtar

Proper refactoring of the framebuffer drawing routines. This time it's
not only the merging of duplicated code. It isn't half finished either
so the various combinations of foreign endianness, pixel reversing, bits
per pixel, cpu word sizes and byte order should work.

Added myself to look after this code. Based on the time spent on proving
it I don't expect a busy inbox. Famous last words.

Zsolt Kajtar (1):
  Refactoring of framebuffer drawing routines

 MAINTAINERS                             |  16 +
 drivers/video/fbdev/core/Kconfig        |  10 +-
 drivers/video/fbdev/core/cfbcopyarea.c  | 428 +-------------------
 drivers/video/fbdev/core/cfbfillrect.c  | 362 +----------------
 drivers/video/fbdev/core/cfbimgblt.c    | 357 +----------------
 drivers/video/fbdev/core/cfbmem.h       |  43 ++
 drivers/video/fbdev/core/fb_copyarea.h  | 405 +++++++++++++++++++
 drivers/video/fbdev/core/fb_draw.h      | 274 ++++++-------
 drivers/video/fbdev/core/fb_fillrect.h  | 280 ++++++++++++++
 drivers/video/fbdev/core/fb_imageblit.h | 495 ++++++++++++++++++++++++
 drivers/video/fbdev/core/syscopyarea.c  | 369 +-----------------
 drivers/video/fbdev/core/sysfillrect.c  | 324 +---------------
 drivers/video/fbdev/core/sysimgblt.c    | 333 +---------------
 drivers/video/fbdev/core/sysmem.h       |  39 ++
 14 files changed, 1480 insertions(+), 2255 deletions(-)
 create mode 100644 drivers/video/fbdev/core/cfbmem.h
 create mode 100644 drivers/video/fbdev/core/fb_copyarea.h
 create mode 100644 drivers/video/fbdev/core/fb_fillrect.h
 create mode 100644 drivers/video/fbdev/core/fb_imageblit.h
 create mode 100644 drivers/video/fbdev/core/sysmem.h

-- 
2.30.2


^ permalink raw reply

* [PATCH 1/1] Refactoring of framebuffer drawing routines
From: Zsolt Kajtar @ 2025-02-24 20:59 UTC (permalink / raw)
  To: linux-fbdev, dri-devel; +Cc: Zsolt Kajtar
In-Reply-To: <20250224205908.26336-1-soci@c64.rulez.org>

Signed-off-by: Zsolt Kajtar <soci@c64.rulez.org>
---
 MAINTAINERS                             |  16 +
 drivers/video/fbdev/core/Kconfig        |  10 +-
 drivers/video/fbdev/core/cfbcopyarea.c  | 428 +-------------------
 drivers/video/fbdev/core/cfbfillrect.c  | 362 +----------------
 drivers/video/fbdev/core/cfbimgblt.c    | 357 +----------------
 drivers/video/fbdev/core/cfbmem.h       |  43 ++
 drivers/video/fbdev/core/fb_copyarea.h  | 405 +++++++++++++++++++
 drivers/video/fbdev/core/fb_draw.h      | 274 ++++++-------
 drivers/video/fbdev/core/fb_fillrect.h  | 280 ++++++++++++++
 drivers/video/fbdev/core/fb_imageblit.h | 495 ++++++++++++++++++++++++
 drivers/video/fbdev/core/syscopyarea.c  | 369 +-----------------
 drivers/video/fbdev/core/sysfillrect.c  | 324 +---------------
 drivers/video/fbdev/core/sysimgblt.c    | 333 +---------------
 drivers/video/fbdev/core/sysmem.h       |  39 ++
 14 files changed, 1480 insertions(+), 2255 deletions(-)
 create mode 100644 drivers/video/fbdev/core/cfbmem.h
 create mode 100644 drivers/video/fbdev/core/fb_copyarea.h
 create mode 100644 drivers/video/fbdev/core/fb_fillrect.h
 create mode 100644 drivers/video/fbdev/core/fb_imageblit.h
 create mode 100644 drivers/video/fbdev/core/sysmem.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 896a307fa..69c5542c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9110,6 +9110,22 @@ S:	Odd Fixes
 T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:	drivers/video/fbdev/core/
 
+FRAMEBUFFER DRAWING
+M:	Zsolt Kajtar <soci@c64.rulez.org>
+S:	Odd Fixes
+F:	drivers/video/fbdev/core/cfbcopyarea.c
+F:	drivers/video/fbdev/core/cfbfillrect.c
+F:	drivers/video/fbdev/core/cfbimgblt.c
+F:	drivers/video/fbdev/core/cfbmem.h
+F:	drivers/video/fbdev/core/fb_copyarea.h
+F:	drivers/video/fbdev/core/fb_draw.h
+F:	drivers/video/fbdev/core/fb_fillrect.h
+F:	drivers/video/fbdev/core/fb_imageblit.h
+F:	drivers/video/fbdev/core/syscopyarea.c
+F:	drivers/video/fbdev/core/sysfillrect.c
+F:	drivers/video/fbdev/core/sysimgblt.c
+F:	drivers/video/fbdev/core/sysmem.h
+
 FRAMEBUFFER LAYER
 M:	Helge Deller <deller@gmx.de>
 L:	linux-fbdev@vger.kernel.org
diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig
index d554d8c54..4abe12db7 100644
--- a/drivers/video/fbdev/core/Kconfig
+++ b/drivers/video/fbdev/core/Kconfig
@@ -69,7 +69,7 @@ config FB_CFB_REV_PIXELS_IN_BYTE
 	bool
 	depends on FB_CORE
 	help
-	  Allow generic frame-buffer functions to work on displays with 1, 2
+	  Allow I/O memory frame-buffer functions to work on displays with 1, 2
 	  and 4 bits per pixel depths which has opposite order of pixels in
 	  byte order to bytes in long order.
 
@@ -97,6 +97,14 @@ config FB_SYS_IMAGEBLIT
 	  blitting. This is used by drivers that don't provide their own
 	  (accelerated) version and the framebuffer is in system RAM.
 
+config FB_SYS_REV_PIXELS_IN_BYTE
+	bool
+	depends on FB_CORE
+	help
+	  Allow virtual memory frame-buffer functions to work on displays with 1, 2
+	  and 4 bits per pixel depths which has opposite order of pixels in
+	  byte order to bytes in long order.
+
 config FB_PROVIDE_GET_FB_UNMAPPED_AREA
 	bool
 	depends on FB
diff --git a/drivers/video/fbdev/core/cfbcopyarea.c b/drivers/video/fbdev/core/cfbcopyarea.c
index a271f57d9..23fbf3a8d 100644
--- a/drivers/video/fbdev/core/cfbcopyarea.c
+++ b/drivers/video/fbdev/core/cfbcopyarea.c
@@ -1,440 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic function for frame buffer with packed pixels of any depth.
- *
- *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- *
- * NOTES:
- *
- *  This is for cfb packed pixels. Iplan and such are incorporated in the
- *  drivers that need them.
- *
- *  FIXME
- *
- *  Also need to add code to deal with cards endians that are different than
- *  the native cpu endians. I also need to deal with MSB position in the word.
- *
- *  The two functions or copying forward and backward could be split up like
- *  the ones for filling, i.e. in aligned and unaligned versions. This would
- *  help moving some redundant computations and branches out of the loop, too.
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
-
 #include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
-#include <asm/io.h>
-#include "fb_draw.h"
-
-#if BITS_PER_LONG == 32
-#  define FB_WRITEL fb_writel
-#  define FB_READL  fb_readl
-#else
-#  define FB_WRITEL fb_writeq
-#  define FB_READL  fb_readq
-#endif
-
-    /*
-     *  Generic bitwise copy algorithm
-     */
-
-static void
-bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
-		const unsigned long __iomem *src, unsigned src_idx, int bits,
-		unsigned n, u32 bswapmask)
-{
-	unsigned long first, last;
-	int const shift = dst_idx-src_idx;
 
-#if 0
-	/*
-	 * If you suspect bug in this function, compare it with this simple
-	 * memmove implementation.
-	 */
-	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
-		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
-	return;
+#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
 #endif
 
-	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
-	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
-	if (!shift) {
-		// Same alignment for source and dest
-
-		if (dst_idx+n <= bits) {
-			// Single word
-			if (last)
-				first &= last;
-			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
-		} else {
-			// Multiple destination words
-
-			// Leading bits
-			if (first != ~0UL) {
-				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
-				dst++;
-				src++;
-				n -= bits - dst_idx;
-			}
-
-			// Main chunk
-			n /= bits;
-			while (n >= 8) {
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				FB_WRITEL(FB_READL(src++), dst++);
-				n -= 8;
-			}
-			while (n--)
-				FB_WRITEL(FB_READL(src++), dst++);
-
-			// Trailing bits
-			if (last)
-				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
-		}
-	} else {
-		/* Different alignment for source and dest */
-		unsigned long d0, d1;
-		int m;
-
-		int const left = shift & (bits - 1);
-		int const right = -shift & (bits - 1);
-
-		if (dst_idx+n <= bits) {
-			// Single destination word
-			if (last)
-				first &= last;
-			d0 = FB_READL(src);
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			if (shift > 0) {
-				// Single source word
-				d0 <<= left;
-			} else if (src_idx+n <= bits) {
-				// Single source word
-				d0 >>= right;
-			} else {
-				// 2 source words
-				d1 = FB_READL(src + 1);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-				d0 = d0 >> right | d1 << left;
-			}
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
-		} else {
-			// Multiple destination words
-			/** We must always remember the last value read, because in case
-			SRC and DST overlap bitwise (e.g. when moving just one pixel in
-			1bpp), we always collect one full long for DST and that might
-			overlap with the current long from SRC. We store this value in
-			'd0'. */
-			d0 = FB_READL(src++);
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			// Leading bits
-			if (shift > 0) {
-				// Single source word
-				d1 = d0;
-				d0 <<= left;
-				n -= bits - dst_idx;
-			} else {
-				// 2 source words
-				d1 = FB_READL(src++);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-
-				d0 = d0 >> right | d1 << left;
-				n -= bits - dst_idx;
-			}
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
-			d0 = d1;
-			dst++;
-
-			// Main chunk
-			m = n % bits;
-			n /= bits;
-			while ((n >= 4) && !bswapmask) {
-				d1 = FB_READL(src++);
-				FB_WRITEL(d0 >> right | d1 << left, dst++);
-				d0 = d1;
-				d1 = FB_READL(src++);
-				FB_WRITEL(d0 >> right | d1 << left, dst++);
-				d0 = d1;
-				d1 = FB_READL(src++);
-				FB_WRITEL(d0 >> right | d1 << left, dst++);
-				d0 = d1;
-				d1 = FB_READL(src++);
-				FB_WRITEL(d0 >> right | d1 << left, dst++);
-				d0 = d1;
-				n -= 4;
-			}
-			while (n--) {
-				d1 = FB_READL(src++);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-				d0 = d0 >> right | d1 << left;
-				d0 = fb_rev_pixels_in_long(d0, bswapmask);
-				FB_WRITEL(d0, dst++);
-				d0 = d1;
-			}
-
-			// Trailing bits
-			if (m) {
-				if (m <= bits - right) {
-					// Single source word
-					d0 >>= right;
-				} else {
-					// 2 source words
-					d1 = FB_READL(src);
-					d1 = fb_rev_pixels_in_long(d1,
-								bswapmask);
-					d0 = d0 >> right | d1 << left;
-				}
-				d0 = fb_rev_pixels_in_long(d0, bswapmask);
-				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
-			}
-		}
-	}
-}
-
-    /*
-     *  Generic bitwise copy algorithm, operating backward
-     */
-
-static void
-bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
-		const unsigned long __iomem *src, unsigned src_idx, int bits,
-		unsigned n, u32 bswapmask)
-{
-	unsigned long first, last;
-	int shift;
-
-#if 0
-	/*
-	 * If you suspect bug in this function, compare it with this simple
-	 * memmove implementation.
-	 */
-	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
-		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
-	return;
-#endif
-
-	dst += (dst_idx + n - 1) / bits;
-	src += (src_idx + n - 1) / bits;
-	dst_idx = (dst_idx + n - 1) % bits;
-	src_idx = (src_idx + n - 1) % bits;
-
-	shift = dst_idx-src_idx;
-
-	first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
-	last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
-
-	if (!shift) {
-		// Same alignment for source and dest
-
-		if ((unsigned long)dst_idx+1 >= n) {
-			// Single word
-			if (first)
-				last &= first;
-			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
-		} else {
-			// Multiple destination words
-
-			// Leading bits
-			if (first) {
-				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
-				dst--;
-				src--;
-				n -= dst_idx+1;
-			}
-
-			// Main chunk
-			n /= bits;
-			while (n >= 8) {
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				FB_WRITEL(FB_READL(src--), dst--);
-				n -= 8;
-			}
-			while (n--)
-				FB_WRITEL(FB_READL(src--), dst--);
-
-			// Trailing bits
-			if (last != -1UL)
-				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
-		}
-	} else {
-		// Different alignment for source and dest
-		unsigned long d0, d1;
-		int m;
-
-		int const left = shift & (bits-1);
-		int const right = -shift & (bits-1);
-
-		if ((unsigned long)dst_idx+1 >= n) {
-			// Single destination word
-			if (first)
-				last &= first;
-			d0 = FB_READL(src);
-			if (shift < 0) {
-				// Single source word
-				d0 >>= right;
-			} else if (1+(unsigned long)src_idx >= n) {
-				// Single source word
-				d0 <<= left;
-			} else {
-				// 2 source words
-				d1 = FB_READL(src - 1);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-				d0 = d0 << left | d1 >> right;
-			}
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
-		} else {
-			// Multiple destination words
-			/** We must always remember the last value read, because in case
-			SRC and DST overlap bitwise (e.g. when moving just one pixel in
-			1bpp), we always collect one full long for DST and that might
-			overlap with the current long from SRC. We store this value in
-			'd0'. */
-
-			d0 = FB_READL(src--);
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			// Leading bits
-			if (shift < 0) {
-				// Single source word
-				d1 = d0;
-				d0 >>= right;
-			} else {
-				// 2 source words
-				d1 = FB_READL(src--);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-				d0 = d0 << left | d1 >> right;
-			}
-			d0 = fb_rev_pixels_in_long(d0, bswapmask);
-			if (!first)
-				FB_WRITEL(d0, dst);
-			else
-				FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
-			d0 = d1;
-			dst--;
-			n -= dst_idx+1;
-
-			// Main chunk
-			m = n % bits;
-			n /= bits;
-			while ((n >= 4) && !bswapmask) {
-				d1 = FB_READL(src--);
-				FB_WRITEL(d0 << left | d1 >> right, dst--);
-				d0 = d1;
-				d1 = FB_READL(src--);
-				FB_WRITEL(d0 << left | d1 >> right, dst--);
-				d0 = d1;
-				d1 = FB_READL(src--);
-				FB_WRITEL(d0 << left | d1 >> right, dst--);
-				d0 = d1;
-				d1 = FB_READL(src--);
-				FB_WRITEL(d0 << left | d1 >> right, dst--);
-				d0 = d1;
-				n -= 4;
-			}
-			while (n--) {
-				d1 = FB_READL(src--);
-				d1 = fb_rev_pixels_in_long(d1, bswapmask);
-				d0 = d0 << left | d1 >> right;
-				d0 = fb_rev_pixels_in_long(d0, bswapmask);
-				FB_WRITEL(d0, dst--);
-				d0 = d1;
-			}
-
-			// Trailing bits
-			if (m) {
-				if (m <= bits - left) {
-					// Single source word
-					d0 <<= left;
-				} else {
-					// 2 source words
-					d1 = FB_READL(src);
-					d1 = fb_rev_pixels_in_long(d1,
-								bswapmask);
-					d0 = d0 << left | d1 >> right;
-				}
-				d0 = fb_rev_pixels_in_long(d0, bswapmask);
-				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
-			}
-		}
-	}
-}
+#include "cfbmem.h"
+#include "fb_copyarea.h"
 
 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
 {
-	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
-	u32 height = area->height, width = area->width;
-	unsigned int const bits_per_line = p->fix.line_length * 8u;
-	unsigned long __iomem *base = NULL;
-	int bits = BITS_PER_LONG, bytes = bits >> 3;
-	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
-	u32 bswapmask = fb_compute_bswapmask(p);
-
 	if (p->state != FBINFO_STATE_RUNNING)
 		return;
 
 	if (p->flags & FBINFO_VIRTFB)
-		fb_warn_once(p, "Framebuffer is not in I/O address space.");
-
-	/* if the beginning of the target area might overlap with the end of
-	the source area, be have to copy the area reverse. */
-	if ((dy == sy && dx > sx) || (dy > sy)) {
-		dy += height;
-		sy += height;
-		rev_copy = 1;
-	}
-
-	// split the base of the framebuffer into a long-aligned address and the
-	// index of the first bit
-	base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
-	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
-	// add offset of source and target area
-	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
-	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
+		fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__);
 
 	if (p->fbops->fb_sync)
 		p->fbops->fb_sync(p);
 
-	if (rev_copy) {
-		while (height--) {
-			dst_idx -= bits_per_line;
-			src_idx -= bits_per_line;
-			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
-				base + (src_idx / bits), src_idx % bits, bits,
-				width*p->var.bits_per_pixel, bswapmask);
-		}
-	} else {
-		while (height--) {
-			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
-				base + (src_idx / bits), src_idx % bits, bits,
-				width*p->var.bits_per_pixel, bswapmask);
-			dst_idx += bits_per_line;
-			src_idx += bits_per_line;
-		}
-	}
+	fb_copyarea(p, area);
 }
-
 EXPORT_SYMBOL(cfb_copyarea);
 
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated copyarea");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("I/O memory packed pixel framebuffer area copy");
 MODULE_LICENSE("GPL");
-
diff --git a/drivers/video/fbdev/core/cfbfillrect.c b/drivers/video/fbdev/core/cfbfillrect.c
index cbaa4c9e2..615de8925 100644
--- a/drivers/video/fbdev/core/cfbfillrect.c
+++ b/drivers/video/fbdev/core/cfbfillrect.c
@@ -1,374 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic fillrect for frame buffers with packed pixels of any depth.
- *
- *      Copyright (C)  2000 James Simmons (jsimmons@linux-fbdev.org)
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- *
- * NOTES:
- *
- *  Also need to add code to deal with cards endians that are different than
- *  the native cpu endians. I also need to deal with MSB position in the word.
- *
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
 #include <linux/module.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
-#include "fb_draw.h"
 
-#if BITS_PER_LONG == 32
-#  define FB_WRITEL fb_writel
-#  define FB_READL  fb_readl
-#else
-#  define FB_WRITEL fb_writeq
-#  define FB_READL  fb_readq
+#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
 #endif
 
-    /*
-     *  Aligned pattern fill using 32/64-bit memory accesses
-     */
-
-static void
-bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
-		unsigned long pat, unsigned n, int bits, u32 bswapmask)
-{
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
-	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
-	if (dst_idx+n <= bits) {
-		// Single word
-		if (last)
-			first &= last;
-		FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
-	} else {
-		// Multiple destination words
-
-		// Leading bits
-		if (first!= ~0UL) {
-			FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
-			dst++;
-			n -= bits - dst_idx;
-		}
-
-		// Main chunk
-		n /= bits;
-		while (n >= 8) {
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			FB_WRITEL(pat, dst++);
-			n -= 8;
-		}
-		while (n--)
-			FB_WRITEL(pat, dst++);
-
-		// Trailing bits
-		if (last)
-			FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
-	}
-}
-
-
-    /*
-     *  Unaligned generic pattern fill using 32/64-bit memory accesses
-     *  The pattern must have been expanded to a full 32/64-bit value
-     *  Left/right are the appropriate shifts to convert to the pattern to be
-     *  used for the next 32/64-bit word
-     */
-
-static void
-bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
-		  unsigned long pat, int left, int right, unsigned n, int bits)
-{
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		// Single word
-		if (last)
-			first &= last;
-		FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
-	} else {
-		// Multiple destination words
-		// Leading bits
-		if (first) {
-			FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			n -= bits - dst_idx;
-		}
-
-		// Main chunk
-		n /= bits;
-		while (n >= 4) {
-			FB_WRITEL(pat, dst++);
-			pat = pat << left | pat >> right;
-			FB_WRITEL(pat, dst++);
-			pat = pat << left | pat >> right;
-			FB_WRITEL(pat, dst++);
-			pat = pat << left | pat >> right;
-			FB_WRITEL(pat, dst++);
-			pat = pat << left | pat >> right;
-			n -= 4;
-		}
-		while (n--) {
-			FB_WRITEL(pat, dst++);
-			pat = pat << left | pat >> right;
-		}
-
-		// Trailing bits
-		if (last)
-			FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
-	}
-}
-
-    /*
-     *  Aligned pattern invert using 32/64-bit memory accesses
-     */
-static void
-bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst,
-		    int dst_idx, unsigned long pat, unsigned n, int bits,
-		    u32 bswapmask)
-{
-	unsigned long val = pat, dat;
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
-	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
-	if (dst_idx+n <= bits) {
-		// Single word
-		if (last)
-			first &= last;
-		dat = FB_READL(dst);
-		FB_WRITEL(comp(dat ^ val, dat, first), dst);
-	} else {
-		// Multiple destination words
-		// Leading bits
-		if (first!=0UL) {
-			dat = FB_READL(dst);
-			FB_WRITEL(comp(dat ^ val, dat, first), dst);
-			dst++;
-			n -= bits - dst_idx;
-		}
-
-		// Main chunk
-		n /= bits;
-		while (n >= 8) {
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-			n -= 8;
-		}
-		while (n--) {
-			FB_WRITEL(FB_READL(dst) ^ val, dst);
-			dst++;
-		}
-		// Trailing bits
-		if (last) {
-			dat = FB_READL(dst);
-			FB_WRITEL(comp(dat ^ val, dat, last), dst);
-		}
-	}
-}
-
-
-    /*
-     *  Unaligned generic pattern invert using 32/64-bit memory accesses
-     *  The pattern must have been expanded to a full 32/64-bit value
-     *  Left/right are the appropriate shifts to convert to the pattern to be
-     *  used for the next 32/64-bit word
-     */
-
-static void
-bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst,
-		      int dst_idx, unsigned long pat, int left, int right,
-		      unsigned n, int bits)
-{
-	unsigned long first, last, dat;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		// Single word
-		if (last)
-			first &= last;
-		dat = FB_READL(dst);
-		FB_WRITEL(comp(dat ^ pat, dat, first), dst);
-	} else {
-		// Multiple destination words
-
-		// Leading bits
-		if (first != 0UL) {
-			dat = FB_READL(dst);
-			FB_WRITEL(comp(dat ^ pat, dat, first), dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			n -= bits - dst_idx;
-		}
-
-		// Main chunk
-		n /= bits;
-		while (n >= 4) {
-			FB_WRITEL(FB_READL(dst) ^ pat, dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			FB_WRITEL(FB_READL(dst) ^ pat, dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			FB_WRITEL(FB_READL(dst) ^ pat, dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			FB_WRITEL(FB_READL(dst) ^ pat, dst);
-			dst++;
-			pat = pat << left | pat >> right;
-			n -= 4;
-		}
-		while (n--) {
-			FB_WRITEL(FB_READL(dst) ^ pat, dst);
-			dst++;
-			pat = pat << left | pat >> right;
-		}
-
-		// Trailing bits
-		if (last) {
-			dat = FB_READL(dst);
-			FB_WRITEL(comp(dat ^ pat, dat, last), dst);
-		}
-	}
-}
+#include "cfbmem.h"
+#include "fb_fillrect.h"
 
 void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
 {
-	unsigned long pat, pat2, fg;
-	unsigned long width = rect->width, height = rect->height;
-	int bits = BITS_PER_LONG, bytes = bits >> 3;
-	u32 bpp = p->var.bits_per_pixel;
-	unsigned long __iomem *dst;
-	int dst_idx, left;
-
 	if (p->state != FBINFO_STATE_RUNNING)
 		return;
 
 	if (p->flags & FBINFO_VIRTFB)
-		fb_warn_once(p, "Framebuffer is not in I/O address space.");
-
-	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-	    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
-		fg = ((u32 *) (p->pseudo_palette))[rect->color];
-	else
-		fg = rect->color;
-
-	pat = pixel_to_pat(bpp, fg);
+		fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__);
 
-	dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
-	dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
-	dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
-	/* FIXME For now we support 1-32 bpp only */
-	left = bits % bpp;
 	if (p->fbops->fb_sync)
 		p->fbops->fb_sync(p);
-	if (!left) {
-		u32 bswapmask = fb_compute_bswapmask(p);
-		void (*fill_op32)(struct fb_info *p,
-				  unsigned long __iomem *dst, int dst_idx,
-		                  unsigned long pat, unsigned n, int bits,
-				  u32 bswapmask) = NULL;
 
-		switch (rect->rop) {
-		case ROP_XOR:
-			fill_op32 = bitfill_aligned_rev;
-			break;
-		case ROP_COPY:
-			fill_op32 = bitfill_aligned;
-			break;
-		default:
-			printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
-			fill_op32 = bitfill_aligned;
-			break;
-		}
-		while (height--) {
-			dst += dst_idx >> (ffs(bits) - 1);
-			dst_idx &= (bits - 1);
-			fill_op32(p, dst, dst_idx, pat, width*bpp, bits,
-				  bswapmask);
-			dst_idx += p->fix.line_length*8;
-		}
-	} else {
-		int right, r;
-		void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,
-				int dst_idx, unsigned long pat, int left,
-				int right, unsigned n, int bits) = NULL;
-#ifdef __LITTLE_ENDIAN
-		right = left;
-		left = bpp - right;
-#else
-		right = bpp - left;
-#endif
-		switch (rect->rop) {
-		case ROP_XOR:
-			fill_op = bitfill_unaligned_rev;
-			break;
-		case ROP_COPY:
-			fill_op = bitfill_unaligned;
-			break;
-		default:
-			printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
-			fill_op = bitfill_unaligned;
-			break;
-		}
-		while (height--) {
-			dst += dst_idx / bits;
-			dst_idx &= (bits - 1);
-			r = dst_idx % bpp;
-			/* rotate pattern to the correct start position */
-			pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
-			fill_op(p, dst, dst_idx, pat2, left, right,
-				width*bpp, bits);
-			dst_idx += p->fix.line_length*8;
-		}
-	}
+	fb_fillrect(p, rect);
 }
-
 EXPORT_SYMBOL(cfb_fillrect);
 
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("I/O memory packed pixel framebuffer area fill");
 MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c
index 7d1d2f1a6..bcec4e32c 100644
--- a/drivers/video/fbdev/core/cfbimgblt.c
+++ b/drivers/video/fbdev/core/cfbimgblt.c
@@ -1,369 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic BitBLT function for frame buffer with packed pixels of any depth.
- *
- *      Copyright (C)  June 1999 James Simmons
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- *
- * NOTES:
- *
- *    This function copys a image from system memory to video memory. The
- *  image can be a bitmap where each 0 represents the background color and
- *  each 1 represents the foreground color. Great for font handling. It can
- *  also be a color image. This is determined by image_depth. The color image
- *  must be laid out exactly in the same format as the framebuffer. Yes I know
- *  their are cards with hardware that coverts images of various depths to the
- *  framebuffer depth. But not every card has this. All images must be rounded
- *  up to the nearest byte. For example a bitmap 12 bits wide must be two
- *  bytes width.
- *
- *  Tony:
- *  Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API.  This speeds
- *  up the code significantly.
- *
- *  Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
- *  still processed a bit at a time.
- *
- *  Also need to add code to deal with cards endians that are different than
- *  the native cpu endians. I also need to deal with MSB position in the word.
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
 #include <linux/module.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
-#include "fb_draw.h"
 
-#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
-#else
-#define DPRINTK(fmt, args...)
+#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
 #endif
 
-static const u32 cfb_tab8_be[] = {
-    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
-    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
-    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
-    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
-};
-
-static const u32 cfb_tab8_le[] = {
-    0x00000000,0xff000000,0x00ff0000,0xffff0000,
-    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
-    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
-    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
-};
-
-static const u32 cfb_tab16_be[] = {
-    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
-};
-
-static const u32 cfb_tab16_le[] = {
-    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
-};
-
-static const u32 cfb_tab32[] = {
-	0x00000000, 0xffffffff
-};
-
-#define FB_WRITEL fb_writel
-#define FB_READL  fb_readl
-
-static inline void color_imageblit(const struct fb_image *image,
-				   struct fb_info *p, u8 __iomem *dst1,
-				   u32 start_index,
-				   u32 pitch_index)
-{
-	/* Draw the penguin */
-	u32 __iomem *dst, *dst2;
-	u32 color = 0, val, shift;
-	int i, n, bpp = p->var.bits_per_pixel;
-	u32 null_bits = 32 - bpp;
-	u32 *palette = (u32 *) p->pseudo_palette;
-	const u8 *src = image->data;
-	u32 bswapmask = fb_compute_bswapmask(p);
-
-	dst2 = (u32 __iomem *) dst1;
-	for (i = image->height; i--; ) {
-		n = image->width;
-		dst = (u32 __iomem *) dst1;
-		shift = 0;
-		val = 0;
-
-		if (start_index) {
-			u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
-						start_index, bswapmask);
-			val = FB_READL(dst) & start_mask;
-			shift = start_index;
-		}
-		while (n--) {
-			if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-			    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
-				color = palette[*src];
-			else
-				color = *src;
-			color <<= FB_LEFT_POS(p, bpp);
-			val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
-			if (shift >= null_bits) {
-				FB_WRITEL(val, dst++);
-
-				val = (shift == null_bits) ? 0 :
-					FB_SHIFT_LOW(p, color, 32 - shift);
-			}
-			shift += bpp;
-			shift &= (32 - 1);
-			src++;
-		}
-		if (shift) {
-			u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
-						bswapmask);
-
-			FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
-		}
-		dst1 += p->fix.line_length;
-		if (pitch_index) {
-			dst2 += p->fix.line_length;
-			dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
-
-			start_index += pitch_index;
-			start_index &= 32 - 1;
-		}
-	}
-}
-
-static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
-				  u8 __iomem *dst1, u32 fgcolor,
-				  u32 bgcolor,
-				  u32 start_index,
-				  u32 pitch_index)
-{
-	u32 shift, color = 0, bpp = p->var.bits_per_pixel;
-	u32 __iomem *dst, *dst2;
-	u32 val, pitch = p->fix.line_length;
-	u32 null_bits = 32 - bpp;
-	u32 spitch = (image->width+7)/8;
-	const u8 *src = image->data, *s;
-	u32 i, j, l;
-	u32 bswapmask = fb_compute_bswapmask(p);
-
-	dst2 = (u32 __iomem *) dst1;
-	fgcolor <<= FB_LEFT_POS(p, bpp);
-	bgcolor <<= FB_LEFT_POS(p, bpp);
-
-	for (i = image->height; i--; ) {
-		shift = val = 0;
-		l = 8;
-		j = image->width;
-		dst = (u32 __iomem *) dst1;
-		s = src;
-
-		/* write leading bits */
-		if (start_index) {
-			u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
-						start_index, bswapmask);
-			val = FB_READL(dst) & start_mask;
-			shift = start_index;
-		}
-
-		while (j--) {
-			l--;
-			color = (*s & (1 << l)) ? fgcolor : bgcolor;
-			val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
-
-			/* Did the bitshift spill bits to the next long? */
-			if (shift >= null_bits) {
-				FB_WRITEL(val, dst++);
-				val = (shift == null_bits) ? 0 :
-					FB_SHIFT_LOW(p, color, 32 - shift);
-			}
-			shift += bpp;
-			shift &= (32 - 1);
-			if (!l) { l = 8; s++; }
-		}
-
-		/* write trailing bits */
- 		if (shift) {
-			u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
-						bswapmask);
-
-			FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
-		}
-
-		dst1 += pitch;
-		src += spitch;
-		if (pitch_index) {
-			dst2 += pitch;
-			dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
-			start_index += pitch_index;
-			start_index &= 32 - 1;
-		}
-
-	}
-}
-
-/*
- * fast_imageblit - optimized monochrome color expansion
- *
- * Only if:  bits_per_pixel == 8, 16, or 32
- *           image->width is divisible by pixel/dword (ppw);
- *           fix->line_legth is divisible by 4;
- *           beginning and end of a scanline is dword aligned
- */
-static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
-				  u8 __iomem *dst1, u32 fgcolor,
-				  u32 bgcolor)
-{
-	u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
-	u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
-	u32 bit_mask, eorx, shift;
-	const char *s = image->data, *src;
-	u32 __iomem *dst;
-	const u32 *tab = NULL;
-	size_t tablen;
-	u32 colortab[16];
-	int i, j, k;
-
-	switch (bpp) {
-	case 8:
-		tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
-		tablen = 16;
-		break;
-	case 16:
-		tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
-		tablen = 4;
-		break;
-	case 32:
-		tab = cfb_tab32;
-		tablen = 2;
-		break;
-	default:
-		return;
-	}
-
-	for (i = ppw-1; i--; ) {
-		fgx <<= bpp;
-		bgx <<= bpp;
-		fgx |= fgcolor;
-		bgx |= bgcolor;
-	}
-
-	bit_mask = (1 << ppw) - 1;
-	eorx = fgx ^ bgx;
-	k = image->width/ppw;
-
-	for (i = 0; i < tablen; ++i)
-		colortab[i] = (tab[i] & eorx) ^ bgx;
-
-	for (i = image->height; i--; ) {
-		dst = (u32 __iomem *)dst1;
-		shift = 8;
-		src = s;
-
-		/*
-		 * Manually unroll the per-line copying loop for better
-		 * performance. This works until we processed the last
-		 * completely filled source byte (inclusive).
-		 */
-		switch (ppw) {
-		case 4: /* 8 bpp */
-			for (j = k; j >= 2; j -= 2, ++src) {
-				FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
-			}
-			break;
-		case 2: /* 16 bpp */
-			for (j = k; j >= 4; j -= 4, ++src) {
-				FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
-			}
-			break;
-		case 1: /* 32 bpp */
-			for (j = k; j >= 8; j -= 8, ++src) {
-				FB_WRITEL(colortab[(*src >> 7) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 5) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 3) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 1) & bit_mask], dst++);
-				FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
-			}
-			break;
-		}
-
-		/*
-		 * For image widths that are not a multiple of 8, there
-		 * are trailing pixels left on the current line. Print
-		 * them as well.
-		 */
-		for (; j--; ) {
-			shift -= ppw;
-			FB_WRITEL(colortab[(*src >> shift) & bit_mask], dst++);
-			if (!shift) {
-				shift = 8;
-				++src;
-			}
-		}
-
-		dst1 += p->fix.line_length;
-		s += spitch;
-	}
-}
+#include "cfbmem.h"
+#include "fb_imageblit.h"
 
 void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
 {
-	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
-	u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
-	u32 width = image->width;
-	u32 dx = image->dx, dy = image->dy;
-	u8 __iomem *dst1;
-
 	if (p->state != FBINFO_STATE_RUNNING)
 		return;
 
 	if (p->flags & FBINFO_VIRTFB)
-		fb_warn_once(p, "Framebuffer is not in I/O address space.");
-
-	bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
-	start_index = bitstart & (32 - 1);
-	pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
-
-	bitstart /= 8;
-	bitstart &= ~(bpl - 1);
-	dst1 = p->screen_base + bitstart;
+		fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__);
 
 	if (p->fbops->fb_sync)
 		p->fbops->fb_sync(p);
 
-	if (image->depth == 1) {
-		if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-		    p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-			fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
-			bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
-		} else {
-			fgcolor = image->fg_color;
-			bgcolor = image->bg_color;
-		}
-
-		if (32 % bpp == 0 && !start_index && !pitch_index &&
-		    ((width & (32/bpp-1)) == 0) &&
-		    bpp >= 8 && bpp <= 32)
-			fast_imageblit(image, p, dst1, fgcolor, bgcolor);
-		else
-			slow_imageblit(image, p, dst1, fgcolor, bgcolor,
-					start_index, pitch_index);
-	} else
-		color_imageblit(image, p, dst1, start_index, pitch_index);
+	fb_imageblit(p, image);
 }
-
 EXPORT_SYMBOL(cfb_imageblit);
 
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated imaging drawing");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("I/O memory packed pixel framebuffer image draw");
 MODULE_LICENSE("GPL");
-
diff --git a/drivers/video/fbdev/core/cfbmem.h b/drivers/video/fbdev/core/cfbmem.h
new file mode 100644
index 000000000..ce2f5f751
--- /dev/null
+++ b/drivers/video/fbdev/core/cfbmem.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ *	I/O memory framebuffer access for drawing routines
+ *
+ *	Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org)
+ */
+
+/* keeps track of a bit address in framebuffer memory */
+struct fb_address {
+	void __iomem *address;
+	int bits;
+};
+
+/* initialize the bit address pointer to the beginning of the frame buffer */
+static inline struct fb_address fb_address_init(struct fb_info *p)
+{
+	void __iomem *base = p->screen_base;
+	struct fb_address ptr;
+
+	ptr.address = PTR_ALIGN_DOWN(base, BITS_PER_LONG / BITS_PER_BYTE);
+	ptr.bits = (base - ptr.address) * BITS_PER_BYTE;
+	return ptr;
+}
+
+/* framebuffer write access */
+static inline void fb_write_offset(unsigned long val, int offset, const struct fb_address *dst)
+{
+#if BITS_PER_LONG == 32
+	fb_writel(val, dst->address + offset * (BITS_PER_LONG / BITS_PER_BYTE));
+#else
+	fb_writeq(val, dst->address + offset * (BITS_PER_LONG / BITS_PER_BYTE));
+#endif
+}
+
+/* framebuffer read access */
+static inline unsigned long fb_read_offset(int offset, const struct fb_address *src)
+{
+#if BITS_PER_LONG == 32
+	return fb_readl(src->address + offset * (BITS_PER_LONG / BITS_PER_BYTE));
+#else
+	return fb_readq(src->address + offset * (BITS_PER_LONG / BITS_PER_BYTE));
+#endif
+}
diff --git a/drivers/video/fbdev/core/fb_copyarea.h b/drivers/video/fbdev/core/fb_copyarea.h
new file mode 100644
index 000000000..ecd6e72ad
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_copyarea.h
@@ -0,0 +1,405 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ *  Generic bit area copy and twister engine for packed pixel framebuffers
+ *
+ *      Rewritten by:
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
+ *
+ *	Based on previous work of:
+ *	Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
+ *	Anton Vorontsov <avorontsov@ru.mvista.com>
+ *	Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *	Antonino Daplas <adaplas@hotpop.com>
+ *	Geert Uytterhoeven
+ *	and others
+ *
+ * NOTES:
+ *
+ * Handles native and foreign byte order on both endians, standard and
+ * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits,
+ * bits per pixel from 1 to the word length. Handles line lengths at byte
+ * granularity while maintaining aligned accesses.
+ *
+ * Optimized routines for word aligned copying and byte aligned copying
+ * on reverse pixel framebuffers.
+ */
+#include "fb_draw.h"
+
+/* used when no reversing is necessary */
+static inline unsigned long fb_no_reverse(unsigned long val, struct fb_reverse reverse)
+{
+	return val;
+}
+
+/* modifies the masked area in a word */
+static inline void fb_copy_offset_masked(unsigned long mask, int offset,
+					 const struct fb_address *dst,
+					 const struct fb_address *src)
+{
+	fb_modify_offset(fb_read_offset(offset, src), mask, offset, dst);
+}
+
+/* copies the whole word */
+static inline void fb_copy_offset(int offset, const struct fb_address *dst,
+				  const struct fb_address *src)
+{
+	fb_write_offset(fb_read_offset(offset, src), offset, dst);
+}
+
+/* forward aligned copy */
+static inline void fb_copy_aligned_fwd(const struct fb_address *dst,
+				       const struct fb_address *src,
+				       int end, struct fb_reverse reverse)
+{
+	unsigned long first, last;
+
+	first = fb_pixel_mask(dst->bits, reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse);
+
+	/* Same alignment for source and dest */
+	if (end <= BITS_PER_LONG) {
+		/* Single word */
+		last = last ? (last & first) : first;
+
+		/* Trailing bits */
+		if (last == ~0UL)
+			fb_copy_offset(0, dst, src);
+		else
+			fb_copy_offset_masked(last, 0, dst, src);
+	} else {
+		/* Multiple destination words */
+		int offset = first != ~0UL;
+
+		/* Leading bits */
+		if (offset)
+			fb_copy_offset_masked(first, 0, dst, src);
+
+		/* Main chunk */
+		end /= BITS_PER_LONG;
+		while (offset + 4 <= end) {
+			fb_copy_offset(offset + 0, dst, src);
+			fb_copy_offset(offset + 1, dst, src);
+			fb_copy_offset(offset + 2, dst, src);
+			fb_copy_offset(offset + 3, dst, src);
+			offset += 4;
+		}
+		while (offset < end)
+			fb_copy_offset(offset++, dst, src);
+
+		/* Trailing bits */
+		if (last)
+			fb_copy_offset_masked(last, offset, dst, src);
+	}
+}
+
+/* reverse aligned copy */
+static inline void fb_copy_aligned_rev(const struct fb_address *dst,
+				       const struct fb_address *src,
+				       int end, struct fb_reverse reverse)
+{
+	unsigned long first, last;
+
+	first = fb_pixel_mask(dst->bits, reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse);
+
+	if (end <= BITS_PER_LONG) {
+		/* Single word */
+		if (last)
+			first &= last;
+		if (first == ~0UL)
+			fb_copy_offset(0, dst, src);
+		else
+			fb_copy_offset_masked(first, 0, dst, src);
+	} else {
+		/* Multiple destination words */
+		int offset = first != ~0UL;
+
+		/* Trailing bits */
+		end /= BITS_PER_LONG;
+
+		if (last)
+			fb_copy_offset_masked(last, end, dst, src);
+
+		/* Main chunk */
+		while (end >= offset + 4) {
+			fb_copy_offset(end - 1, dst, src);
+			fb_copy_offset(end - 2, dst, src);
+			fb_copy_offset(end - 3, dst, src);
+			fb_copy_offset(end - 4, dst, src);
+			end -= 4;
+		}
+		while (end > offset)
+			fb_copy_offset(--end, dst, src);
+
+		/* Leading bits */
+		if (offset)
+			fb_copy_offset_masked(first, 0, dst, src);
+	}
+}
+
+static inline void fb_copy_aligned(struct fb_address *dst, struct fb_address *src,
+				   int width, u32 height, unsigned int bits_per_line,
+				   struct fb_reverse reverse, bool rev_copy)
+{
+	if (rev_copy)
+		while (height--) {
+			fb_copy_aligned_rev(dst, src, width + dst->bits, reverse);
+			fb_address_backward(dst, bits_per_line);
+			fb_address_backward(src, bits_per_line);
+		}
+	else
+		while (height--) {
+			fb_copy_aligned_fwd(dst, src, width + dst->bits, reverse);
+			fb_address_forward(dst, bits_per_line);
+			fb_address_forward(src, bits_per_line);
+		}
+}
+
+static __always_inline void fb_copy_fwd(const struct fb_address *dst,
+					const struct fb_address *src, int width,
+					unsigned long (*reorder)(unsigned long val,
+								 struct fb_reverse reverse),
+					struct fb_reverse reverse)
+{
+	unsigned long first, last;
+	unsigned long d0, d1;
+	int end = dst->bits + width;
+	int shift, left, right;
+
+	first = fb_pixel_mask(dst->bits, reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse);
+
+	shift = dst->bits - src->bits;
+	right = shift & (BITS_PER_LONG - 1);
+	left = -shift & (BITS_PER_LONG - 1);
+
+	if (end <= BITS_PER_LONG) {
+		/* Single destination word */
+		last = last ? (last & first) : first;
+		if (shift < 0) {
+			d0 = fb_left(reorder(fb_read_offset(-1, src), reverse), left);
+			if (src->bits + width > BITS_PER_LONG)
+				d0 |= fb_right(reorder(fb_read_offset(0, src), reverse), right);
+
+			if (last == ~0UL)
+				fb_write_offset(reorder(d0, reverse), 0, dst);
+			else
+				fb_modify_offset(reorder(d0, reverse), last, 0, dst);
+		} else {
+			d0 = fb_right(reorder(fb_read_offset(0, src), reverse), right);
+			fb_modify_offset(reorder(d0, reverse), last, 0, dst);
+		}
+	} else {
+		/* Multiple destination words */
+		int offset = first != ~0UL;
+
+		/* Leading bits */
+		if (shift < 0)
+			d0 = reorder(fb_read_offset(-1, src), reverse);
+		else
+			d0 = 0;
+
+		/* 2 source words */
+		if (offset) {
+			d1 = reorder(fb_read_offset(0, src), reverse);
+			d0 = fb_left(d0, left) | fb_right(d1, right);
+			fb_modify_offset(reorder(d0, reverse), first, 0, dst);
+			d0 = d1;
+		}
+
+		/* Main chunk */
+		end /= BITS_PER_LONG;
+		if (reorder == fb_no_reverse)
+			while (offset + 4 <= end) {
+				d1 = fb_read_offset(offset + 0, src);
+				d0 = fb_left(d0, left) | fb_right(d1, right);
+				fb_write_offset(d0, offset + 0, dst);
+				d0 = d1;
+				d1 = fb_read_offset(offset + 1, src);
+				d0 = fb_left(d0, left) | fb_right(d1, right);
+				fb_write_offset(d0, offset + 1, dst);
+				d0 = d1;
+				d1 = fb_read_offset(offset + 2, src);
+				d0 = fb_left(d0, left) | fb_right(d1, right);
+				fb_write_offset(d0, offset + 2, dst);
+				d0 = d1;
+				d1 = fb_read_offset(offset + 3, src);
+				d0 = fb_left(d0, left) | fb_right(d1, right);
+				fb_write_offset(d0, offset + 3, dst);
+				d0 = d1;
+				offset += 4;
+			}
+
+		while (offset < end) {
+			d1 = reorder(fb_read_offset(offset, src), reverse);
+			d0 = fb_left(d0, left) | fb_right(d1, right);
+			fb_write_offset(reorder(d0, reverse), offset, dst);
+			d0 = d1;
+			offset++;
+		}
+
+		/* Trailing bits */
+		if (last) {
+			d0 = fb_left(d0, left);
+			if (src->bits + width
+			    > offset * BITS_PER_LONG + ((shift < 0) ? BITS_PER_LONG : 0))
+				d0 |= fb_right(reorder(fb_read_offset(offset, src), reverse),
+					       right);
+			fb_modify_offset(reorder(d0, reverse), last, offset, dst);
+		}
+	}
+}
+
+static __always_inline void fb_copy_rev(const struct fb_address *dst,
+					const struct fb_address *src, int end,
+					unsigned long (*reorder)(unsigned long val,
+								 struct fb_reverse reverse),
+					struct fb_reverse reverse)
+{
+	unsigned long first, last;
+	unsigned long d0, d1;
+	int shift, left, right;
+
+	first = fb_pixel_mask(dst->bits, reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse);
+
+	shift = dst->bits - src->bits;
+	right = shift & (BITS_PER_LONG-1);
+	left = -shift & (BITS_PER_LONG-1);
+
+	if (end <= BITS_PER_LONG) {
+		/* Single destination word */
+		if (last)
+			first &= last;
+
+		if (shift > 0) {
+			d0 = fb_right(reorder(fb_read_offset(1, src), reverse), right);
+			if (src->bits > left)
+				d0 |= fb_left(reorder(fb_read_offset(0, src), reverse), left);
+			fb_modify_offset(reorder(d0, reverse), first, 0, dst);
+		} else {
+			d0 = fb_left(reorder(fb_read_offset(0, src), reverse), left);
+			if (src->bits + end - dst->bits > BITS_PER_LONG)
+				d0 |= fb_right(reorder(fb_read_offset(1, src), reverse), right);
+			if (first == ~0UL)
+				fb_write_offset(reorder(d0, reverse), 0, dst);
+			else
+				fb_modify_offset(reorder(d0, reverse), first, 0, dst);
+		}
+	} else {
+		/* Multiple destination words */
+		int offset = first != ~0UL;
+
+		end /= BITS_PER_LONG;
+
+		/* 2 source words */
+		if (fb_right(~0UL, right) & last)
+			d0 = fb_right(reorder(fb_read_offset(end + 1, src), reverse), right);
+		else
+			d0 = 0;
+
+		/* Trailing bits */
+		d1 = reorder(fb_read_offset(end, src), reverse);
+		if (last)
+			fb_modify_offset(reorder(fb_left(d1, left) | d0, reverse),
+					 last, end, dst);
+		d0 = d1;
+
+		/* Main chunk */
+		if (reorder == fb_no_reverse)
+			while (end >= offset + 4) {
+				d1 = fb_read_offset(end - 1, src);
+				d0 = fb_left(d1, left) | fb_right(d0, right);
+				fb_write_offset(d0, end - 1, dst);
+				d0 = d1;
+				d1 = fb_read_offset(end - 2, src);
+				d0 = fb_left(d1, left) | fb_right(d0, right);
+				fb_write_offset(d0, end - 2, dst);
+				d0 = d1;
+				d1 = fb_read_offset(end - 3, src);
+				d0 = fb_left(d1, left) | fb_right(d0, right);
+				fb_write_offset(d0, end - 3, dst);
+				d0 = d1;
+				d1 = fb_read_offset(end - 4, src);
+				d0 = fb_left(d1, left) | fb_right(d0, right);
+				fb_write_offset(d0, end - 4, dst);
+				d0 = d1;
+				end -= 4;
+			}
+
+		while (end > offset) {
+			end--;
+			d1 = reorder(fb_read_offset(end, src), reverse);
+			d0 = fb_left(d1, left) | fb_right(d0, right);
+			fb_write_offset(reorder(d0, reverse), end, dst);
+			d0 = d1;
+		}
+
+		/* Leading bits */
+		if (offset) {
+			d0 = fb_right(d0, right);
+			if (src->bits > left)
+				d0 |= fb_left(reorder(fb_read_offset(0, src), reverse), left);
+			fb_modify_offset(reorder(d0, reverse), first, 0, dst);
+		}
+	}
+}
+
+static __always_inline void fb_copy(struct fb_address *dst, struct fb_address *src,
+				    int width, u32 height, unsigned int bits_per_line,
+				    unsigned long (*reorder)(unsigned long val,
+							     struct fb_reverse reverse),
+				    struct fb_reverse reverse, bool rev_copy)
+{
+	if (rev_copy)
+		while (height--) {
+			int move = src->bits < dst->bits ? -1 : 0;
+
+			fb_address_move_long(src, move);
+			fb_copy_rev(dst, src, width + dst->bits, reorder, reverse);
+			fb_address_backward(dst, bits_per_line);
+			fb_address_backward(src, bits_per_line);
+			fb_address_move_long(src, -move);
+		}
+	else
+		while (height--) {
+			int move = src->bits > dst->bits ? 1 : 0;
+
+			fb_address_move_long(src, move);
+			fb_copy_fwd(dst, src, width, reorder, reverse);
+			fb_address_forward(dst, bits_per_line);
+			fb_address_forward(src, bits_per_line);
+			fb_address_move_long(src, -move);
+		}
+}
+
+static inline void fb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+	int bpp = p->var.bits_per_pixel;
+	u32 dy = area->dy;
+	u32 sy = area->sy;
+	u32 height = area->height;
+	int width = area->width * bpp;
+	unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
+	struct fb_reverse reverse = fb_reverse_init(p);
+	struct fb_address dst = fb_address_init(p);
+	struct fb_address src = dst;
+	bool rev_copy = (dy > sy) || (dy == sy && area->dx > area->sx);
+
+	if (rev_copy) {
+		dy += height - 1;
+		sy += height - 1;
+	}
+	fb_address_forward(&dst, dy*bits_per_line + area->dx*bpp);
+	fb_address_forward(&src, sy*bits_per_line + area->sx*bpp);
+
+	if (src.bits == dst.bits)
+		fb_copy_aligned(&dst, &src, width, height, bits_per_line, reverse, rev_copy);
+	else if (!reverse.byte && (!reverse.pixel ||
+				     !((src.bits ^ dst.bits) & (BITS_PER_BYTE-1)))) {
+		fb_copy(&dst, &src, width, height, bits_per_line,
+			fb_no_reverse, reverse, rev_copy);
+	} else
+		fb_copy(&dst, &src, width, height, bits_per_line,
+			fb_reverse_long, reverse, rev_copy);
+}
diff --git a/drivers/video/fbdev/core/fb_draw.h b/drivers/video/fbdev/core/fb_draw.h
index e0d829873..8eb13f7b3 100644
--- a/drivers/video/fbdev/core/fb_draw.h
+++ b/drivers/video/fbdev/core/fb_draw.h
@@ -1,187 +1,163 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ *  Various common functions used by the framebuffer drawing code
+ *
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
+ */
 #ifndef _FB_DRAW_H
 #define _FB_DRAW_H
 
-#include <asm/types.h>
-#include <linux/fb.h>
-#include <linux/bug.h>
+/* swap bytes in a long, independent of word size */
+#define swab_long _swab_long(BITS_PER_LONG)
+#define _swab_long(x) __swab_long(x)
+#define __swab_long(x) swab##x
 
-    /*
-     *  Compose two values, using a bitmask as decision value
-     *  This is equivalent to (a & mask) | (b & ~mask)
-     */
-
-static inline unsigned long
-comp(unsigned long a, unsigned long b, unsigned long mask)
+/* move the address pointer by the number of words */
+static inline void fb_address_move_long(struct fb_address *adr, int offset)
 {
-    return ((a ^ b) & mask) ^ b;
+	adr->address += offset * (BITS_PER_LONG / BITS_PER_BYTE);
 }
 
-    /*
-     *  Create a pattern with the given pixel's color
-     */
+/* move the address pointer forward with the number of bits */
+static inline void fb_address_forward(struct fb_address *adr, unsigned int offset)
+{
+	unsigned int bits = (unsigned int)adr->bits + offset;
+
+	adr->bits = bits & (BITS_PER_LONG - 1u);
+	adr->address += (bits & ~(BITS_PER_LONG - 1u)) / BITS_PER_BYTE;
+}
 
-#if BITS_PER_LONG == 64
-static inline unsigned long
-pixel_to_pat( u32 bpp, u32 pixel)
+/* move the address pointer backwards with the number of bits */
+static inline void fb_address_backward(struct fb_address *adr, unsigned int offset)
 {
-	switch (bpp) {
-	case 1:
-		return 0xfffffffffffffffful*pixel;
-	case 2:
-		return 0x5555555555555555ul*pixel;
-	case 4:
-		return 0x1111111111111111ul*pixel;
-	case 8:
-		return 0x0101010101010101ul*pixel;
-	case 12:
-		return 0x1001001001001001ul*pixel;
-	case 16:
-		return 0x0001000100010001ul*pixel;
-	case 24:
-		return 0x0001000001000001ul*pixel;
-	case 32:
-		return 0x0000000100000001ul*pixel;
-	default:
-		WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
-		return 0;
-    }
+	int bits = adr->bits - (int)offset;
+
+	adr->bits = bits & (BITS_PER_LONG - 1);
+	if (bits < 0)
+		adr->address -= (adr->bits - bits) / BITS_PER_BYTE;
+	else
+		adr->address += (bits - adr->bits) / BITS_PER_BYTE;
 }
-#else
-static inline unsigned long
-pixel_to_pat( u32 bpp, u32 pixel)
+
+/* compose pixels based on mask */
+static inline unsigned long fb_comp(unsigned long set, unsigned long unset, unsigned long mask)
 {
-	switch (bpp) {
-	case 1:
-		return 0xfffffffful*pixel;
-	case 2:
-		return 0x55555555ul*pixel;
-	case 4:
-		return 0x11111111ul*pixel;
-	case 8:
-		return 0x01010101ul*pixel;
-	case 12:
-		return 0x01001001ul*pixel;
-	case 16:
-		return 0x00010001ul*pixel;
-	case 24:
-		return 0x01000001ul*pixel;
-	case 32:
-		return 0x00000001ul*pixel;
-	default:
-		WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
-		return 0;
-    }
+	return ((set ^ unset) & mask) ^ unset;
 }
-#endif
 
-#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
-#if BITS_PER_LONG == 64
-#define REV_PIXELS_MASK1 0x5555555555555555ul
-#define REV_PIXELS_MASK2 0x3333333333333333ul
-#define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful
-#else
-#define REV_PIXELS_MASK1 0x55555555ul
-#define REV_PIXELS_MASK2 0x33333333ul
-#define REV_PIXELS_MASK4 0x0f0f0f0ful
-#endif
+/* framebuffer read-modify-write access for replacing bits in the mask */
+static inline void fb_modify_offset(unsigned long val, unsigned long mask,
+				    int offset, const struct fb_address *dst)
+{
+	fb_write_offset(fb_comp(val, fb_read_offset(offset, dst), mask), offset, dst);
+}
 
-static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
-						  u32 bswapmask)
+/*
+ * get current palette, if applicable for visual
+ *
+ * The pseudo color table entries (and colors) are right justified and in the
+ * same byte order as it's expected to be placed into a native ordered
+ * framebuffer memory. What that means:
+ *
+ * Expected bytes in framebuffer memory (in native order):
+ * RR GG BB RR GG BB RR GG BB ...
+ *
+ * Pseudo palette entry on little endian arch:
+ * RR | GG << 8 | BB << 16
+ *
+ * Pseudo palette entry on a big endian arch:
+ * RR << 16 | GG << 8 | BB
+ */
+static inline const u32 *fb_palette(struct fb_info *info)
 {
-	if (bswapmask & 1)
-		val = comp(val >> 1, val << 1, REV_PIXELS_MASK1);
-	if (bswapmask & 2)
-		val = comp(val >> 2, val << 2, REV_PIXELS_MASK2);
-	if (bswapmask & 3)
-		val = comp(val >> 4, val << 4, REV_PIXELS_MASK4);
-	return val;
+	return (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+		info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? info->pseudo_palette : NULL;
 }
 
-static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index,
-					     u32 bswapmask)
+/* move pixels right on screen when framebuffer is in native order */
+static inline unsigned long fb_right(unsigned long value, int index)
 {
-	u32 mask;
-
-	if (!bswapmask) {
-		mask = FB_SHIFT_HIGH(p, ~(u32)0, index);
-	} else {
-		mask = 0xff << FB_LEFT_POS(p, 8);
-		mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
-		mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
-#if defined(__i386__) || defined(__x86_64__)
-		/* Shift argument is limited to 0 - 31 on x86 based CPU's */
-		if(index + bswapmask < 32)
+#ifdef __LITTLE_ENDIAN
+	return value << index;
+#else
+	return value >> index;
 #endif
-			mask |= FB_SHIFT_HIGH(p, ~(u32)0,
-					(index + bswapmask) & ~(bswapmask));
-	}
-	return mask;
 }
 
-static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p,
-							u32 index,
-							u32 bswapmask)
+/* move pixels left on screen when framebuffer is in native order */
+static inline unsigned long fb_left(unsigned long value, int index)
 {
-	unsigned long mask;
-
-	if (!bswapmask) {
-		mask = FB_SHIFT_HIGH(p, ~0UL, index);
-	} else {
-		mask = 0xff << FB_LEFT_POS(p, 8);
-		mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
-		mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
-#if defined(__i386__) || defined(__x86_64__)
-		/* Shift argument is limited to 0 - 31 on x86 based CPU's */
-		if(index + bswapmask < BITS_PER_LONG)
+#ifdef __LITTLE_ENDIAN
+	return value >> index;
+#else
+	return value << index;
 #endif
-			mask |= FB_SHIFT_HIGH(p, ~0UL,
-					(index + bswapmask) & ~(bswapmask));
-	}
-	return mask;
 }
 
+/* reversal options */
+struct fb_reverse {
+	bool byte, pixel;
+};
 
-static inline u32 fb_compute_bswapmask(struct fb_info *info)
+/* reverse bits of each byte in a long */
+static inline unsigned long fb_reverse_bits_long(unsigned long val)
 {
-	u32 bswapmask = 0;
-	unsigned bpp = info->var.bits_per_pixel;
-
-	if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) {
-		/*
-		 * Reversed order of pixel layout in bytes
-		 * works only for 1, 2 and 4 bpp
-		 */
-		bswapmask = 7 - bpp + 1;
-	}
-	return bswapmask;
+#if defined(CONFIG_HAVE_ARCH_BITREVERSE) && BITS_PER_LONG == 32
+	return bitrev8x4(val);
+#else
+	val = fb_comp(val >> 1, val << 1, ~0UL / 3);
+	val = fb_comp(val >> 2, val << 2, ~0UL / 5);
+	return fb_comp(val >> 4, val << 4, ~0UL / 17);
+#endif
 }
 
-#else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
-
-static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
-						  u32 bswapmask)
+/* apply byte and bit reversals as necessary */
+static inline unsigned long fb_reverse_long(unsigned long val,
+					    struct fb_reverse reverse)
 {
-	return val;
+	if (reverse.pixel)
+		val = fb_reverse_bits_long(val);
+	return reverse.byte ? swab_long(val) : val;
 }
 
-#define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i))
-#define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i))
-#define fb_compute_bswapmask(...) 0
-
-#endif  /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
-
-#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
-#define _cpu_to_le_long(x) __cpu_to_le_long(x)
-#define __cpu_to_le_long(x) cpu_to_le##x
+/* calculate a pixel mask for the given reversal */
+static inline unsigned long fb_pixel_mask(int index, struct fb_reverse reverse)
+{
+#ifdef FB_REV_PIXELS_IN_BYTE
+	if (reverse.byte)
+		return reverse.pixel ? fb_left(~0UL, index) : swab_long(fb_right(~0UL, index));
+	else
+		return reverse.pixel ? swab_long(fb_left(~0UL, index)) : fb_right(~0UL, index);
+#else
+	return reverse.byte ? swab_long(fb_right(~0UL, index)) : fb_right(~0UL, index);
+#endif
+}
 
-#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
-#define _le_long_to_cpu(x) __le_long_to_cpu(x)
-#define __le_long_to_cpu(x) le##x##_to_cpu
 
-static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
+/*
+ * initialise reversals based on info
+ *
+ * Normally the first byte is the low byte on little endian and in the high
+ * on big endian. If it's the other way around then that's reverse byte order.
+ *
+ * Normally the first pixel is the LSB on little endian and the MSB on big
+ * endian. If that's not the case that's reverse pixel order.
+ */
+static inline struct fb_reverse fb_reverse_init(struct fb_info *info)
 {
-	return (word << shift) | (word >> (x - shift));
+	struct fb_reverse reverse;
+#ifdef __LITTLE_ENDIAN
+	reverse.byte = fb_be_math(info) != 0;
+#else
+	reverse.byte = fb_be_math(info) == 0;
+#endif
+#ifdef FB_REV_PIXELS_IN_BYTE
+	reverse.pixel = info->var.bits_per_pixel < BITS_PER_BYTE
+		&& (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B);
+#else
+	reverse.pixel = false;
+#endif
+	return reverse;
 }
 
 #endif /* FB_DRAW_H */
diff --git a/drivers/video/fbdev/core/fb_fillrect.h b/drivers/video/fbdev/core/fb_fillrect.h
new file mode 100644
index 000000000..66042e534
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_fillrect.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ *  Generic bit area filler and twister engine for packed pixel framebuffers
+ *
+ *	Rewritten by:
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
+ *
+ *	Based on earlier work of:
+ *	Copyright (C)  2000 James Simmons (jsimmons@linux-fbdev.org)
+ *	Michal Januszewski <spock@gentoo.org>
+ *	Anton Vorontsov <avorontsov@ru.mvista.com>
+ *	Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *	Antonino A. Daplas <adaplas@gmail.com>
+ *	Geert Uytterhoeven
+ *	and others
+ *
+ * NOTES:
+ *
+ * Handles native and foreign byte order on both endians, standard and
+ * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits,
+ * bits per pixel from 1 to the word length. Handles line lengths at byte
+ * granularity while maintaining aligned accesses.
+ *
+ * Optimized path for power of two bits per pixel modes.
+ */
+#include "fb_draw.h"
+
+/* inverts bits at a given offset */
+static inline void fb_invert_offset(unsigned long pat, int offset, const struct fb_address *dst)
+{
+	fb_write_offset(fb_read_offset(offset, dst) ^ pat, offset, dst);
+}
+
+/* state for pattern generator and whether swapping is necessary */
+struct fb_pattern {
+	unsigned long pixels;
+	int left, right;
+	struct fb_reverse reverse;
+};
+
+/* used to get the pattern in native order */
+static unsigned long fb_pattern_get(struct fb_pattern *pattern)
+{
+	return pattern->pixels;
+}
+
+/* used to get the pattern in reverse order */
+static unsigned long fb_pattern_get_reverse(struct fb_pattern *pattern)
+{
+	return swab_long(pattern->pixels);
+}
+
+/* next static pattern */
+static void fb_pattern_static(struct fb_pattern *pattern)
+{
+	/* nothing to do */
+}
+
+/* next rotating pattern */
+static void fb_pattern_rotate(struct fb_pattern *pattern)
+{
+	pattern->pixels = fb_left(pattern->pixels, pattern->left)
+		| fb_right(pattern->pixels, pattern->right);
+}
+
+#define FB_PAT(i) (((1UL<<(BITS_PER_LONG-1)/(i)*(i))/((1<<(i))-1)<<(i))|1)
+
+/* create the filling pattern from a given color */
+static unsigned long pixel_to_pat(int bpp, u32 color)
+{
+	static const unsigned long mulconst[BITS_PER_LONG/4] = {
+		0, ~0UL, FB_PAT(2), FB_PAT(3),
+		FB_PAT(4), FB_PAT(5), FB_PAT(6), FB_PAT(7),
+#if BITS_PER_LONG == 64
+		FB_PAT(8), FB_PAT(9), FB_PAT(10), FB_PAT(11),
+		FB_PAT(12), FB_PAT(13), FB_PAT(14), FB_PAT(15),
+#endif
+	};
+	unsigned long pattern;
+
+	switch (bpp) {
+	case 0 ... BITS_PER_LONG/4-1:
+		pattern = mulconst[bpp] * color;
+		break;
+	case BITS_PER_LONG/4 ... BITS_PER_LONG/2-1:
+		pattern = color;
+		pattern = pattern | pattern << bpp;
+		pattern = pattern | pattern << bpp*2;
+		break;
+	case BITS_PER_LONG/2 ... BITS_PER_LONG-1:
+		pattern = color;
+		pattern = pattern | pattern << bpp;
+		break;
+	default:
+		pattern = color;
+		break;
+	}
+#ifndef __LITTLE_ENDIAN
+	pattern <<= (BITS_PER_LONG % bpp);
+	pattern |= pattern >> bpp;
+#endif
+	return pattern;
+}
+
+/* overwrite bits according to a pattern in a line */
+static __always_inline void bitfill(const struct fb_address *dst,
+				    struct fb_pattern *pattern,
+				    unsigned long (*get)(struct fb_pattern *pattern),
+				    void (*rotate)(struct fb_pattern *pattern),
+				    int end)
+{
+	unsigned long first, last;
+
+	end += dst->bits;
+	first = fb_pixel_mask(dst->bits, pattern->reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
+
+	if (end <= BITS_PER_LONG) {
+		last = last ? (last & first) : first;
+		first = get(pattern);
+		if (last == ~0UL)
+			fb_write_offset(first, 0, dst);
+		else if (last)
+			fb_modify_offset(first, last, 0, dst);
+	} else {
+		int offset = first != ~0UL;
+
+		if (offset) {
+			fb_modify_offset(get(pattern), first, 0, dst);
+			rotate(pattern);
+		}
+		end /= BITS_PER_LONG;
+		for (; offset + 4 <= end; offset += 4) {
+			fb_write_offset(get(pattern), offset + 0, dst);
+			rotate(pattern);
+			fb_write_offset(get(pattern), offset + 1, dst);
+			rotate(pattern);
+			fb_write_offset(get(pattern), offset + 2, dst);
+			rotate(pattern);
+			fb_write_offset(get(pattern), offset + 3, dst);
+			rotate(pattern);
+		}
+		while (offset < end) {
+			fb_write_offset(get(pattern), offset++, dst);
+			rotate(pattern);
+		}
+
+		if (last)
+			fb_modify_offset(get(pattern), last, offset, dst);
+	}
+}
+
+/* inverts bits according to a pattern in a line */
+static __always_inline void bitinvert(const struct fb_address *dst,
+				      struct fb_pattern *pattern,
+				      unsigned long (*get)(struct fb_pattern *pattern),
+				      void (*rotate)(struct fb_pattern *pattern),
+				      int end)
+{
+	unsigned long first, last;
+	int offset;
+
+	end += dst->bits;
+	first = fb_pixel_mask(dst->bits, pattern->reverse);
+	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
+
+	if (end <= BITS_PER_LONG) {
+		offset = 0;
+		last = last ? (last & first) : first;
+	} else {
+		offset = first != ~0UL;
+
+		if (offset) {
+			first &= get(pattern);
+			if (first)
+				fb_invert_offset(first, 0, dst);
+			rotate(pattern);
+		}
+
+		end /= BITS_PER_LONG;
+		for (; offset + 4 <= end; offset += 4) {
+			fb_invert_offset(get(pattern), offset + 0, dst);
+			rotate(pattern);
+			fb_invert_offset(get(pattern), offset + 1, dst);
+			rotate(pattern);
+			fb_invert_offset(get(pattern), offset + 2, dst);
+			rotate(pattern);
+			fb_invert_offset(get(pattern), offset + 3, dst);
+			rotate(pattern);
+		}
+		while (offset < end) {
+			fb_invert_offset(get(pattern), offset++, dst);
+			rotate(pattern);
+		}
+	}
+
+	last &= get(pattern);
+	if (last)
+		fb_invert_offset(last, offset, dst);
+}
+
+/* pattern doesn't change. 1, 2, 4, 8, 16, 32, 64 bpp */
+static inline void fb_fillrect_static(const struct fb_fillrect *rect, int bpp,
+				      struct fb_address *dst, struct fb_pattern *pattern,
+				      unsigned int bits_per_line)
+{
+	u32 height = rect->height;
+	int width = rect->width * bpp;
+
+	if (bpp > 8 && pattern->reverse.byte)
+		pattern->pixels = swab_long(pattern->pixels);
+
+	if (rect->rop == ROP_XOR)
+		while (height--) {
+			bitinvert(dst, pattern, fb_pattern_get, fb_pattern_static, width);
+			fb_address_forward(dst, bits_per_line);
+		}
+	else
+		while (height--) {
+			bitfill(dst, pattern, fb_pattern_get, fb_pattern_static, width);
+			fb_address_forward(dst, bits_per_line);
+		}
+}
+
+/* rotate pattern to the correct position */
+static inline unsigned long fb_rotate(unsigned long pattern, int shift, int bpp)
+{
+	shift %= bpp;
+	return fb_right(pattern, shift) | fb_left(pattern, bpp - shift);
+}
+
+/* rotating pattern, for example 24 bpp */
+static __always_inline void fb_fillrect_rotating(const struct fb_fillrect *rect,
+						 int bpp, struct fb_address *dst,
+						 struct fb_pattern *pattern,
+						 unsigned long (*get)(struct fb_pattern *pattern),
+						 unsigned int bits_per_line)
+{
+	unsigned long pat = pattern->pixels;
+	u32 height = rect->height;
+	int width = rect->width * bpp;
+
+	if (rect->rop == ROP_XOR)
+		while (height--) {
+			pattern->pixels = fb_rotate(pat, dst->bits, bpp);
+			bitinvert(dst, pattern, get, fb_pattern_rotate, width);
+			fb_address_forward(dst, bits_per_line);
+		}
+	else
+		while (height--) {
+			pattern->pixels = fb_rotate(pat, dst->bits, bpp);
+			bitfill(dst, pattern, get, fb_pattern_rotate, width);
+			fb_address_forward(dst, bits_per_line);
+		}
+}
+
+static inline void fb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+	int bpp = p->var.bits_per_pixel;
+	unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
+	const u32 *palette = fb_palette(p);
+	struct fb_address dst = fb_address_init(p);
+	struct fb_pattern pattern;
+
+	fb_address_forward(&dst, rect->dy * bits_per_line + rect->dx * bpp);
+
+	pattern.pixels = pixel_to_pat(bpp, palette ? palette[rect->color] : rect->color);
+	pattern.reverse = fb_reverse_init(p);
+	pattern.left = BITS_PER_LONG % bpp;
+	if (pattern.left) {
+		pattern.right = bpp - pattern.left;
+		if (pattern.reverse.byte)
+			fb_fillrect_rotating(rect, bpp, &dst, &pattern,
+					     fb_pattern_get_reverse, bits_per_line);
+		else
+			fb_fillrect_rotating(rect, bpp, &dst, &pattern,
+					     fb_pattern_get, bits_per_line);
+	} else
+		fb_fillrect_static(rect, bpp, &dst, &pattern, bits_per_line);
+}
diff --git a/drivers/video/fbdev/core/fb_imageblit.h b/drivers/video/fbdev/core/fb_imageblit.h
new file mode 100644
index 000000000..3b2bb4946
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_imageblit.h
@@ -0,0 +1,495 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ *  Generic bitmap / 8 bpp image bitstreamer for packed pixel framebuffers
+ *
+ *	Rewritten by:
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
+ *
+ *	Based on previous work of:
+ *	Copyright (C)  June 1999 James Simmons
+ *	Anton Vorontsov <avorontsov@ru.mvista.com>
+ *	Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *	Antonino A. Daplas <adaplas@gmail.com>
+ *	and others
+ *
+ * NOTES:
+ *
+ * Handles native and foreign byte order on both endians, standard and
+ * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits,
+ * bits per pixel from 1 to the word length. Handles line lengths at byte
+ * granularity while maintaining aligned accesses.
+ *
+ * Optimized routines for word aligned 1, 2, 4 pixel per word for high
+ * bpp modes and 4 pixel at a time operation for low bpp.
+ *
+ * The color image is expected to be one byte per pixel, and values should
+ * not exceed the bitdepth or the pseudo palette (if used).
+ */
+#include "fb_draw.h"
+
+/* bitmap image iterator, one pixel at a time */
+struct fb_bitmap_iter {
+	const u8 *data;
+	unsigned long colors[2];
+	int width, i;
+};
+
+static bool fb_bitmap_image(void *iterator, unsigned long *pixels, int *bits)
+{
+	struct fb_bitmap_iter *iter = iterator;
+
+	if (iter->i < iter->width) {
+		int bit = ~iter->i & (BITS_PER_BYTE-1);
+		int byte = iter->i++ / BITS_PER_BYTE;
+
+		*pixels = iter->colors[(iter->data[byte] >> bit) & 1];
+		return true;
+	}
+	iter->data += BITS_TO_BYTES(iter->width);
+	iter->i = 0;
+	return false;
+}
+
+/* color image iterator, one pixel at a time */
+struct fb_color_iter {
+	const u8 *data;
+	const u32 *palette;
+	struct fb_reverse reverse;
+	int shift;
+	int width, i;
+};
+
+static bool fb_color_image(void *iterator, unsigned long *pixels, int *bits)
+{
+	struct fb_color_iter *iter = iterator;
+
+	if (iter->i < iter->width) {
+		unsigned long color = iter->data[iter->i++];
+
+		if (iter->palette)
+			color = iter->palette[color];
+		*pixels = color << iter->shift;
+		if (iter->reverse.pixel)
+			*pixels = fb_reverse_bits_long(*pixels);
+		return true;
+	}
+	iter->data += iter->width;
+	iter->i = 0;
+	return false;
+}
+
+/* bitmap image iterator, 4 pixels at a time */
+struct fb_bitmap4x_iter {
+	const u8 *data;
+	u32 fgxcolor, bgcolor;
+	int width, i;
+	const u32 *expand;
+	int bpp;
+	bool top;
+};
+
+static bool fb_bitmap4x_image(void *iterator, unsigned long *pixels, int *bits)
+{
+	struct fb_bitmap4x_iter *iter = iterator;
+	u8 data;
+
+	if (iter->i >= BITS_PER_BYTE/2) {
+		iter->i -= BITS_PER_BYTE/2;
+		iter->top = !iter->top;
+		if (iter->top)
+			data = *iter->data++ >> BITS_PER_BYTE/2;
+		else
+			data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1);
+	} else if (iter->i != 0) {
+		*bits = iter->bpp * iter->i;
+		if (iter->top)
+			data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1);
+		else
+			data = *iter->data++ >> BITS_PER_BYTE/2;
+#ifndef __LITTLE_ENDIAN
+		data >>= BITS_PER_BYTE/2 - iter->i;
+#endif
+		iter->i = 0;
+	} else {
+		*bits = iter->bpp * BITS_PER_BYTE/2;
+		iter->i = iter->width;
+		iter->top = false;
+		return false;
+	}
+	*pixels = (iter->fgxcolor & iter->expand[data]) ^ iter->bgcolor;
+#ifndef __LITTLE_ENDIAN
+	*pixels <<= BITS_PER_LONG - *bits;
+#endif
+	return true;
+}
+
+/* draw a line a group of pixels at a time */
+static __always_inline void fb_bitblit(bool (*get)(void *iter, unsigned long *pixels,
+						   int *bits),
+				       void *iter, int bits, struct fb_address *dst,
+				       struct fb_reverse reverse)
+{
+	unsigned long pixels, val, mask, old;
+	int offset = 0;
+	int shift = dst->bits;
+
+	if (shift) {
+		old = fb_read_offset(0, dst);
+		val = fb_reverse_long(old, reverse);
+		val &= ~fb_right(~0UL, shift);
+	} else {
+		old = 0;
+		val = 0;
+	}
+
+	while (get(iter, &pixels, &bits)) {
+		val |= fb_right(pixels, shift);
+		shift += bits;
+
+		if (shift < BITS_PER_LONG)
+			continue;
+
+		val = fb_reverse_long(val, reverse);
+		fb_write_offset(val, offset++, dst);
+		shift &= BITS_PER_LONG - 1;
+		val = !shift ? 0 : fb_left(pixels, bits - shift);
+	}
+
+	if (shift) {
+		mask = ~fb_pixel_mask(shift, reverse);
+		val = fb_reverse_long(val, reverse);
+		if (offset || !dst->bits)
+			old = fb_read_offset(offset, dst);
+		fb_write_offset(fb_comp(val, old, mask), offset, dst);
+	}
+}
+
+/* draw a color image a pixel at a time */
+static inline void fb_color_imageblit(const struct fb_image *image, struct fb_address *dst,
+				      unsigned int bits_per_line, const u32 *palette, int bpp,
+				      struct fb_reverse reverse)
+{
+	struct fb_color_iter iter;
+	u32 height;
+
+	iter.data = (const u8 *)image->data;
+	iter.palette = palette;
+	iter.reverse = reverse;
+#ifdef __LITTLE_ENDIAN
+	if (reverse.pixel)
+		iter.shift = BITS_PER_BYTE - bpp;
+	else
+		iter.shift = 0;
+#else
+	if (reverse.pixel)
+		iter.shift = BITS_PER_LONG - BITS_PER_BYTE;
+	else
+		iter.shift = BITS_PER_LONG - bpp;
+#endif
+	iter.width = image->width;
+	iter.i = 0;
+
+	height = image->height;
+	while (height--) {
+		fb_bitblit(fb_color_image, &iter, bpp, dst, reverse);
+		fb_address_forward(dst, bits_per_line);
+	}
+}
+
+#ifdef __LITTLE_ENDIAN
+#define FB_GEN(a, b) (((a)/8+(((a)&4)<<((b)-2)) +		       +(((a)&2)<<((b)*2-1))+(((a)&1u)<<((b)*3)))*((1<<(b))-1))
+#define FB_GEN1(a) ((a)/8+((a)&4)/2+((a)&2)*2+((a)&1)*8)
+#else
+#define FB_GEN(a, b) (((((a)/8)<<((b)*3))+(((a)&4)<<((b)*2-2)) +		       +(((a)&2)<<(b-1))+((a)&1u))*((1<<(b))-1))
+#define FB_GEN1(a) (a)
+#endif
+
+#define FB_GENx(a) { FB_GEN(0, (a)), FB_GEN(1, (a)), FB_GEN(2, (a)), FB_GEN(3, (a)),	+	FB_GEN(4, (a)), FB_GEN(5, (a)), FB_GEN(6, (a)), FB_GEN(7, (a)),			+	FB_GEN(8, (a)), FB_GEN(9, (a)), FB_GEN(10, (a)), FB_GEN(11, (a)),		+	FB_GEN(12, (a)), FB_GEN(13, (a)), FB_GEN(14, (a)), FB_GEN(15, (a)) }
+
+/* draw a 2 color image four pixels at a time (for 1-8 bpp only) */
+static inline void fb_bitmap4x_imageblit(const struct fb_image *image, struct fb_address *dst,
+					 unsigned long fgcolor, unsigned long bgcolor, int bpp,
+					 unsigned int bits_per_line, struct fb_reverse reverse)
+{
+	static const u32 mul[BITS_PER_BYTE] = {
+		0xf, 0x55, 0x249, 0x1111, 0x8421, 0x41041, 0x204081, 0x01010101
+	};
+	static const u32 expand[BITS_PER_BYTE][1 << 4] = {
+		{
+			FB_GEN1(0), FB_GEN1(1), FB_GEN1(2), FB_GEN1(3),
+			FB_GEN1(4), FB_GEN1(5), FB_GEN1(6), FB_GEN1(7),
+			FB_GEN1(8), FB_GEN1(9), FB_GEN1(10), FB_GEN1(11),
+			FB_GEN1(12), FB_GEN1(13), FB_GEN1(14), FB_GEN1(15)
+		},
+		FB_GENx(2), FB_GENx(3), FB_GENx(4),
+		FB_GENx(5), FB_GENx(6), FB_GENx(7), FB_GENx(8),
+	};
+	struct fb_bitmap4x_iter iter;
+	u32 height;
+
+	iter.data = (const u8 *)image->data;
+	if (reverse.pixel) {
+		fgcolor = fb_reverse_bits_long(fgcolor << (BITS_PER_BYTE - bpp));
+		bgcolor = fb_reverse_bits_long(bgcolor << (BITS_PER_BYTE - bpp));
+	}
+	iter.fgxcolor = (fgcolor ^ bgcolor) * mul[bpp-1];
+	iter.bgcolor = bgcolor * mul[bpp-1];
+	iter.width = image->width;
+	iter.i = image->width;
+	iter.expand = expand[bpp-1];
+	iter.bpp = bpp;
+	iter.top = false;
+
+	height = image->height;
+	while (height--) {
+		fb_bitblit(fb_bitmap4x_image, &iter, bpp * BITS_PER_BYTE/2, dst, reverse);
+		fb_address_forward(dst, bits_per_line);
+	}
+}
+
+/* draw a bitmap image 1 pixel at a time (for >8 bpp) */
+static inline void fb_bitmap1x_imageblit(const struct fb_image *image, struct fb_address *dst,
+					 unsigned long fgcolor, unsigned long bgcolor, int bpp,
+					 unsigned int bits_per_line, struct fb_reverse reverse)
+{
+	struct fb_bitmap_iter iter;
+	u32 height;
+
+	iter.colors[0] = bgcolor;
+	iter.colors[1] = fgcolor;
+#ifndef __LITTLE_ENDIAN
+	iter.colors[0] <<= BITS_PER_LONG - bpp;
+	iter.colors[1] <<= BITS_PER_LONG - bpp;
+#endif
+	iter.data = (const u8 *)image->data;
+	iter.width = image->width;
+	iter.i = 0;
+
+	height = image->height;
+	while (height--) {
+		fb_bitblit(fb_bitmap_image, &iter, bpp, dst, reverse);
+		fb_address_forward(dst, bits_per_line);
+	}
+}
+
+/* one pixel per word, 64/32 bpp blitting */
+static inline void fb_bitmap_1ppw(const struct fb_image *image, struct fb_address *dst,
+				  unsigned long fgcolor, unsigned long bgcolor,
+				  int words_per_line, struct fb_reverse reverse)
+{
+	unsigned long tab[2];
+	const u8 *src = (u8 *)image->data;
+	int width = image->width;
+	int offset;
+	u32 height;
+
+	if (reverse.byte) {
+		tab[0] = swab_long(bgcolor);
+		tab[1] = swab_long(fgcolor);
+	} else {
+		tab[0] = bgcolor;
+		tab[1] = fgcolor;
+	}
+
+	height = image->height;
+	while (height--) {
+		for (offset = 0; offset + 8 <= width; offset += 8) {
+			unsigned int srcbyte = *src++;
+
+			fb_write_offset(tab[(srcbyte >> 7) & 1], offset + 0, dst);
+			fb_write_offset(tab[(srcbyte >> 6) & 1], offset + 1, dst);
+			fb_write_offset(tab[(srcbyte >> 5) & 1], offset + 2, dst);
+			fb_write_offset(tab[(srcbyte >> 4) & 1], offset + 3, dst);
+			fb_write_offset(tab[(srcbyte >> 3) & 1], offset + 4, dst);
+			fb_write_offset(tab[(srcbyte >> 2) & 1], offset + 5, dst);
+			fb_write_offset(tab[(srcbyte >> 1) & 1], offset + 6, dst);
+			fb_write_offset(tab[(srcbyte >> 0) & 1], offset + 7, dst);
+		}
+
+		if (offset < width) {
+			unsigned int srcbyte = *src++;
+
+			while (offset < width) {
+				fb_write_offset(tab[(srcbyte >> 7) & 1], offset, dst);
+				srcbyte <<= 1;
+				offset++;
+			}
+		}
+		fb_address_move_long(dst, words_per_line);
+	}
+}
+
+static inline unsigned long fb_pack(unsigned long left, unsigned long right, int bits)
+{
+#ifdef __LITTLE_ENDIAN
+	return left | right << bits;
+#else
+	return right | left << bits;
+#endif
+}
+
+/* aligned 32/16 bpp blitting */
+static inline void fb_bitmap_2ppw(const struct fb_image *image, struct fb_address *dst,
+				  unsigned long fgcolor, unsigned long bgcolor,
+				  int words_per_line, struct fb_reverse reverse)
+{
+	unsigned long tab[4];
+	const u8 *src = (u8 *)image->data;
+	int width = image->width / 2;
+	int offset;
+	u32 height;
+
+	tab[0] = fb_pack(bgcolor, bgcolor, BITS_PER_LONG/2);
+	tab[1] = fb_pack(bgcolor, fgcolor, BITS_PER_LONG/2);
+	tab[2] = fb_pack(fgcolor, bgcolor, BITS_PER_LONG/2);
+	tab[3] = fb_pack(fgcolor, fgcolor, BITS_PER_LONG/2);
+
+	if (reverse.byte) {
+		tab[0] = swab_long(tab[0]);
+		tab[1] = swab_long(tab[1]);
+		tab[2] = swab_long(tab[2]);
+		tab[3] = swab_long(tab[3]);
+	}
+
+	height = image->height;
+	while (height--) {
+		for (offset = 0; offset + 4 <= width; offset += 4) {
+			unsigned int srcbyte = *src++;
+
+			fb_write_offset(tab[(srcbyte >> 6) & 3], offset + 0, dst);
+			fb_write_offset(tab[(srcbyte >> 4) & 3], offset + 1, dst);
+			fb_write_offset(tab[(srcbyte >> 2) & 3], offset + 2, dst);
+			fb_write_offset(tab[(srcbyte >> 0) & 3], offset + 3, dst);
+		}
+
+		if (offset < width) {
+			unsigned int srcbyte = *src++;
+
+			while (offset < width) {
+				fb_write_offset(tab[(srcbyte >> 6) & 3], offset, dst);
+				srcbyte <<= 2;
+				offset++;
+			}
+		}
+		fb_address_move_long(dst, words_per_line);
+	}
+}
+
+#define FB_PATP(a, b) (((a)<<((b)*BITS_PER_LONG/4))*((1UL<<BITS_PER_LONG/4)-1UL))
+#define FB_PAT4(a) (FB_PATP((a)&1, 0)|FB_PATP(((a)&2)/2, 1)| +	FB_PATP(((a)&4)/4, 2)|FB_PATP(((a)&8)/8, 3))
+
+/* aligned 16/8 bpp blitting */
+static inline void fb_bitmap_4ppw(const struct fb_image *image, struct fb_address *dst,
+				  unsigned long fgcolor, unsigned long bgcolor,
+				  int words_per_line, struct fb_reverse reverse)
+{
+	static const unsigned long tab16_be[] = {
+		0, FB_PAT4(1UL), FB_PAT4(2UL), FB_PAT4(3UL),
+		FB_PAT4(4UL), FB_PAT4(5UL), FB_PAT4(6UL), FB_PAT4(7UL),
+		FB_PAT4(8UL), FB_PAT4(9UL), FB_PAT4(10UL), FB_PAT4(11UL),
+		FB_PAT4(12UL), FB_PAT4(13UL), FB_PAT4(14UL), ~0UL
+	};
+	static const unsigned long tab16_le[] = {
+		0, FB_PAT4(8UL), FB_PAT4(4UL), FB_PAT4(12UL),
+		FB_PAT4(2UL), FB_PAT4(10UL), FB_PAT4(6UL), FB_PAT4(14UL),
+		FB_PAT4(1UL), FB_PAT4(9UL), FB_PAT4(5UL), FB_PAT4(13UL),
+		FB_PAT4(3UL), FB_PAT4(11UL), FB_PAT4(7UL), ~0UL
+	};
+	const unsigned long *tab;
+	const u8 *src = (u8 *)image->data;
+	int width = image->width / 4;
+	int offset;
+	u32 height;
+
+	fgcolor = fgcolor | fgcolor << BITS_PER_LONG/4;
+	bgcolor = bgcolor | bgcolor << BITS_PER_LONG/4;
+	fgcolor = fgcolor | fgcolor << BITS_PER_LONG/2;
+	bgcolor = bgcolor | bgcolor << BITS_PER_LONG/2;
+	fgcolor ^= bgcolor;
+
+	if (BITS_PER_LONG/4 > BITS_PER_BYTE && reverse.byte) {
+		fgcolor = swab_long(fgcolor);
+		bgcolor = swab_long(bgcolor);
+	}
+
+#ifdef __LITTLE_ENDIAN
+	tab = reverse.byte ? tab16_be : tab16_le;
+#else
+	tab = reverse.byte ? tab16_le : tab16_be;
+#endif
+
+	height = image->height;
+	while (height--) {
+		for (offset = 0; offset + 2 <= width; offset += 2, src++) {
+			fb_write_offset((fgcolor & tab[*src >> 4]) ^ bgcolor, offset + 0, dst);
+			fb_write_offset((fgcolor & tab[*src & 0xf]) ^ bgcolor, offset + 1, dst);
+		}
+
+		if (offset < width)
+			fb_write_offset((fgcolor & tab[*src++ >> 4]) ^ bgcolor, offset, dst);
+
+		fb_address_move_long(dst, words_per_line);
+	}
+}
+
+static inline void fb_bitmap_imageblit(const struct fb_image *image, struct fb_address *dst,
+				       unsigned int bits_per_line, const u32 *palette, int bpp,
+				       struct fb_reverse reverse)
+{
+	unsigned long fgcolor, bgcolor;
+
+	if (palette) {
+		fgcolor = palette[image->fg_color];
+		bgcolor = palette[image->bg_color];
+	} else {
+		fgcolor = image->fg_color;
+		bgcolor = image->bg_color;
+	}
+
+	if (!dst->bits && !(bits_per_line & (BITS_PER_LONG-1))) {
+		if (bpp == BITS_PER_LONG && BITS_PER_LONG == 32) {
+			fb_bitmap_1ppw(image, dst, fgcolor, bgcolor,
+				       bits_per_line / BITS_PER_LONG, reverse);
+			return;
+		}
+		if (bpp == BITS_PER_LONG/2 && !(image->width & 1)) {
+			fb_bitmap_2ppw(image, dst, fgcolor, bgcolor,
+				       bits_per_line / BITS_PER_LONG, reverse);
+			return;
+		}
+		if (bpp == BITS_PER_LONG/4 && !(image->width & 3)) {
+			fb_bitmap_4ppw(image, dst, fgcolor, bgcolor,
+				       bits_per_line / BITS_PER_LONG, reverse);
+			return;
+		}
+	}
+
+	if (bpp > 0 && bpp <= BITS_PER_BYTE)
+		fb_bitmap4x_imageblit(image, dst, fgcolor, bgcolor, bpp,
+				     bits_per_line, reverse);
+	else if (bpp > BITS_PER_BYTE && bpp <= BITS_PER_LONG)
+		fb_bitmap1x_imageblit(image, dst, fgcolor, bgcolor, bpp,
+				     bits_per_line, reverse);
+}
+
+static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+	int bpp = p->var.bits_per_pixel;
+	unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
+	struct fb_address dst = fb_address_init(p);
+	struct fb_reverse reverse = fb_reverse_init(p);
+	const u32 *palette = fb_palette(p);
+
+	fb_address_forward(&dst, image->dy * bits_per_line + image->dx * bpp);
+
+	if (image->depth == 1)
+		fb_bitmap_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
+	else
+		fb_color_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
+}
diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c
index 75e7001e8..b634e2d21 100644
--- a/drivers/video/fbdev/core/syscopyarea.c
+++ b/drivers/video/fbdev/core/syscopyarea.c
@@ -1,373 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic Bit Block Transfer for frame buffers located in system RAM with
- *  packed pixels of any depth.
- *
- *  Based almost entirely from cfbcopyarea.c (which is based almost entirely
- *  on Geert Uytterhoeven's copyarea routine)
- *
- *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- *
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
 #include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
-#include <asm/io.h>
-#include "fb_draw.h"
 
-    /*
-     *  Generic bitwise copy algorithm
-     */
+#ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
+#endif
 
-static void
-bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
-	const unsigned long *src, unsigned src_idx, int bits, unsigned n)
-{
-	unsigned long first, last;
-	int const shift = dst_idx-src_idx;
-	int left, right;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (!shift) {
-		/* Same alignment for source and dest */
-		if (dst_idx+n <= bits) {
-			/* Single word */
-			if (last)
-				first &= last;
-			*dst = comp(*src, *dst, first);
-		} else {
-			/* Multiple destination words */
-			/* Leading bits */
- 			if (first != ~0UL) {
-				*dst = comp(*src, *dst, first);
-				dst++;
-				src++;
-				n -= bits - dst_idx;
-			}
-
-			/* Main chunk */
-			n /= bits;
-			while (n >= 8) {
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				*dst++ = *src++;
-				n -= 8;
-			}
-			while (n--)
-				*dst++ = *src++;
-
-			/* Trailing bits */
-			if (last)
-				*dst = comp(*src, *dst, last);
-		}
-	} else {
-		unsigned long d0, d1;
-		int m;
-
-		/* Different alignment for source and dest */
-		right = shift & (bits - 1);
-		left = -shift & (bits - 1);
-
-		if (dst_idx+n <= bits) {
-			/* Single destination word */
-			if (last)
-				first &= last;
-			if (shift > 0) {
-				/* Single source word */
-				*dst = comp(*src << left, *dst, first);
-			} else if (src_idx+n <= bits) {
-				/* Single source word */
-				*dst = comp(*src >> right, *dst, first);
-			} else {
-				/* 2 source words */
-				d0 = *src++;
-				d1 = *src;
-				*dst = comp(d0 >> right | d1 << left, *dst,
-					    first);
-			}
-		} else {
-			/* Multiple destination words */
-			/** We must always remember the last value read,
-			    because in case SRC and DST overlap bitwise (e.g.
-			    when moving just one pixel in 1bpp), we always
-			    collect one full long for DST and that might
-			    overlap with the current long from SRC. We store
-			    this value in 'd0'. */
-			d0 = *src++;
-			/* Leading bits */
-			if (shift > 0) {
-				/* Single source word */
-				*dst = comp(d0 << left, *dst, first);
-				dst++;
-				n -= bits - dst_idx;
-			} else {
-				/* 2 source words */
-				d1 = *src++;
-				*dst = comp(d0 >> right | d1 << left, *dst,
-					    first);
-				d0 = d1;
-				dst++;
-				n -= bits - dst_idx;
-			}
-
-			/* Main chunk */
-			m = n % bits;
-			n /= bits;
-			while (n >= 4) {
-				d1 = *src++;
-				*dst++ = d0 >> right | d1 << left;
-				d0 = d1;
-				d1 = *src++;
-				*dst++ = d0 >> right | d1 << left;
-				d0 = d1;
-				d1 = *src++;
-				*dst++ = d0 >> right | d1 << left;
-				d0 = d1;
-				d1 = *src++;
-				*dst++ = d0 >> right | d1 << left;
-				d0 = d1;
-				n -= 4;
-			}
-			while (n--) {
-				d1 = *src++;
-				*dst++ = d0 >> right | d1 << left;
-				d0 = d1;
-			}
-
-			/* Trailing bits */
-			if (m) {
-				if (m <= bits - right) {
-					/* Single source word */
-					d0 >>= right;
-				} else {
-					/* 2 source words */
- 					d1 = *src;
-					d0 = d0 >> right | d1 << left;
-				}
-				*dst = comp(d0, *dst, last);
-			}
-		}
-	}
-}
-
-    /*
-     *  Generic bitwise copy algorithm, operating backward
-     */
-
-static void
-bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
-	   const unsigned long *src, unsigned src_idx, unsigned bits,
-	   unsigned n)
-{
-	unsigned long first, last;
-	int shift;
-
-	dst += (dst_idx + n - 1) / bits;
-	src += (src_idx + n - 1) / bits;
-	dst_idx = (dst_idx + n - 1) % bits;
-	src_idx = (src_idx + n - 1) % bits;
-
-	shift = dst_idx-src_idx;
-
-	first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits);
-	last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits);
-
-	if (!shift) {
-		/* Same alignment for source and dest */
-		if ((unsigned long)dst_idx+1 >= n) {
-			/* Single word */
-			if (first)
-				last &= first;
-			*dst = comp(*src, *dst, last);
-		} else {
-			/* Multiple destination words */
-
-			/* Leading bits */
-			if (first) {
-				*dst = comp(*src, *dst, first);
-				dst--;
-				src--;
-				n -= dst_idx+1;
-			}
-
-			/* Main chunk */
-			n /= bits;
-			while (n >= 8) {
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				*dst-- = *src--;
-				n -= 8;
-			}
-			while (n--)
-				*dst-- = *src--;
-			/* Trailing bits */
-			if (last != -1UL)
-				*dst = comp(*src, *dst, last);
-		}
-	} else {
-		/* Different alignment for source and dest */
-
-		int const left = shift & (bits-1);
-		int const right = -shift & (bits-1);
-
-		if ((unsigned long)dst_idx+1 >= n) {
-			/* Single destination word */
-			if (first)
-				last &= first;
-			if (shift < 0) {
-				/* Single source word */
-				*dst = comp(*src >> right, *dst, last);
-			} else if (1+(unsigned long)src_idx >= n) {
-				/* Single source word */
-				*dst = comp(*src << left, *dst, last);
-			} else {
-				/* 2 source words */
-				*dst = comp(*src << left | *(src-1) >> right,
-					    *dst, last);
-			}
-		} else {
-			/* Multiple destination words */
-			/** We must always remember the last value read,
-			    because in case SRC and DST overlap bitwise (e.g.
-			    when moving just one pixel in 1bpp), we always
-			    collect one full long for DST and that might
-			    overlap with the current long from SRC. We store
-			    this value in 'd0'. */
-			unsigned long d0, d1;
-			int m;
-
-			d0 = *src--;
-			/* Leading bits */
-			if (shift < 0) {
-				/* Single source word */
-				d1 = d0;
-				d0 >>= right;
-			} else {
-				/* 2 source words */
-				d1 = *src--;
-				d0 = d0 << left | d1 >> right;
-			}
-			if (!first)
-				*dst = d0;
-			else
-				*dst = comp(d0, *dst, first);
-			d0 = d1;
-			dst--;
-			n -= dst_idx+1;
-
-			/* Main chunk */
-			m = n % bits;
-			n /= bits;
-			while (n >= 4) {
-				d1 = *src--;
-				*dst-- = d0 << left | d1 >> right;
-				d0 = d1;
-				d1 = *src--;
-				*dst-- = d0 << left | d1 >> right;
-				d0 = d1;
-				d1 = *src--;
-				*dst-- = d0 << left | d1 >> right;
-				d0 = d1;
-				d1 = *src--;
-				*dst-- = d0 << left | d1 >> right;
-				d0 = d1;
-				n -= 4;
-			}
-			while (n--) {
-				d1 = *src--;
-				*dst-- = d0 << left | d1 >> right;
-				d0 = d1;
-			}
-
-			/* Trailing bits */
-			if (m) {
-				if (m <= bits - left) {
-					/* Single source word */
-					d0 <<= left;
-				} else {
-					/* 2 source words */
-					d1 = *src;
-					d0 = d0 << left | d1 >> right;
-				}
-				*dst = comp(d0, *dst, last);
-			}
-		}
-	}
-}
+#include "sysmem.h"
+#include "fb_copyarea.h"
 
 void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
 {
-	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
-	u32 height = area->height, width = area->width;
-	unsigned int const bits_per_line = p->fix.line_length * 8u;
-	unsigned long *base = NULL;
-	int bits = BITS_PER_LONG, bytes = bits >> 3;
-	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
-
-	if (p->state != FBINFO_STATE_RUNNING)
-		return;
-
 	if (!(p->flags & FBINFO_VIRTFB))
-		fb_warn_once(p, "Framebuffer is not in virtual address space.");
-
-	/* if the beginning of the target area might overlap with the end of
-	the source area, be have to copy the area reverse. */
-	if ((dy == sy && dx > sx) || (dy > sy)) {
-		dy += height;
-		sy += height;
-		rev_copy = 1;
-	}
+		fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__);
 
-	/* split the base of the framebuffer into a long-aligned address and
-	   the index of the first bit */
-	base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
-	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
-	/* add offset of source and target area */
-	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
-	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
-
-	if (p->fbops->fb_sync)
-		p->fbops->fb_sync(p);
-
-	if (rev_copy) {
-		while (height--) {
-			dst_idx -= bits_per_line;
-			src_idx -= bits_per_line;
-			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
-				base + (src_idx / bits), src_idx % bits, bits,
-				width*p->var.bits_per_pixel);
-		}
-	} else {
-		while (height--) {
-			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
-				base + (src_idx / bits), src_idx % bits, bits,
-				width*p->var.bits_per_pixel);
-			dst_idx += bits_per_line;
-			src_idx += bits_per_line;
-		}
-	}
+	fb_copyarea(p, area);
 }
-
 EXPORT_SYMBOL(sys_copyarea);
 
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer area copy");
 MODULE_LICENSE("GPL");
-
diff --git a/drivers/video/fbdev/core/sysfillrect.c b/drivers/video/fbdev/core/sysfillrect.c
index e49221a88..372ca6a32 100644
--- a/drivers/video/fbdev/core/sysfillrect.c
+++ b/drivers/video/fbdev/core/sysfillrect.c
@@ -1,328 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic fillrect for frame buffers in system RAM with packed pixels of
- *  any depth.
- *
- *  Based almost entirely from cfbfillrect.c (which is based almost entirely
- *  on Geert Uytterhoeven's fillrect routine)
- *
- *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
 #include <linux/module.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
-#include "fb_draw.h"
 
-    /*
-     *  Aligned pattern fill using 32/64-bit memory accesses
-     */
-
-static void
-bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
-		unsigned long pat, unsigned n, int bits)
-{
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		/* Single word */
-		if (last)
-			first &= last;
-		*dst = comp(pat, *dst, first);
-	} else {
-		/* Multiple destination words */
-
-		/* Leading bits */
- 		if (first!= ~0UL) {
-			*dst = comp(pat, *dst, first);
-			dst++;
-			n -= bits - dst_idx;
-		}
-
-		/* Main chunk */
-		n /= bits;
-		memset_l(dst, pat, n);
-		dst += n;
-
-		/* Trailing bits */
-		if (last)
-			*dst = comp(pat, *dst, last);
-	}
-}
-
-
-    /*
-     *  Unaligned generic pattern fill using 32/64-bit memory accesses
-     *  The pattern must have been expanded to a full 32/64-bit value
-     *  Left/right are the appropriate shifts to convert to the pattern to be
-     *  used for the next 32/64-bit word
-     */
-
-static void
-bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
-		  unsigned long pat, int left, int right, unsigned n, int bits)
-{
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		/* Single word */
-		if (last)
-			first &= last;
-		*dst = comp(pat, *dst, first);
-	} else {
-		/* Multiple destination words */
-		/* Leading bits */
-		if (first) {
-			*dst = comp(pat, *dst, first);
-			dst++;
-			pat = pat << left | pat >> right;
-			n -= bits - dst_idx;
-		}
-
-		/* Main chunk */
-		n /= bits;
-		while (n >= 4) {
-			*dst++ = pat;
-			pat = pat << left | pat >> right;
-			*dst++ = pat;
-			pat = pat << left | pat >> right;
-			*dst++ = pat;
-			pat = pat << left | pat >> right;
-			*dst++ = pat;
-			pat = pat << left | pat >> right;
-			n -= 4;
-		}
-		while (n--) {
-			*dst++ = pat;
-			pat = pat << left | pat >> right;
-		}
-
-		/* Trailing bits */
-		if (last)
-			*dst = comp(pat, *dst, last);
-	}
-}
-
-    /*
-     *  Aligned pattern invert using 32/64-bit memory accesses
-     */
-static void
-bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
-		    unsigned long pat, unsigned n, int bits)
-{
-	unsigned long val = pat;
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		/* Single word */
-		if (last)
-			first &= last;
-		*dst = comp(*dst ^ val, *dst, first);
-	} else {
-		/* Multiple destination words */
-		/* Leading bits */
-		if (first!=0UL) {
-			*dst = comp(*dst ^ val, *dst, first);
-			dst++;
-			n -= bits - dst_idx;
-		}
-
-		/* Main chunk */
-		n /= bits;
-		while (n >= 8) {
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			*dst++ ^= val;
-			n -= 8;
-		}
-		while (n--)
-			*dst++ ^= val;
-		/* Trailing bits */
-		if (last)
-			*dst = comp(*dst ^ val, *dst, last);
-	}
-}
-
-
-    /*
-     *  Unaligned generic pattern invert using 32/64-bit memory accesses
-     *  The pattern must have been expanded to a full 32/64-bit value
-     *  Left/right are the appropriate shifts to convert to the pattern to be
-     *  used for the next 32/64-bit word
-     */
-
-static void
-bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
-		      unsigned long pat, int left, int right, unsigned n,
-		      int bits)
-{
-	unsigned long first, last;
-
-	if (!n)
-		return;
-
-	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
-	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
-	if (dst_idx+n <= bits) {
-		/* Single word */
-		if (last)
-			first &= last;
-		*dst = comp(*dst ^ pat, *dst, first);
-	} else {
-		/* Multiple destination words */
-
-		/* Leading bits */
-		if (first != 0UL) {
-			*dst = comp(*dst ^ pat, *dst, first);
-			dst++;
-			pat = pat << left | pat >> right;
-			n -= bits - dst_idx;
-		}
-
-		/* Main chunk */
-		n /= bits;
-		while (n >= 4) {
-			*dst++ ^= pat;
-			pat = pat << left | pat >> right;
-			*dst++ ^= pat;
-			pat = pat << left | pat >> right;
-			*dst++ ^= pat;
-			pat = pat << left | pat >> right;
-			*dst++ ^= pat;
-			pat = pat << left | pat >> right;
-			n -= 4;
-		}
-		while (n--) {
-			*dst ^= pat;
-			pat = pat << left | pat >> right;
-		}
+#ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
+#endif
 
-		/* Trailing bits */
-		if (last)
-			*dst = comp(*dst ^ pat, *dst, last);
-	}
-}
+#include "sysmem.h"
+#include "fb_fillrect.h"
 
 void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
 {
-	unsigned long pat, pat2, fg;
-	unsigned long width = rect->width, height = rect->height;
-	int bits = BITS_PER_LONG, bytes = bits >> 3;
-	u32 bpp = p->var.bits_per_pixel;
-	unsigned long *dst;
-	int dst_idx, left;
-
-	if (p->state != FBINFO_STATE_RUNNING)
-		return;
-
 	if (!(p->flags & FBINFO_VIRTFB))
-		fb_warn_once(p, "Framebuffer is not in virtual address space.");
+		fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__);
 
-	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-	    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
-		fg = ((u32 *) (p->pseudo_palette))[rect->color];
-	else
-		fg = rect->color;
-
-	pat = pixel_to_pat( bpp, fg);
-
-	dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
-	dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
-	dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
-	/* FIXME For now we support 1-32 bpp only */
-	left = bits % bpp;
-	if (p->fbops->fb_sync)
-		p->fbops->fb_sync(p);
-	if (!left) {
-		void (*fill_op32)(struct fb_info *p, unsigned long *dst,
-				  int dst_idx, unsigned long pat, unsigned n,
-				  int bits) = NULL;
-
-		switch (rect->rop) {
-		case ROP_XOR:
-			fill_op32 = bitfill_aligned_rev;
-			break;
-		case ROP_COPY:
-			fill_op32 = bitfill_aligned;
-			break;
-		default:
-			printk( KERN_ERR "cfb_fillrect(): unknown rop, "
-				"defaulting to ROP_COPY\n");
-			fill_op32 = bitfill_aligned;
-			break;
-		}
-		while (height--) {
-			dst += dst_idx >> (ffs(bits) - 1);
-			dst_idx &= (bits - 1);
-			fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
-			dst_idx += p->fix.line_length*8;
-		}
-	} else {
-		int right, r;
-		void (*fill_op)(struct fb_info *p, unsigned long *dst,
-				int dst_idx, unsigned long pat, int left,
-				int right, unsigned n, int bits) = NULL;
-#ifdef __LITTLE_ENDIAN
-		right = left;
-		left = bpp - right;
-#else
-		right = bpp - left;
-#endif
-		switch (rect->rop) {
-		case ROP_XOR:
-			fill_op = bitfill_unaligned_rev;
-			break;
-		case ROP_COPY:
-			fill_op = bitfill_unaligned;
-			break;
-		default:
-			printk(KERN_ERR "sys_fillrect(): unknown rop, "
-				"defaulting to ROP_COPY\n");
-			fill_op = bitfill_unaligned;
-			break;
-		}
-		while (height--) {
-			dst += dst_idx / bits;
-			dst_idx &= (bits - 1);
-			r = dst_idx % bpp;
-			/* rotate pattern to the correct start position */
-			pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
-			fill_op(p, dst, dst_idx, pat2, left, right,
-				width*bpp, bits);
-			dst_idx += p->fix.line_length*8;
-		}
-	}
+	fb_fillrect(p, rect);
 }
-
 EXPORT_SYMBOL(sys_fillrect);
 
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer area fill");
 MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/sysimgblt.c b/drivers/video/fbdev/core/sysimgblt.c
index 6949bbd51..c756cc658 100644
--- a/drivers/video/fbdev/core/sysimgblt.c
+++ b/drivers/video/fbdev/core/sysimgblt.c
@@ -1,339 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic 1-bit or 8-bit source to 1-32 bit destination expansion
- *  for frame buffer located in system RAM with packed pixels of any depth.
- *
- *  Based almost entirely on cfbimgblt.c
- *
- *      Copyright (C)  April 2007 Antonino Daplas <adaplas@pol.net>
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
+ *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
  */
 #include <linux/module.h>
-#include <linux/string.h>
 #include <linux/fb.h>
+#include <linux/bitrev.h>
 #include <asm/types.h>
 
-#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
-#else
-#define DPRINTK(fmt, args...)
+#ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE
+#define FB_REV_PIXELS_IN_BYTE
 #endif
 
-static const u32 cfb_tab8_be[] = {
-    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
-    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
-    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
-    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
-};
-
-static const u32 cfb_tab8_le[] = {
-    0x00000000,0xff000000,0x00ff0000,0xffff0000,
-    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
-    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
-    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
-};
-
-static const u32 cfb_tab16_be[] = {
-    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
-};
-
-static const u32 cfb_tab16_le[] = {
-    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
-};
-
-static const u32 cfb_tab32[] = {
-	0x00000000, 0xffffffff
-};
-
-static void color_imageblit(const struct fb_image *image, struct fb_info *p,
-			    void *dst1, u32 start_index, u32 pitch_index)
-{
-	/* Draw the penguin */
-	u32 *dst, *dst2;
-	u32 color = 0, val, shift;
-	int i, n, bpp = p->var.bits_per_pixel;
-	u32 null_bits = 32 - bpp;
-	u32 *palette = (u32 *) p->pseudo_palette;
-	const u8 *src = image->data;
-
-	dst2 = dst1;
-	for (i = image->height; i--; ) {
-		n = image->width;
-		dst = dst1;
-		shift = 0;
-		val = 0;
-
-		if (start_index) {
-			u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
-							 start_index));
-			val = *dst & start_mask;
-			shift = start_index;
-		}
-		while (n--) {
-			if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-			    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
-				color = palette[*src];
-			else
-				color = *src;
-			color <<= FB_LEFT_POS(p, bpp);
-			val |= FB_SHIFT_HIGH(p, color, shift);
-			if (shift >= null_bits) {
-				*dst++ = val;
-
-				val = (shift == null_bits) ? 0 :
-					FB_SHIFT_LOW(p, color, 32 - shift);
-			}
-			shift += bpp;
-			shift &= (32 - 1);
-			src++;
-		}
-		if (shift) {
-			u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
-
-			*dst &= end_mask;
-			*dst |= val;
-		}
-		dst1 += p->fix.line_length;
-		if (pitch_index) {
-			dst2 += p->fix.line_length;
-			dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
-
-			start_index += pitch_index;
-			start_index &= 32 - 1;
-		}
-	}
-}
-
-static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
-				  void *dst1, u32 fgcolor, u32 bgcolor,
-				  u32 start_index, u32 pitch_index)
-{
-	u32 shift, color = 0, bpp = p->var.bits_per_pixel;
-	u32 *dst, *dst2;
-	u32 val, pitch = p->fix.line_length;
-	u32 null_bits = 32 - bpp;
-	u32 spitch = (image->width+7)/8;
-	const u8 *src = image->data, *s;
-	u32 i, j, l;
-
-	dst2 = dst1;
-	fgcolor <<= FB_LEFT_POS(p, bpp);
-	bgcolor <<= FB_LEFT_POS(p, bpp);
-
-	for (i = image->height; i--; ) {
-		shift = val = 0;
-		l = 8;
-		j = image->width;
-		dst = dst1;
-		s = src;
-
-		/* write leading bits */
-		if (start_index) {
-			u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
-							 start_index));
-			val = *dst & start_mask;
-			shift = start_index;
-		}
-
-		while (j--) {
-			l--;
-			color = (*s & (1 << l)) ? fgcolor : bgcolor;
-			val |= FB_SHIFT_HIGH(p, color, shift);
-
-			/* Did the bitshift spill bits to the next long? */
-			if (shift >= null_bits) {
-				*dst++ = val;
-				val = (shift == null_bits) ? 0 :
-					FB_SHIFT_LOW(p, color, 32 - shift);
-			}
-			shift += bpp;
-			shift &= (32 - 1);
-			if (!l) { l = 8; s++; }
-		}
-
-		/* write trailing bits */
- 		if (shift) {
-			u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
-
-			*dst &= end_mask;
-			*dst |= val;
-		}
-
-		dst1 += pitch;
-		src += spitch;
-		if (pitch_index) {
-			dst2 += pitch;
-			dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
-			start_index += pitch_index;
-			start_index &= 32 - 1;
-		}
-
-	}
-}
-
-/*
- * fast_imageblit - optimized monochrome color expansion
- *
- * Only if:  bits_per_pixel == 8, 16, or 32
- *           image->width is divisible by pixel/dword (ppw);
- *           fix->line_legth is divisible by 4;
- *           beginning and end of a scanline is dword aligned
- */
-static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
-				  void *dst1, u32 fgcolor, u32 bgcolor)
-{
-	u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
-	u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
-	u32 bit_mask, eorx, shift;
-	const u8 *s = image->data, *src;
-	u32 *dst;
-	const u32 *tab;
-	size_t tablen;
-	u32 colortab[16];
-	int i, j, k;
-
-	switch (bpp) {
-	case 8:
-		tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
-		tablen = 16;
-		break;
-	case 16:
-		tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
-		tablen = 4;
-		break;
-	case 32:
-		tab = cfb_tab32;
-		tablen = 2;
-		break;
-	default:
-		return;
-	}
-
-	for (i = ppw-1; i--; ) {
-		fgx <<= bpp;
-		bgx <<= bpp;
-		fgx |= fgcolor;
-		bgx |= bgcolor;
-	}
-
-	bit_mask = (1 << ppw) - 1;
-	eorx = fgx ^ bgx;
-	k = image->width/ppw;
-
-	for (i = 0; i < tablen; ++i)
-		colortab[i] = (tab[i] & eorx) ^ bgx;
-
-	for (i = image->height; i--; ) {
-		dst = dst1;
-		shift = 8;
-		src = s;
-
-		/*
-		 * Manually unroll the per-line copying loop for better
-		 * performance. This works until we processed the last
-		 * completely filled source byte (inclusive).
-		 */
-		switch (ppw) {
-		case 4: /* 8 bpp */
-			for (j = k; j >= 2; j -= 2, ++src) {
-				*dst++ = colortab[(*src >> 4) & bit_mask];
-				*dst++ = colortab[(*src >> 0) & bit_mask];
-			}
-			break;
-		case 2: /* 16 bpp */
-			for (j = k; j >= 4; j -= 4, ++src) {
-				*dst++ = colortab[(*src >> 6) & bit_mask];
-				*dst++ = colortab[(*src >> 4) & bit_mask];
-				*dst++ = colortab[(*src >> 2) & bit_mask];
-				*dst++ = colortab[(*src >> 0) & bit_mask];
-			}
-			break;
-		case 1: /* 32 bpp */
-			for (j = k; j >= 8; j -= 8, ++src) {
-				*dst++ = colortab[(*src >> 7) & bit_mask];
-				*dst++ = colortab[(*src >> 6) & bit_mask];
-				*dst++ = colortab[(*src >> 5) & bit_mask];
-				*dst++ = colortab[(*src >> 4) & bit_mask];
-				*dst++ = colortab[(*src >> 3) & bit_mask];
-				*dst++ = colortab[(*src >> 2) & bit_mask];
-				*dst++ = colortab[(*src >> 1) & bit_mask];
-				*dst++ = colortab[(*src >> 0) & bit_mask];
-			}
-			break;
-		}
-
-		/*
-		 * For image widths that are not a multiple of 8, there
-		 * are trailing pixels left on the current line. Print
-		 * them as well.
-		 */
-		for (; j--; ) {
-			shift -= ppw;
-			*dst++ = colortab[(*src >> shift) & bit_mask];
-			if (!shift) {
-				shift = 8;
-				++src;
-			}
-		}
-
-		dst1 += p->fix.line_length;
-		s += spitch;
-	}
-}
+#include "sysmem.h"
+#include "fb_imageblit.h"
 
 void sys_imageblit(struct fb_info *p, const struct fb_image *image)
 {
-	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
-	u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
-	u32 width = image->width;
-	u32 dx = image->dx, dy = image->dy;
-	void *dst1;
-
-	if (p->state != FBINFO_STATE_RUNNING)
-		return;
-
 	if (!(p->flags & FBINFO_VIRTFB))
-		fb_warn_once(p, "Framebuffer is not in virtual address space.");
-
-	bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
-	start_index = bitstart & (32 - 1);
-	pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
+		fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__);
 
-	bitstart /= 8;
-	bitstart &= ~(bpl - 1);
-	dst1 = (void __force *)p->screen_base + bitstart;
-
-	if (p->fbops->fb_sync)
-		p->fbops->fb_sync(p);
-
-	if (image->depth == 1) {
-		if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
-		    p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-			fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
-			bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
-		} else {
-			fgcolor = image->fg_color;
-			bgcolor = image->bg_color;
-		}
-
-		if (32 % bpp == 0 && !start_index && !pitch_index &&
-		    ((width & (32/bpp-1)) == 0) &&
-		    bpp >= 8 && bpp <= 32)
-			fast_imageblit(image, p, dst1, fgcolor, bgcolor);
-		else
-			slow_imageblit(image, p, dst1, fgcolor, bgcolor,
-					start_index, pitch_index);
-	} else
-		color_imageblit(image, p, dst1, start_index, pitch_index);
+	fb_imageblit(p, image);
 }
-
 EXPORT_SYMBOL(sys_imageblit);
 
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)");
+MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>");
+MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer image draw");
 MODULE_LICENSE("GPL");
-
diff --git a/drivers/video/fbdev/core/sysmem.h b/drivers/video/fbdev/core/sysmem.h
new file mode 100644
index 000000000..033c31a96
--- /dev/null
+++ b/drivers/video/fbdev/core/sysmem.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ *	Virtual memory framebuffer access for drawing routines
+ *
+ *	Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org)
+ */
+
+/* keeps track of a bit address in framebuffer memory */
+struct fb_address {
+	void *address;
+	int bits;
+};
+
+/* initialize the bit address pointer to the beginning of the frame buffer */
+static inline struct fb_address fb_address_init(struct fb_info *p)
+{
+	void *base = p->screen_buffer;
+	struct fb_address ptr;
+
+	ptr.address = PTR_ALIGN_DOWN(base, BITS_PER_LONG / BITS_PER_BYTE);
+	ptr.bits = (base - ptr.address) * BITS_PER_BYTE;
+	return ptr;
+}
+
+/* framebuffer write access */
+static inline void fb_write_offset(unsigned long val, int offset, const struct fb_address *dst)
+{
+	unsigned long *mem = dst->address;
+
+	mem[offset] = val;
+}
+
+/* framebuffer read access */
+static inline unsigned long fb_read_offset(int offset, const struct fb_address *src)
+{
+	unsigned long *mem = src->address;
+
+	return mem[offset];
+}
-- 
2.30.2


^ permalink raw reply related

* Re: [PATCH v3 1/2] dt-bindings: mfd: Document TI LM3533 MFD
From: Rob Herring @ 2025-02-24 20:31 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Jonathan Cameron,
	Lars-Peter Clausen, Pavel Machek, Daniel Thompson, Jingoo Han,
	Helge Deller, Andy Shevchenko, Uwe Kleine-König, devicetree,
	linux-kernel, linux-iio, linux-leds, dri-devel, linux-fbdev
In-Reply-To: <20250224114815.146053-2-clamor95@gmail.com>

On Mon, Feb 24, 2025 at 01:48:13PM +0200, Svyatoslav Ryhel wrote:
> Add bindings for the LM3533 - a complete power source for backlight, keypad
> and indicator LEDs in smartphone handsets. The high-voltage inductive boost
> converter provides the power for two series LED strings display backlight
> and keypad functions.
> 
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
>  .../devicetree/bindings/mfd/ti,lm3533.yaml    | 231 ++++++++++++++++++
>  1 file changed, 231 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/ti,lm3533.yaml
> 
> diff --git a/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml b/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml
> new file mode 100644
> index 000000000000..c8ac6d4424aa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml
> @@ -0,0 +1,231 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/ti,lm3533.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: TI LM3533 Complete Lighting Power Solution
> +
> +description: >
> +  The LM3533 is a complete power source for backlight, keypad, and indicator LEDs
> +  in smartphone handsets. The high-voltage inductive boost converter provides the
> +  power for two series LED strings display backlight and keypad functions.
> +
> +  https://www.ti.com/product/LM3533
> +
> +maintainers:
> +  - Svyatoslav Ryhel <clamor95@gmail.com>
> +
> +properties:
> +  compatible:
> +    const: ti,lm3533
> +
> +  reg:
> +    maxItems: 1
> +
> +  enable-gpios:
> +    description: GPIO to use to enable/disable the backlight (HWEN pin).
> +    maxItems: 1
> +
> +  ti,boost-ovp-microvolt:
> +    description:
> +      Boost OVP select (16V, 24V, 32V, 40V)
> +    enum: [ 16000000, 24000000, 32000000, 40000000 ]
> +    default: 16000000
> +
> +  ti,boost-freq-hz:
> +    description:
> +      Boost frequency select (500KHz or 1MHz)
> +    enum: [ 500000, 1000000 ]
> +    default: 500000
> +
> +  light-sensor:
> +    type: object
> +    description:
> +      Properties for an illumination sensor.
> +    additionalProperties: false
> +
> +    properties:
> +      compatible:
> +        const: ti,lm3533-als
> +
> +      ti,resistor-value-ohm:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        description:
> +          Internal configuration resister value when ALS is in Analog Sensor
> +          mode and PWM mode is disabled.
> +        minimum: 1575
> +        maximum: 200000
> +
> +      ti,pwm-mode:
> +        type: boolean
> +        description:
> +          Switch for mode in which ALS is running. If this propertly is set
> +          then ALS is running in PWM mode, internal resistor value is set to
> +          high-impedance (0) and resistor-value-ohm propertly is ignored.
> +
> +    required:
> +      - compatible
> +
> +required:
> +  - compatible
> +  - reg
> +  - enable-gpios
> +  - light-sensor
> +  - backlight-0
> +  - backlight-1
> +  - led-0
> +  - led-1
> +  - led-2
> +  - led-3
> +
> +patternProperties:
> +  "^backlight-[01]$":
> +    type: object
> +    description:
> +      Properties for a backlight device.
> +
> +    $ref: /schemas/leds/backlight/common.yaml#
> +
> +    properties:
> +      compatible:
> +        const: ti,lm3533-backlight
> +
> +      default-brightness: true
> +
> +      ti,max-current-microamp:
> +        description:
> +          Maximum current in µA with a 800 µA step.
> +        enum: [ 5000, 5800, 6600, 7400, 8200, 9000, 9800,
> +                10600, 11400, 12200, 13000, 13800, 14600,
> +                15400, 16200, 17000, 17800, 18600, 19400,
> +                20200, 21000, 21800, 22600, 23400, 24200,
> +                25000, 25800, 26600, 27400, 28200, 29000,
> +                29800 ]
> +        default: 5000
> +
> +      ti,pwm-config-mask:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        description:
> +          Control Bank PWM Configuration Register mask that allows to configure
> +          PWM input in Zones 0-4
> +          BIT(0) - PWM Input is enabled
> +          BIT(1) - PWM Input is enabled in Zone 0
> +          BIT(2) - PWM Input is enabled in Zone 1
> +          BIT(3) - PWM Input is enabled in Zone 2
> +          BIT(4) - PWM Input is enabled in Zone 3
> +          BIT(5) - PWM Input is enabled in Zone 4
> +
> +      ti,linear-mapping-mode:
> +        description:
> +          Enable linear mapping mode. If disabled, then it will use exponential
> +          mapping mode in which the ramp up/down appears to have a more uniform
> +          transition to the human eye.
> +        type: boolean
> +
> +      ti,hardware-controlled:
> +        description:
> +          Each backlight has its own voltage Control Bank (A and B) and there are
> +          two HVLED sinks which by default are linked to respective Bank. Setting
> +          this property will link both sinks to a Control Bank of backlight where
> +          property is defined.
> +        type: boolean
> +
> +    required:
> +      - compatible
> +
> +    additionalProperties: false
> +
> +  "^led-[0-3]$":
> +    type: object
> +    description:
> +      Properties for a led device.
> +
> +    $ref: /schemas/leds/common.yaml#
> +
> +    properties:
> +      compatible:
> +        const: ti,lm3533-leds
> +
> +      linux,default-trigger: true
> +
> +      ti,max-current-microamp:
> +        description:
> +          Maximum current in µA with a 800 µA step.
> +        enum: [ 5000, 5800, 6600, 7400, 8200, 9000, 9800,
> +                10600, 11400, 12200, 13000, 13800, 14600,
> +                15400, 16200, 17000, 17800, 18600, 19400,
> +                20200, 21000, 21800, 22600, 23400, 24200,
> +                25000, 25800, 26600, 27400, 28200, 29000,
> +                29800 ]
> +        default: 5000
> +
> +      ti,pwm-config-mask:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        description:
> +          Same descryption and function as for backlight.
> +
> +    required:
> +      - compatible
> +
> +    additionalProperties: false
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        led-controller@36 {
> +            compatible = "ti,lm3533";
> +            reg = <0x36>;
> +
> +            enable-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>;
> +
> +            ti,boost-ovp-microvolt = <24000000>;
> +            ti,boost-freq-hz = <500000>;
> +
> +            backlight-0 {
> +                compatible = "ti,lm3533-backlight";
> +
> +                ti,max-current-microamp = <23400>;
> +                default-brightness = <113>;
> +                ti,hardware-controlled;
> +            };
> +
> +            backlight-1 {
> +                compatible = "ti,lm3533-backlight";
> +                status = "disabled";

Examples should be enabled. Drop status.

With those fixed,

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>

> +            };
> +
> +            led-0 {
> +                compatible = "ti,lm3533-leds";
> +                status = "disabled";
> +            };
> +
> +            led-1 {
> +                compatible = "ti,lm3533-leds";
> +                status = "disabled";
> +            };
> +
> +            led-2 {
> +                compatible = "ti,lm3533-leds";
> +                status = "disabled";
> +            };
> +
> +            led-3 {
> +                compatible = "ti,lm3533-leds";
> +                status = "disabled";
> +            };
> +
> +            light-sensor {
> +                compatible = "ti,lm3533-als";
> +                status = "disabled";
> +            };
> +        };
> +    };
> +...
> -- 
> 2.43.0
> 

^ permalink raw reply

* RE: [PATCH] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Michael Kelley @ 2025-02-24 19:24 UTC (permalink / raw)
  To: Saurabh Singh Sengar
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, deller@gmx.de, akpm@linux-foundation.org,
	linux-hyperv@vger.kernel.org, linux-fbdev@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	ssengar@microsoft.com
In-Reply-To: <20250224132946.GA7039@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>

From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Monday, February 24, 2025 5:30 AM
> 
> On Mon, Feb 24, 2025 at 12:38:22AM +0000, Michael Kelley wrote:
> > From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Sunday, February 23, 2025 6:10 AM
> > >
> > > On Sat, Feb 22, 2025 at 08:16:53PM +0000, Michael Kelley wrote:
> > > > From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 22, 2025 9:27 AM
> > > > >
> >
> > [anip]
> >
> > > > >
> > > > > I had considered moving the entire `hvfb_putmem()` function to `destroy`,
> > > > > but I was hesitant for two reasons:
> > > > >
> > > > >   1. I wasn’t aware of any scenario where this would be useful. However,
> > > > >      your explanation has convinced me that it is necessary.
> > > > >   2. `hvfb_release_phymem()` relies on the `hdev` pointer, which requires
> > > > >      multiple `container_of` operations to derive it from the `info` pointer.
> > > > >      I was unsure if the complexity was justified, but it seems worthwhile now.
> > > > >
> > > > > I will move `hvfb_putmem()` to the `destroy` function in V2, and I hope this
> > > > > will address all the cases you mentioned.
> > > > >
> > > >
> > > > Yes, that's what I expect needs to happen, though I haven't looked at the
> > > > details of making sure all the needed data structures are still around. Like
> > > > you, I just had this sense that hvfb_putmem() might need to be moved as
> > > > well, so I tried to produce a failure scenario to prove it, which turned out
> > > > to be easy.
> > > >
> > > > Michael
> > >
> > > I will add this in V2 as well. But I have found an another issue which is
> > > not very frequent.
> > >
> > >
> > > [  176.562153] ------------[ cut here ]------------
> > > [  176.562159] fb0: fb_WARN_ON_ONCE(pageref->page != page)
> > > [  176.562176] WARNING: CPU: 50 PID: 1522 at drivers/video/fbdev/core/fb_defio.c:67
> > > fb_deferred_io_mkwrite+0x215/0x280
> > >
> > > <snip>
> > >
> > > [  176.562258] Call Trace:
> > > [  176.562260]  <TASK>
> > > [  176.562263]  ? show_regs+0x6c/0x80
> > > [  176.562269]  ? __warn+0x8d/0x150
> > > [  176.562273]  ? fb_deferred_io_mkwrite+0x215/0x280
> > > [  176.562275]  ? report_bug+0x182/0x1b0
> > > [  176.562280]  ? handle_bug+0x133/0x1a0
> > > [  176.562283]  ? exc_invalid_op+0x18/0x80
> > > [  176.562284]  ? asm_exc_invalid_op+0x1b/0x20
> > > [  176.562289]  ? fb_deferred_io_mkwrite+0x215/0x280
> > > [  176.562291]  ? fb_deferred_io_mkwrite+0x215/0x280
> > > [  176.562293]  do_page_mkwrite+0x4d/0xb0
> > > [  176.562296]  do_wp_page+0xe8/0xd50
> > > [  176.562300]  ? ___pte_offset_map+0x1c/0x1b0
> > > [  176.562304]  __handle_mm_fault+0xbe1/0x10e0
> > > [  176.562307]  handle_mm_fault+0x17f/0x2e0
> > > [  176.562309]  do_user_addr_fault+0x2d1/0x8d0
> > > [  176.562314]  exc_page_fault+0x85/0x1e0
> > > [  176.562318]  asm_exc_page_fault+0x27/0x30
> > >
> > > Looks this is because driver is unbind still Xorg is trying to write
> > > to memory which is causing some page faults. I have confirmed PID 1522
> > > is of Xorg. I think this is because we need to cancel the framebuffer
> > > deferred work after flushing it.
> >
> > Does this new issue occur even after moving hvfb_putmem()
> > into the destroy() function?
> 
> Unfortunately yes :(
> 
> >                             I'm hoping it doesn't. I've
> > looked at the fb_deferred_io code, and can't quite figure out
> > how that deferred I/O work is supposed to get cancelled. Or
> > maybe it's just not supposed to get started again after the flush.
> >
> 
> I want to understand why cancel_delayed_work_sync was introduce in
> hvfb_suspend and not the flush. Following commit introduced it.
> 
> 382a462217572 ('video: hyperv_fb: Fix hibernation for the deferred IO feature')
> 
> But I agree this need more analysis.
> 
> > If the new issue still happens, that seems like more of a flaw
> > in the fb deferred I/O mechanism not shutting itself down
> > properly.
> >
> 
> As the repro rate is quite low, this will take some effort to get this
> fixed. Shall we take this in a separate patch later ?
> 

Yes, I'm OK with doing a separate patch later. It might turn out
to not be a bug in hyperv_fb anyway.

Michael

^ permalink raw reply

* Re: [PATCH v1 00/13] Convert to use devm_kmemdup_array()
From: Raag Jadav @ 2025-02-24 14:29 UTC (permalink / raw)
  To: Mark Brown
  Cc: perex, tiwai, lgirdwood, deller, andriy.shevchenko, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	linux-sound, linux-fbdev, linux-pm, linux-media, ntb,
	linux-kernel
In-Reply-To: <31f8302b-96be-41f1-9e58-c9f8cb2a9524@sirena.org.uk>

On Mon, Feb 24, 2025 at 01:46:29PM +0000, Mark Brown wrote:
> On Fri, Feb 21, 2025 at 10:23:20PM +0530, Raag Jadav wrote:
> > This series is the second wave of patches to add users of newly introduced
> > devm_kmemdup_array() helper. Original series on [1].
> > 
> > This depends on changes available on immutable tag[2]. Feel free to pick
> > your subsystem patches with it, or share your preferred way to route them.
> 
> Please don't combine patches for multiple subsystems into a single patch
> series, it just makes everything more complex to deal with - it creates
> cross tree issues that wouldn't otherwise exist.

Sure, will split v2 per subsystem.

Raag

^ permalink raw reply

* Re: [PATCH v1 00/13] Convert to use devm_kmemdup_array()
From: Mark Brown @ 2025-02-24 13:46 UTC (permalink / raw)
  To: Raag Jadav
  Cc: perex, tiwai, lgirdwood, deller, andriy.shevchenko, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	linux-sound, linux-fbdev, linux-pm, linux-media, ntb,
	linux-kernel
In-Reply-To: <20250221165333.2780888-1-raag.jadav@intel.com>

[-- Attachment #1: Type: text/plain, Size: 552 bytes --]

On Fri, Feb 21, 2025 at 10:23:20PM +0530, Raag Jadav wrote:
> This series is the second wave of patches to add users of newly introduced
> devm_kmemdup_array() helper. Original series on [1].
> 
> This depends on changes available on immutable tag[2]. Feel free to pick
> your subsystem patches with it, or share your preferred way to route them.

Please don't combine patches for multiple subsystems into a single patch
series, it just makes everything more complex to deal with - it creates
cross tree issues that wouldn't otherwise exist.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Saurabh Singh Sengar @ 2025-02-24 13:29 UTC (permalink / raw)
  To: Michael Kelley
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, deller@gmx.de, akpm@linux-foundation.org,
	linux-hyperv@vger.kernel.org, linux-fbdev@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	ssengar@microsoft.com
In-Reply-To: <SN6PR02MB41571ED5CEA6B91A7F35AC3FD4C02@SN6PR02MB4157.namprd02.prod.outlook.com>

On Mon, Feb 24, 2025 at 12:38:22AM +0000, Michael Kelley wrote:
> From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Sunday, February 23, 2025 6:10 AM
> > 
> > On Sat, Feb 22, 2025 at 08:16:53PM +0000, Michael Kelley wrote:
> > > From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 22, 2025 9:27 AM
> > > >
> 
> [anip]
> 
> > > >
> > > > I had considered moving the entire `hvfb_putmem()` function to `destroy`,
> > > > but I was hesitant for two reasons:
> > > >
> > > >   1. I wasn’t aware of any scenario where this would be useful. However,
> > > >      your explanation has convinced me that it is necessary.
> > > >   2. `hvfb_release_phymem()` relies on the `hdev` pointer, which requires
> > > >      multiple `container_of` operations to derive it from the `info` pointer.
> > > >      I was unsure if the complexity was justified, but it seems worthwhile now.
> > > >
> > > > I will move `hvfb_putmem()` to the `destroy` function in V2, and I hope this
> > > > will address all the cases you mentioned.
> > > >
> > >
> > > Yes, that's what I expect needs to happen, though I haven't looked at the
> > > details of making sure all the needed data structures are still around. Like
> > > you, I just had this sense that hvfb_putmem() might need to be moved as
> > > well, so I tried to produce a failure scenario to prove it, which turned out
> > > to be easy.
> > >
> > > Michael
> > 
> > I will add this in V2 as well. But I have found an another issue which is
> > not very frequent.
> > 
> > 
> > [  176.562153] ------------[ cut here ]------------
> > [  176.562159] fb0: fb_WARN_ON_ONCE(pageref->page != page)
> > [  176.562176] WARNING: CPU: 50 PID: 1522 at drivers/video/fbdev/core/fb_defio.c:67
> > fb_deferred_io_mkwrite+0x215/0x280
> > 
> > <snip>
> > 
> > [  176.562258] Call Trace:
> > [  176.562260]  <TASK>
> > [  176.562263]  ? show_regs+0x6c/0x80
> > [  176.562269]  ? __warn+0x8d/0x150
> > [  176.562273]  ? fb_deferred_io_mkwrite+0x215/0x280
> > [  176.562275]  ? report_bug+0x182/0x1b0
> > [  176.562280]  ? handle_bug+0x133/0x1a0
> > [  176.562283]  ? exc_invalid_op+0x18/0x80
> > [  176.562284]  ? asm_exc_invalid_op+0x1b/0x20
> > [  176.562289]  ? fb_deferred_io_mkwrite+0x215/0x280
> > [  176.562291]  ? fb_deferred_io_mkwrite+0x215/0x280
> > [  176.562293]  do_page_mkwrite+0x4d/0xb0
> > [  176.562296]  do_wp_page+0xe8/0xd50
> > [  176.562300]  ? ___pte_offset_map+0x1c/0x1b0
> > [  176.562304]  __handle_mm_fault+0xbe1/0x10e0
> > [  176.562307]  handle_mm_fault+0x17f/0x2e0
> > [  176.562309]  do_user_addr_fault+0x2d1/0x8d0
> > [  176.562314]  exc_page_fault+0x85/0x1e0
> > [  176.562318]  asm_exc_page_fault+0x27/0x30
> > 
> > Looks this is because driver is unbind still Xorg is trying to write
> > to memory which is causing some page faults. I have confirmed PID 1522
> > is of Xorg. I think this is because we need to cancel the framebuffer
> > deferred work after flushing it.
> 
> Does this new issue occur even after moving hvfb_putmem()
> into the destroy() function?

Unfortunately yes :( 

>                             I'm hoping it doesn't. I've
> looked at the fb_deferred_io code, and can't quite figure out
> how that deferred I/O work is supposed to get cancelled. Or
> maybe it's just not supposed to get started again after the flush.
> 

I want to understand why cancel_delayed_work_sync was introduce in
hvfb_suspend and not the flush. Following commit introduced it.

382a462217572 ('video: hyperv_fb: Fix hibernation for the deferred IO feature')

But I agree this need more analysis.

> If the new issue still happens, that seems like more of a flaw
> in the fb deferred I/O mechanism not shutting itself down
> properly.
> 

As the repro rate is quite low, this will take some effort to get this
fixed. Shall we take this in a separate patch later ?


> Michael
>

<snip> 

^ permalink raw reply

* Re: [PATCH v1 00/13] Convert to use devm_kmemdup_array()
From: Raag Jadav @ 2025-02-24 12:39 UTC (permalink / raw)
  To: perex, tiwai, broonie, lgirdwood, deller, andriy.shevchenko, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer
  Cc: linux-sound, linux-fbdev, linux-pm, linux-media, ntb,
	linux-kernel
In-Reply-To: <20250221165333.2780888-1-raag.jadav@intel.com>

On Fri, Feb 21, 2025 at 10:23:20PM +0530, Raag Jadav wrote:
> This series is the second wave of patches to add users of newly introduced
> devm_kmemdup_array() helper. Original series on [1].
> 
> This depends on changes available on immutable tag[2]. Feel free to pick
> your subsystem patches with it, or share your preferred way to route them.
> 
> [1] https://lore.kernel.org/r/20250212062513.2254767-1-raag.jadav@intel.com
> [2] https://lore.kernel.org/r/Z7cqCaME4LxTTBn6@black.fi.intel.com

Update: Anyone wishing to pull from this series, please use the updated
tag[3]. I'll wait for a few days for review comments and send out a v2
with fbdev fix.

[3] https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com

Apologies for the inconvenience.

Raag

^ permalink raw reply

* Re: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
From: Andy Shevchenko @ 2025-02-24 12:28 UTC (permalink / raw)
  To: Raag Jadav
  Cc: kernel test robot, perex, tiwai, broonie, lgirdwood, deller, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	oe-kbuild-all, linux-sound, linux-fbdev, linux-pm, linux-media,
	ntb, linux-kernel
In-Reply-To: <Z7xkb9m_Qc54znOH@black.fi.intel.com>

On Mon, Feb 24, 2025 at 02:22:07PM +0200, Raag Jadav wrote:
> On Mon, Feb 24, 2025 at 01:41:05PM +0200, Andy Shevchenko wrote:
> > On Mon, Feb 24, 2025 at 01:24:08PM +0200, Raag Jadav wrote:
> > > On Mon, Feb 24, 2025 at 12:58:35PM +0200, Andy Shevchenko wrote:
> > > > On Sat, Feb 22, 2025 at 05:41:24AM +0800, kernel test robot wrote:
> > > > > Hi Raag,
> > > > > 
> > > > > kernel test robot noticed the following build warnings:
> > > > > 
> > > > > [auto build test WARNING on b16e9f8547a328b19af59afc213ce323124d11e9]
> > > > > 
> > > > > url:    https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/ASoC-Intel-avs-use-devm_kmemdup_array/20250222-010322
> > > > > base:   b16e9f8547a328b19af59afc213ce323124d11e9
> > > > > patch link:    https://lore.kernel.org/r/20250221165333.2780888-8-raag.jadav%40intel.com
> > > > > patch subject: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
> > > > > config: arm-randconfig-004-20250222 (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/config)
> > > > > compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
> > > > > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/reproduce)
> > > > > 
> > > > > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > > > > the same patch/commit), kindly add following tags
> > > > > | Reported-by: kernel test robot <lkp@intel.com>
> > > > > | Closes: https://lore.kernel.org/oe-kbuild-all/202502220449.DvJuMgsL-lkp@intel.com/
> > > > > 
> > > > > All warnings (new ones prefixed by >>):
> > > > > 
> > > > >    drivers/video/fbdev/pxafb.c: In function 'pxafb_probe':
> > > > > >> drivers/video/fbdev/pxafb.c:2236:13: warning: unused variable 'i' [-Wunused-variable]
> > > > >     2236 |         int i, irq, ret;
> > > > >          |             ^
> > > > 
> > > > Ragg, please, fix this, and issue a v2 with the link to fixed PR:
> > > > https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com.
> > > 
> > > Sure, but perhaps wait a few days for review comments.
> > 
> > Then perhaps answering to the cover letter that the maintainers who want to
> > apply should use the updated PR?
> 
> Okay, but I'm not sure how this plays out for those who already pulled
> the old PR, i.e. Sebastian.
> 
> Anything to worry about here?

I already informed him and Stephen about a new PR I just sent a few hours ago.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
From: Raag Jadav @ 2025-02-24 12:22 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: kernel test robot, perex, tiwai, broonie, lgirdwood, deller, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	oe-kbuild-all, linux-sound, linux-fbdev, linux-pm, linux-media,
	ntb, linux-kernel
In-Reply-To: <Z7xa0cGZvGxsGCrI@smile.fi.intel.com>

On Mon, Feb 24, 2025 at 01:41:05PM +0200, Andy Shevchenko wrote:
> On Mon, Feb 24, 2025 at 01:24:08PM +0200, Raag Jadav wrote:
> > On Mon, Feb 24, 2025 at 12:58:35PM +0200, Andy Shevchenko wrote:
> > > On Sat, Feb 22, 2025 at 05:41:24AM +0800, kernel test robot wrote:
> > > > Hi Raag,
> > > > 
> > > > kernel test robot noticed the following build warnings:
> > > > 
> > > > [auto build test WARNING on b16e9f8547a328b19af59afc213ce323124d11e9]
> > > > 
> > > > url:    https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/ASoC-Intel-avs-use-devm_kmemdup_array/20250222-010322
> > > > base:   b16e9f8547a328b19af59afc213ce323124d11e9
> > > > patch link:    https://lore.kernel.org/r/20250221165333.2780888-8-raag.jadav%40intel.com
> > > > patch subject: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
> > > > config: arm-randconfig-004-20250222 (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/config)
> > > > compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
> > > > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/reproduce)
> > > > 
> > > > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > > > the same patch/commit), kindly add following tags
> > > > | Reported-by: kernel test robot <lkp@intel.com>
> > > > | Closes: https://lore.kernel.org/oe-kbuild-all/202502220449.DvJuMgsL-lkp@intel.com/
> > > > 
> > > > All warnings (new ones prefixed by >>):
> > > > 
> > > >    drivers/video/fbdev/pxafb.c: In function 'pxafb_probe':
> > > > >> drivers/video/fbdev/pxafb.c:2236:13: warning: unused variable 'i' [-Wunused-variable]
> > > >     2236 |         int i, irq, ret;
> > > >          |             ^
> > > 
> > > Ragg, please, fix this, and issue a v2 with the link to fixed PR:
> > > https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com.
> > 
> > Sure, but perhaps wait a few days for review comments.
> 
> Then perhaps answering to the cover letter that the maintainers who want to
> apply should use the updated PR?

Okay, but I'm not sure how this plays out for those who already pulled
the old PR, i.e. Sebastian.

Anything to worry about here?

Raag

^ permalink raw reply

* [PATCH v3 2/2] mfd: lm3533: convert to use OF
From: Svyatoslav Ryhel @ 2025-02-24 11:48 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Cameron, Lars-Peter Clausen, Pavel Machek,
	Daniel Thompson, Jingoo Han, Helge Deller, Svyatoslav Ryhel,
	Andy Shevchenko, Uwe Kleine-König
  Cc: devicetree, linux-kernel, linux-iio, linux-leds, dri-devel,
	linux-fbdev
In-Reply-To: <20250224114815.146053-1-clamor95@gmail.com>

Remove platform data and fully relay on OF and device tree
parsing and binding devices.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/iio/light/lm3533-als.c      |  40 ++++---
 drivers/leds/leds-lm3533.c          |  46 +++++---
 drivers/mfd/lm3533-core.c           | 159 ++++++++--------------------
 drivers/video/backlight/lm3533_bl.c |  71 ++++++++++---
 include/linux/mfd/lm3533.h          |  35 +-----
 5 files changed, 164 insertions(+), 187 deletions(-)

diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 99f0b903018c..cb52965e93c6 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -16,9 +16,12 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/units.h>
 
 #include <linux/mfd/lm3533.h>
 
@@ -56,6 +59,9 @@ struct lm3533_als {
 
 	atomic_t zone;
 	struct mutex thresh_mutex;
+
+	unsigned pwm_mode:1;		/* PWM input mode (default analog) */
+	u8 r_select;			/* 1 - 127 (ignored in PWM-mode) */
 };
 
 
@@ -753,18 +759,17 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
 	return 0;
 }
 
-static int lm3533_als_setup(struct lm3533_als *als,
-			    const struct lm3533_als_platform_data *pdata)
+static int lm3533_als_setup(struct lm3533_als *als)
 {
 	int ret;
 
-	ret = lm3533_als_set_input_mode(als, pdata->pwm_mode);
+	ret = lm3533_als_set_input_mode(als, als->pwm_mode);
 	if (ret)
 		return ret;
 
 	/* ALS input is always high impedance in PWM-mode. */
-	if (!pdata->pwm_mode) {
-		ret = lm3533_als_set_resistor(als, pdata->r_select);
+	if (!als->pwm_mode) {
+		ret = lm3533_als_set_resistor(als, als->r_select);
 		if (ret)
 			return ret;
 	}
@@ -828,22 +833,16 @@ static const struct iio_info lm3533_als_info = {
 
 static int lm3533_als_probe(struct platform_device *pdev)
 {
-	const struct lm3533_als_platform_data *pdata;
 	struct lm3533 *lm3533;
 	struct lm3533_als *als;
 	struct iio_dev *indio_dev;
+	u32 val;
 	int ret;
 
 	lm3533 = dev_get_drvdata(pdev->dev.parent);
 	if (!lm3533)
 		return -EINVAL;
 
-	pdata = dev_get_platdata(&pdev->dev);
-	if (!pdata) {
-		dev_err(&pdev->dev, "no platform data\n");
-		return -EINVAL;
-	}
-
 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*als));
 	if (!indio_dev)
 		return -ENOMEM;
@@ -864,13 +863,21 @@ static int lm3533_als_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, indio_dev);
 
+	val = 200 * KILO; /* 200kOhm */
+	device_property_read_u32(&pdev->dev, "ti,resistor-value-ohm", &val);
+
+	/* Convert resitance into R_ALS value with 2v / 10uA * R */
+	als->r_select = DIV_ROUND_UP(2 * MICRO, 10 * val);
+
+	als->pwm_mode = device_property_read_bool(&pdev->dev, "ti,pwm-mode");
+
 	if (als->irq) {
 		ret = lm3533_als_setup_irq(als, indio_dev);
 		if (ret)
 			return ret;
 	}
 
-	ret = lm3533_als_setup(als, pdata);
+	ret = lm3533_als_setup(als);
 	if (ret)
 		goto err_free_irq;
 
@@ -907,9 +914,16 @@ static void lm3533_als_remove(struct platform_device *pdev)
 		free_irq(als->irq, indio_dev);
 }
 
+static const struct of_device_id lm3533_als_match_table[] = {
+	{ .compatible = "ti,lm3533-als" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lm3533_als_match_table);
+
 static struct platform_driver lm3533_als_driver = {
 	.driver	= {
 		.name	= "lm3533-als",
+		.of_match_table = lm3533_als_match_table,
 	},
 	.probe		= lm3533_als_probe,
 	.remove		= lm3533_als_remove,
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index 45795f2a1042..6e661a2a540f 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -10,8 +10,10 @@
 #include <linux/module.h>
 #include <linux/leds.h>
 #include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 
 #include <linux/mfd/lm3533.h>
@@ -48,6 +50,9 @@ struct lm3533_led {
 
 	struct mutex mutex;
 	unsigned long flags;
+
+	u16 max_current;		/* 5000 - 29800 uA (800 uA step) */
+	u8 pwm;				/* 0 - 0x3f */
 };
 
 
@@ -632,23 +637,22 @@ static const struct attribute_group *lm3533_led_attribute_groups[] = {
 	NULL
 };
 
-static int lm3533_led_setup(struct lm3533_led *led,
-					struct lm3533_led_platform_data *pdata)
+static int lm3533_led_setup(struct lm3533_led *led)
 {
 	int ret;
 
-	ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current);
+	ret = lm3533_ctrlbank_set_max_current(&led->cb, led->max_current);
 	if (ret)
 		return ret;
 
-	return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm);
+	return lm3533_ctrlbank_set_pwm(&led->cb, led->pwm);
 }
 
 static int lm3533_led_probe(struct platform_device *pdev)
 {
 	struct lm3533 *lm3533;
-	struct lm3533_led_platform_data *pdata;
 	struct lm3533_led *led;
+	u32 val;
 	int ret;
 
 	dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -657,12 +661,6 @@ static int lm3533_led_probe(struct platform_device *pdev)
 	if (!lm3533)
 		return -EINVAL;
 
-	pdata = dev_get_platdata(&pdev->dev);
-	if (!pdata) {
-		dev_err(&pdev->dev, "no platform data\n");
-		return -EINVAL;
-	}
-
 	if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) {
 		dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id);
 		return -EINVAL;
@@ -673,8 +671,11 @@ static int lm3533_led_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	led->lm3533 = lm3533;
-	led->cdev.name = pdata->name;
-	led->cdev.default_trigger = pdata->default_trigger;
+	led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-%d",
+					pdev->name, pdev->id);
+	led->cdev.default_trigger = "none";
+	device_property_read_string(&pdev->dev, "linux,default-trigger",
+				    &led->cdev.default_trigger);
 	led->cdev.brightness_set_blocking = lm3533_led_set;
 	led->cdev.brightness_get = lm3533_led_get;
 	led->cdev.blink_set = lm3533_led_blink_set;
@@ -702,7 +703,17 @@ static int lm3533_led_probe(struct platform_device *pdev)
 
 	led->cb.dev = led->cdev.dev;
 
-	ret = lm3533_led_setup(led, pdata);
+	/* 5000 - 29800 uA (800 uA step) */
+	val = 5000;
+	device_property_read_u32(&pdev->dev, "ti,max-current-microamp", &val);
+	led->max_current = val;
+
+	/* 0 - 0x3f */
+	val = 0;
+	device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &val);
+	led->pwm = val;
+
+	ret = lm3533_led_setup(led);
 	if (ret)
 		goto err_deregister;
 
@@ -739,9 +750,16 @@ static void lm3533_led_shutdown(struct platform_device *pdev)
 	lm3533_led_set(&led->cdev, LED_OFF);		/* disable blink */
 }
 
+static const struct of_device_id lm3533_led_match_table[] = {
+	{ .compatible = "ti,lm3533-leds" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lm3533_led_match_table);
+
 static struct platform_driver lm3533_led_driver = {
 	.driver = {
 		.name = "lm3533-leds",
+		.of_match_table = lm3533_led_match_table,
 	},
 	.probe		= lm3533_led_probe,
 	.remove		= lm3533_led_remove,
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
index 0a2409d00b2e..e1b8fe136af9 100644
--- a/drivers/mfd/lm3533-core.c
+++ b/drivers/mfd/lm3533-core.c
@@ -14,10 +14,13 @@
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
 #include <linux/regmap.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/units.h>
 
 #include <linux/mfd/lm3533.h>
 
@@ -42,41 +45,41 @@
 
 #define LM3533_REG_MAX			0xb2
 
-
-static struct mfd_cell lm3533_als_devs[] = {
+static struct mfd_cell lm3533_child_devices[] = {
 	{
 		.name	= "lm3533-als",
 		.id	= -1,
+		.of_compatible = "ti,lm3533-als",
 	},
-};
-
-static struct mfd_cell lm3533_bl_devs[] = {
 	{
 		.name	= "lm3533-backlight",
 		.id	= 0,
+		.of_compatible = "ti,lm3533-backlight",
 	},
 	{
 		.name	= "lm3533-backlight",
 		.id	= 1,
+		.of_compatible = "ti,lm3533-backlight",
 	},
-};
-
-static struct mfd_cell lm3533_led_devs[] = {
 	{
 		.name	= "lm3533-leds",
 		.id	= 0,
+		.of_compatible = "ti,lm3533-leds",
 	},
 	{
 		.name	= "lm3533-leds",
 		.id	= 1,
+		.of_compatible = "ti,lm3533-leds",
 	},
 	{
 		.name	= "lm3533-leds",
 		.id	= 2,
+		.of_compatible = "ti,lm3533-leds",
 	},
 	{
 		.name	= "lm3533-leds",
 		.id	= 3,
+		.of_compatible = "ti,lm3533-leds",
 	},
 };
 
@@ -376,136 +379,60 @@ static struct attribute_group lm3533_attribute_group = {
 	.attrs		= lm3533_attributes
 };
 
-static int lm3533_device_als_init(struct lm3533 *lm3533)
-{
-	struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
-	int ret;
-
-	if (!pdata->als)
-		return 0;
-
-	lm3533_als_devs[0].platform_data = pdata->als;
-	lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
-
-	ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL,
-			      0, NULL);
-	if (ret) {
-		dev_err(lm3533->dev, "failed to add ALS device\n");
-		return ret;
-	}
-
-	lm3533->have_als = 1;
-
-	return 0;
-}
-
-static int lm3533_device_bl_init(struct lm3533 *lm3533)
-{
-	struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
-	int i;
-	int ret;
-
-	if (!pdata->backlights || pdata->num_backlights == 0)
-		return 0;
-
-	if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs))
-		pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs);
-
-	for (i = 0; i < pdata->num_backlights; ++i) {
-		lm3533_bl_devs[i].platform_data = &pdata->backlights[i];
-		lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]);
-	}
-
-	ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
-			      pdata->num_backlights, NULL, 0, NULL);
-	if (ret) {
-		dev_err(lm3533->dev, "failed to add backlight devices\n");
-		return ret;
-	}
-
-	lm3533->have_backlights = 1;
-
-	return 0;
-}
-
-static int lm3533_device_led_init(struct lm3533 *lm3533)
-{
-	struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
-	int i;
-	int ret;
-
-	if (!pdata->leds || pdata->num_leds == 0)
-		return 0;
-
-	if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs))
-		pdata->num_leds = ARRAY_SIZE(lm3533_led_devs);
-
-	for (i = 0; i < pdata->num_leds; ++i) {
-		lm3533_led_devs[i].platform_data = &pdata->leds[i];
-		lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]);
-	}
-
-	ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
-			      pdata->num_leds, NULL, 0, NULL);
-	if (ret) {
-		dev_err(lm3533->dev, "failed to add LED devices\n");
-		return ret;
-	}
-
-	lm3533->have_leds = 1;
-
-	return 0;
-}
-
-static int lm3533_device_setup(struct lm3533 *lm3533,
-					struct lm3533_platform_data *pdata)
+static int lm3533_device_setup(struct lm3533 *lm3533)
 {
 	int ret;
 
-	ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq);
+	ret = lm3533_set_boost_freq(lm3533, lm3533->boost_freq);
 	if (ret)
 		return ret;
 
-	return lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
+	return lm3533_set_boost_ovp(lm3533, lm3533->boost_ovp);
 }
 
 static int lm3533_device_init(struct lm3533 *lm3533)
 {
-	struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
+	u32 val;
 	int ret;
 
-	dev_dbg(lm3533->dev, "%s\n", __func__);
+	lm3533->hwen = devm_gpiod_get(lm3533->dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(lm3533->hwen))
+		return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen),
+				     "failed to request HWEN GPIO\n");
 
-	if (!pdata) {
-		dev_err(lm3533->dev, "no platform data\n");
-		return -EINVAL;
-	}
+	val = 16 * MICRO; /* 16V */
+	device_property_read_u32(lm3533->dev, "ti,boost-ovp-microvolt", &val);
 
-	lm3533->hwen = devm_gpiod_get(lm3533->dev, NULL, GPIOD_OUT_LOW);
-	if (IS_ERR(lm3533->hwen))
-		return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), "failed to request HWEN GPIO\n");
-	gpiod_set_consumer_name(lm3533->hwen, "lm3533-hwen");
+	/* boost_ovp is defined in microvolts, convert to enum value */
+	lm3533->boost_ovp = val / (8 * MICRO) - 2;
+
+	val = 500 * KILO; /* 500kHz */
+	device_property_read_u32(lm3533->dev, "ti,boost-freq-hz", &val);
+
+	/* boost_freq is defined in Hz, convert to enum value */
+	lm3533->boost_freq = val / (500 * KILO) - 1;
 
 	lm3533_enable(lm3533);
 
-	ret = lm3533_device_setup(lm3533, pdata);
+	ret = lm3533_device_setup(lm3533);
 	if (ret)
 		goto err_disable;
 
-	lm3533_device_als_init(lm3533);
-	lm3533_device_bl_init(lm3533);
-	lm3533_device_led_init(lm3533);
+	ret = devm_mfd_add_devices(lm3533->dev, 0, lm3533_child_devices,
+				   ARRAY_SIZE(lm3533_child_devices), NULL, 0, NULL);
+	if (ret) {
+		dev_err(lm3533->dev, "failed to add MFD devices: %d\n", ret);
+		goto err_disable;
+	}
 
 	ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group);
 	if (ret < 0) {
 		dev_err(lm3533->dev, "failed to create sysfs attributes\n");
-		goto err_unregister;
+		goto err_disable;
 	}
 
 	return 0;
 
-err_unregister:
-	mfd_remove_devices(lm3533->dev);
 err_disable:
 	lm3533_disable(lm3533);
 
@@ -517,8 +444,6 @@ static void lm3533_device_exit(struct lm3533 *lm3533)
 	dev_dbg(lm3533->dev, "%s\n", __func__);
 
 	sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group);
-
-	mfd_remove_devices(lm3533->dev);
 	lm3533_disable(lm3533);
 }
 
@@ -591,6 +516,9 @@ static int lm3533_i2c_probe(struct i2c_client *i2c)
 	lm3533->dev = &i2c->dev;
 	lm3533->irq = i2c->irq;
 
+	i2c->dev.coherent_dma_mask = 0;
+	i2c->dev.dma_mask = &i2c->dev.coherent_dma_mask;
+
 	return lm3533_device_init(lm3533);
 }
 
@@ -603,6 +531,12 @@ static void lm3533_i2c_remove(struct i2c_client *i2c)
 	lm3533_device_exit(lm3533);
 }
 
+static const struct of_device_id lm3533_match_table[] = {
+	{ .compatible = "ti,lm3533" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lm3533_match_table);
+
 static const struct i2c_device_id lm3533_i2c_ids[] = {
 	{ "lm3533" },
 	{ }
@@ -612,6 +546,7 @@ MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
 static struct i2c_driver lm3533_i2c_driver = {
 	.driver = {
 		   .name = "lm3533",
+		   .of_match_table = lm3533_match_table,
 	},
 	.id_table	= lm3533_i2c_ids,
 	.probe		= lm3533_i2c_probe,
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
index babfd3ceec86..0827a5e98dbb 100644
--- a/drivers/video/backlight/lm3533_bl.c
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -9,7 +9,9 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/backlight.h>
 #include <linux/slab.h>
 
@@ -19,6 +21,7 @@
 #define LM3533_HVCTRLBANK_COUNT		2
 #define LM3533_BL_MAX_BRIGHTNESS	255
 
+#define LM3533_REG_OUTPUT_CONF1		0x10
 #define LM3533_REG_CTRLBANK_AB_BCONF	0x1a
 
 
@@ -27,6 +30,11 @@ struct lm3533_bl {
 	struct lm3533_ctrlbank cb;
 	struct backlight_device *bd;
 	int id;
+
+	u16 max_current;		/* 5000 - 29800 uA (800 uA step) */
+	u8 pwm;				/* 0 - 0x3f */
+	bool linear;
+	bool hvled;
 };
 
 
@@ -246,25 +254,40 @@ static struct attribute_group lm3533_bl_attribute_group = {
 	.attrs		= lm3533_bl_attributes
 };
 
-static int lm3533_bl_setup(struct lm3533_bl *bl,
-					struct lm3533_bl_platform_data *pdata)
+static int lm3533_bl_setup(struct lm3533_bl *bl)
 {
+	int id = lm3533_bl_get_ctrlbank_id(bl);
 	int ret;
 
-	ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current);
+	if (bl->linear) {
+		ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF,
+				    BIT(2 * id + 1), BIT(2 * id + 1));
+		if (ret)
+			return ret;
+	}
+
+	if (bl->hvled) {
+		ret = lm3533_update(bl->lm3533, LM3533_REG_OUTPUT_CONF1,
+				    id | id << 1, BIT(0) | BIT(1));
+		if (ret)
+			return ret;
+	}
+
+	ret = lm3533_ctrlbank_set_max_current(&bl->cb, bl->max_current);
 	if (ret)
 		return ret;
 
-	return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm);
+	return lm3533_ctrlbank_set_pwm(&bl->cb, bl->pwm);
 }
 
 static int lm3533_bl_probe(struct platform_device *pdev)
 {
 	struct lm3533 *lm3533;
-	struct lm3533_bl_platform_data *pdata;
 	struct lm3533_bl *bl;
 	struct backlight_device *bd;
 	struct backlight_properties props;
+	char *name;
+	u32 val;
 	int ret;
 
 	dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -273,12 +296,6 @@ static int lm3533_bl_probe(struct platform_device *pdev)
 	if (!lm3533)
 		return -EINVAL;
 
-	pdata = dev_get_platdata(&pdev->dev);
-	if (!pdata) {
-		dev_err(&pdev->dev, "no platform data\n");
-		return -EINVAL;
-	}
-
 	if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) {
 		dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id);
 		return -EINVAL;
@@ -295,13 +312,15 @@ static int lm3533_bl_probe(struct platform_device *pdev)
 	bl->cb.id = lm3533_bl_get_ctrlbank_id(bl);
 	bl->cb.dev = NULL;			/* until registered */
 
+	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-%d", pdev->name, pdev->id);
+
 	memset(&props, 0, sizeof(props));
 	props.type = BACKLIGHT_RAW;
 	props.max_brightness = LM3533_BL_MAX_BRIGHTNESS;
-	props.brightness = pdata->default_brightness;
-	bd = devm_backlight_device_register(&pdev->dev, pdata->name,
-					pdev->dev.parent, bl, &lm3533_bl_ops,
-					&props);
+	props.brightness = LM3533_BL_MAX_BRIGHTNESS;
+	device_property_read_u32(&pdev->dev, "default-brightness", &props.brightness);
+	bd = devm_backlight_device_register(&pdev->dev, name, pdev->dev.parent,
+					    bl, &lm3533_bl_ops, &props);
 	if (IS_ERR(bd)) {
 		dev_err(&pdev->dev, "failed to register backlight device\n");
 		return PTR_ERR(bd);
@@ -320,7 +339,20 @@ static int lm3533_bl_probe(struct platform_device *pdev)
 
 	backlight_update_status(bd);
 
-	ret = lm3533_bl_setup(bl, pdata);
+	/* 5000 - 29800 uA (800 uA step) */
+	val = 5000;
+	device_property_read_u32(&pdev->dev, "ti,max-current-microamp", &val);
+	bl->max_current = val;
+
+	/* 0 - 0x3f */
+	val = 0;
+	device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &val);
+	bl->pwm = val;
+
+	bl->linear = device_property_read_bool(&pdev->dev, "ti,linear-mapping-mode");
+	bl->hvled = device_property_read_bool(&pdev->dev, "ti,hardware-controlled");
+
+	ret = lm3533_bl_setup(bl);
 	if (ret)
 		goto err_sysfs_remove;
 
@@ -381,10 +413,17 @@ static void lm3533_bl_shutdown(struct platform_device *pdev)
 	lm3533_ctrlbank_disable(&bl->cb);
 }
 
+static const struct of_device_id lm3533_bl_match_table[] = {
+	{ .compatible = "ti,lm3533-backlight" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lm3533_bl_match_table);
+
 static struct platform_driver lm3533_bl_driver = {
 	.driver = {
 		.name	= "lm3533-backlight",
 		.pm	= &lm3533_bl_pm_ops,
+		.of_match_table = lm3533_bl_match_table,
 	},
 	.probe		= lm3533_bl_probe,
 	.remove		= lm3533_bl_remove,
diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h
index 69059a7a2ce5..3b28fc0970f6 100644
--- a/include/linux/mfd/lm3533.h
+++ b/include/linux/mfd/lm3533.h
@@ -27,6 +27,9 @@ struct lm3533 {
 	struct gpio_desc *hwen;
 	int irq;
 
+	u32 boost_ovp;
+	u32 boost_freq;
+
 	unsigned have_als:1;
 	unsigned have_backlights:1;
 	unsigned have_leds:1;
@@ -38,25 +41,6 @@ struct lm3533_ctrlbank {
 	int id;
 };
 
-struct lm3533_als_platform_data {
-	unsigned pwm_mode:1;		/* PWM input mode (default analog) */
-	u8 r_select;			/* 1 - 127 (ignored in PWM-mode) */
-};
-
-struct lm3533_bl_platform_data {
-	char *name;
-	u16 max_current;		/* 5000 - 29800 uA (800 uA step) */
-	u8 default_brightness;		/* 0 - 255 */
-	u8 pwm;				/* 0 - 0x3f */
-};
-
-struct lm3533_led_platform_data {
-	char *name;
-	const char *default_trigger;
-	u16 max_current;		/* 5000 - 29800 uA (800 uA step) */
-	u8 pwm;				/* 0 - 0x3f */
-};
-
 enum lm3533_boost_freq {
 	LM3533_BOOST_FREQ_500KHZ,
 	LM3533_BOOST_FREQ_1000KHZ,
@@ -69,19 +53,6 @@ enum lm3533_boost_ovp {
 	LM3533_BOOST_OVP_40V,
 };
 
-struct lm3533_platform_data {
-	enum lm3533_boost_ovp boost_ovp;
-	enum lm3533_boost_freq boost_freq;
-
-	struct lm3533_als_platform_data *als;
-
-	struct lm3533_bl_platform_data *backlights;
-	int num_backlights;
-
-	struct lm3533_led_platform_data *leds;
-	int num_leds;
-};
-
 extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb);
 extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb);
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 1/2] dt-bindings: mfd: Document TI LM3533 MFD
From: Svyatoslav Ryhel @ 2025-02-24 11:48 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Cameron, Lars-Peter Clausen, Pavel Machek,
	Daniel Thompson, Jingoo Han, Helge Deller, Svyatoslav Ryhel,
	Andy Shevchenko, Uwe Kleine-König
  Cc: devicetree, linux-kernel, linux-iio, linux-leds, dri-devel,
	linux-fbdev
In-Reply-To: <20250224114815.146053-1-clamor95@gmail.com>

Add bindings for the LM3533 - a complete power source for backlight, keypad
and indicator LEDs in smartphone handsets. The high-voltage inductive boost
converter provides the power for two series LED strings display backlight
and keypad functions.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../devicetree/bindings/mfd/ti,lm3533.yaml    | 231 ++++++++++++++++++
 1 file changed, 231 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/ti,lm3533.yaml

diff --git a/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml b/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml
new file mode 100644
index 000000000000..c8ac6d4424aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ti,lm3533.yaml
@@ -0,0 +1,231 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/ti,lm3533.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI LM3533 Complete Lighting Power Solution
+
+description: >
+  The LM3533 is a complete power source for backlight, keypad, and indicator LEDs
+  in smartphone handsets. The high-voltage inductive boost converter provides the
+  power for two series LED strings display backlight and keypad functions.
+
+  https://www.ti.com/product/LM3533
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+properties:
+  compatible:
+    const: ti,lm3533
+
+  reg:
+    maxItems: 1
+
+  enable-gpios:
+    description: GPIO to use to enable/disable the backlight (HWEN pin).
+    maxItems: 1
+
+  ti,boost-ovp-microvolt:
+    description:
+      Boost OVP select (16V, 24V, 32V, 40V)
+    enum: [ 16000000, 24000000, 32000000, 40000000 ]
+    default: 16000000
+
+  ti,boost-freq-hz:
+    description:
+      Boost frequency select (500KHz or 1MHz)
+    enum: [ 500000, 1000000 ]
+    default: 500000
+
+  light-sensor:
+    type: object
+    description:
+      Properties for an illumination sensor.
+    additionalProperties: false
+
+    properties:
+      compatible:
+        const: ti,lm3533-als
+
+      ti,resistor-value-ohm:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Internal configuration resister value when ALS is in Analog Sensor
+          mode and PWM mode is disabled.
+        minimum: 1575
+        maximum: 200000
+
+      ti,pwm-mode:
+        type: boolean
+        description:
+          Switch for mode in which ALS is running. If this propertly is set
+          then ALS is running in PWM mode, internal resistor value is set to
+          high-impedance (0) and resistor-value-ohm propertly is ignored.
+
+    required:
+      - compatible
+
+required:
+  - compatible
+  - reg
+  - enable-gpios
+  - light-sensor
+  - backlight-0
+  - backlight-1
+  - led-0
+  - led-1
+  - led-2
+  - led-3
+
+patternProperties:
+  "^backlight-[01]$":
+    type: object
+    description:
+      Properties for a backlight device.
+
+    $ref: /schemas/leds/backlight/common.yaml#
+
+    properties:
+      compatible:
+        const: ti,lm3533-backlight
+
+      default-brightness: true
+
+      ti,max-current-microamp:
+        description:
+          Maximum current in µA with a 800 µA step.
+        enum: [ 5000, 5800, 6600, 7400, 8200, 9000, 9800,
+                10600, 11400, 12200, 13000, 13800, 14600,
+                15400, 16200, 17000, 17800, 18600, 19400,
+                20200, 21000, 21800, 22600, 23400, 24200,
+                25000, 25800, 26600, 27400, 28200, 29000,
+                29800 ]
+        default: 5000
+
+      ti,pwm-config-mask:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Control Bank PWM Configuration Register mask that allows to configure
+          PWM input in Zones 0-4
+          BIT(0) - PWM Input is enabled
+          BIT(1) - PWM Input is enabled in Zone 0
+          BIT(2) - PWM Input is enabled in Zone 1
+          BIT(3) - PWM Input is enabled in Zone 2
+          BIT(4) - PWM Input is enabled in Zone 3
+          BIT(5) - PWM Input is enabled in Zone 4
+
+      ti,linear-mapping-mode:
+        description:
+          Enable linear mapping mode. If disabled, then it will use exponential
+          mapping mode in which the ramp up/down appears to have a more uniform
+          transition to the human eye.
+        type: boolean
+
+      ti,hardware-controlled:
+        description:
+          Each backlight has its own voltage Control Bank (A and B) and there are
+          two HVLED sinks which by default are linked to respective Bank. Setting
+          this property will link both sinks to a Control Bank of backlight where
+          property is defined.
+        type: boolean
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+  "^led-[0-3]$":
+    type: object
+    description:
+      Properties for a led device.
+
+    $ref: /schemas/leds/common.yaml#
+
+    properties:
+      compatible:
+        const: ti,lm3533-leds
+
+      linux,default-trigger: true
+
+      ti,max-current-microamp:
+        description:
+          Maximum current in µA with a 800 µA step.
+        enum: [ 5000, 5800, 6600, 7400, 8200, 9000, 9800,
+                10600, 11400, 12200, 13000, 13800, 14600,
+                15400, 16200, 17000, 17800, 18600, 19400,
+                20200, 21000, 21800, 22600, 23400, 24200,
+                25000, 25800, 26600, 27400, 28200, 29000,
+                29800 ]
+        default: 5000
+
+      ti,pwm-config-mask:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Same descryption and function as for backlight.
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        led-controller@36 {
+            compatible = "ti,lm3533";
+            reg = <0x36>;
+
+            enable-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>;
+
+            ti,boost-ovp-microvolt = <24000000>;
+            ti,boost-freq-hz = <500000>;
+
+            backlight-0 {
+                compatible = "ti,lm3533-backlight";
+
+                ti,max-current-microamp = <23400>;
+                default-brightness = <113>;
+                ti,hardware-controlled;
+            };
+
+            backlight-1 {
+                compatible = "ti,lm3533-backlight";
+                status = "disabled";
+            };
+
+            led-0 {
+                compatible = "ti,lm3533-leds";
+                status = "disabled";
+            };
+
+            led-1 {
+                compatible = "ti,lm3533-leds";
+                status = "disabled";
+            };
+
+            led-2 {
+                compatible = "ti,lm3533-leds";
+                status = "disabled";
+            };
+
+            led-3 {
+                compatible = "ti,lm3533-leds";
+                status = "disabled";
+            };
+
+            light-sensor {
+                compatible = "ti,lm3533-als";
+                status = "disabled";
+            };
+        };
+    };
+...
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 0/2] mfd: lm3533: convert to use OF
From: Svyatoslav Ryhel @ 2025-02-24 11:48 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Cameron, Lars-Peter Clausen, Pavel Machek,
	Daniel Thompson, Jingoo Han, Helge Deller, Svyatoslav Ryhel,
	Andy Shevchenko, Uwe Kleine-König
  Cc: devicetree, linux-kernel, linux-iio, linux-leds, dri-devel,
	linux-fbdev

Add schema and add support for lm3533 mfd to use device tree bindings.

---
Changes on switching from v2 to v3:
- wrapped lines in schema and commit messages arround 80 chars
- removed |
- switched to MFD binding style
- completed binding example
- restored MFD

Changes on switching from v1 to v2:
- added unit seffix where it is suitable
- added vendor prefixes where it is suitable
- light sensor mover out of pattern properties
- added references to common schemas
- added detailed descriptions of properties
- removed platform data use
- devices bind and configure themself entirely
  using device tree
---

Svyatoslav Ryhel (2):
  dt-bindings: mfd: Document TI LM3533 MFD
  mfd: lm3533: convert to use OF

 .../devicetree/bindings/mfd/ti,lm3533.yaml    | 231 ++++++++++++++++++
 drivers/iio/light/lm3533-als.c                |  40 ++-
 drivers/leds/leds-lm3533.c                    |  46 ++--
 drivers/mfd/lm3533-core.c                     | 159 ++++--------
 drivers/video/backlight/lm3533_bl.c           |  71 ++++--
 include/linux/mfd/lm3533.h                    |  35 +--
 6 files changed, 395 insertions(+), 187 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/ti,lm3533.yaml

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
From: Andy Shevchenko @ 2025-02-24 11:41 UTC (permalink / raw)
  To: Raag Jadav
  Cc: kernel test robot, perex, tiwai, broonie, lgirdwood, deller, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	oe-kbuild-all, linux-sound, linux-fbdev, linux-pm, linux-media,
	ntb, linux-kernel
In-Reply-To: <Z7xW2AIz6vUo6mu-@black.fi.intel.com>

On Mon, Feb 24, 2025 at 01:24:08PM +0200, Raag Jadav wrote:
> On Mon, Feb 24, 2025 at 12:58:35PM +0200, Andy Shevchenko wrote:
> > On Sat, Feb 22, 2025 at 05:41:24AM +0800, kernel test robot wrote:
> > > Hi Raag,
> > > 
> > > kernel test robot noticed the following build warnings:
> > > 
> > > [auto build test WARNING on b16e9f8547a328b19af59afc213ce323124d11e9]
> > > 
> > > url:    https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/ASoC-Intel-avs-use-devm_kmemdup_array/20250222-010322
> > > base:   b16e9f8547a328b19af59afc213ce323124d11e9
> > > patch link:    https://lore.kernel.org/r/20250221165333.2780888-8-raag.jadav%40intel.com
> > > patch subject: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
> > > config: arm-randconfig-004-20250222 (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/config)
> > > compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
> > > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/reproduce)
> > > 
> > > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > > the same patch/commit), kindly add following tags
> > > | Reported-by: kernel test robot <lkp@intel.com>
> > > | Closes: https://lore.kernel.org/oe-kbuild-all/202502220449.DvJuMgsL-lkp@intel.com/
> > > 
> > > All warnings (new ones prefixed by >>):
> > > 
> > >    drivers/video/fbdev/pxafb.c: In function 'pxafb_probe':
> > > >> drivers/video/fbdev/pxafb.c:2236:13: warning: unused variable 'i' [-Wunused-variable]
> > >     2236 |         int i, irq, ret;
> > >          |             ^
> > 
> > Ragg, please, fix this, and issue a v2 with the link to fixed PR:
> > https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com.
> 
> Sure, but perhaps wait a few days for review comments.

Then perhaps answering to the cover letter that the maintainers who want to
apply should use the updated PR?

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
From: Raag Jadav @ 2025-02-24 11:24 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: kernel test robot, perex, tiwai, broonie, lgirdwood, deller, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	oe-kbuild-all, linux-sound, linux-fbdev, linux-pm, linux-media,
	ntb, linux-kernel
In-Reply-To: <Z7xQ2y-7U5-OhzhB@smile.fi.intel.com>

On Mon, Feb 24, 2025 at 12:58:35PM +0200, Andy Shevchenko wrote:
> On Sat, Feb 22, 2025 at 05:41:24AM +0800, kernel test robot wrote:
> > Hi Raag,
> > 
> > kernel test robot noticed the following build warnings:
> > 
> > [auto build test WARNING on b16e9f8547a328b19af59afc213ce323124d11e9]
> > 
> > url:    https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/ASoC-Intel-avs-use-devm_kmemdup_array/20250222-010322
> > base:   b16e9f8547a328b19af59afc213ce323124d11e9
> > patch link:    https://lore.kernel.org/r/20250221165333.2780888-8-raag.jadav%40intel.com
> > patch subject: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
> > config: arm-randconfig-004-20250222 (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/config)
> > compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/reproduce)
> > 
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Closes: https://lore.kernel.org/oe-kbuild-all/202502220449.DvJuMgsL-lkp@intel.com/
> > 
> > All warnings (new ones prefixed by >>):
> > 
> >    drivers/video/fbdev/pxafb.c: In function 'pxafb_probe':
> > >> drivers/video/fbdev/pxafb.c:2236:13: warning: unused variable 'i' [-Wunused-variable]
> >     2236 |         int i, irq, ret;
> >          |             ^
> 
> Ragg, please, fix this, and issue a v2 with the link to fixed PR:
> https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com.

Sure, but perhaps wait a few days for review comments.

Raag

^ permalink raw reply

* Re: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
From: Andy Shevchenko @ 2025-02-24 10:58 UTC (permalink / raw)
  To: kernel test robot
  Cc: Raag Jadav, perex, tiwai, broonie, lgirdwood, deller, sre,
	sakari.ailus, mchehab, hverkuil-cisco, jdmason, fancer.lancer,
	oe-kbuild-all, linux-sound, linux-fbdev, linux-pm, linux-media,
	ntb, linux-kernel
In-Reply-To: <202502220449.DvJuMgsL-lkp@intel.com>

On Sat, Feb 22, 2025 at 05:41:24AM +0800, kernel test robot wrote:
> Hi Raag,
> 
> kernel test robot noticed the following build warnings:
> 
> [auto build test WARNING on b16e9f8547a328b19af59afc213ce323124d11e9]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/ASoC-Intel-avs-use-devm_kmemdup_array/20250222-010322
> base:   b16e9f8547a328b19af59afc213ce323124d11e9
> patch link:    https://lore.kernel.org/r/20250221165333.2780888-8-raag.jadav%40intel.com
> patch subject: [PATCH v1 07/13] fbdev: pxafb: use devm_kmemdup_array()
> config: arm-randconfig-004-20250222 (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/config)
> compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250222/202502220449.DvJuMgsL-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202502220449.DvJuMgsL-lkp@intel.com/
> 
> All warnings (new ones prefixed by >>):
> 
>    drivers/video/fbdev/pxafb.c: In function 'pxafb_probe':
> >> drivers/video/fbdev/pxafb.c:2236:13: warning: unused variable 'i' [-Wunused-variable]
>     2236 |         int i, irq, ret;
>          |             ^

Ragg, please, fix this, and issue a v2 with the link to fixed PR:
https://lore.kernel.org/r/Z7xGpz3Q4Zj6YHx7@black.fi.intel.com.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* RE: [PATCH] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Michael Kelley @ 2025-02-24  0:38 UTC (permalink / raw)
  To: Saurabh Singh Sengar
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, deller@gmx.de, akpm@linux-foundation.org,
	linux-hyperv@vger.kernel.org, linux-fbdev@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	ssengar@microsoft.com
In-Reply-To: <20250223140933.GA16428@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>

From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Sunday, February 23, 2025 6:10 AM
> 
> On Sat, Feb 22, 2025 at 08:16:53PM +0000, Michael Kelley wrote:
> > From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 22, 2025 9:27 AM
> > >

[anip]

> > >
> > > I had considered moving the entire `hvfb_putmem()` function to `destroy`,
> > > but I was hesitant for two reasons:
> > >
> > >   1. I wasn’t aware of any scenario where this would be useful. However,
> > >      your explanation has convinced me that it is necessary.
> > >   2. `hvfb_release_phymem()` relies on the `hdev` pointer, which requires
> > >      multiple `container_of` operations to derive it from the `info` pointer.
> > >      I was unsure if the complexity was justified, but it seems worthwhile now.
> > >
> > > I will move `hvfb_putmem()` to the `destroy` function in V2, and I hope this
> > > will address all the cases you mentioned.
> > >
> >
> > Yes, that's what I expect needs to happen, though I haven't looked at the
> > details of making sure all the needed data structures are still around. Like
> > you, I just had this sense that hvfb_putmem() might need to be moved as
> > well, so I tried to produce a failure scenario to prove it, which turned out
> > to be easy.
> >
> > Michael
> 
> I will add this in V2 as well. But I have found an another issue which is
> not very frequent.
> 
> 
> [  176.562153] ------------[ cut here ]------------
> [  176.562159] fb0: fb_WARN_ON_ONCE(pageref->page != page)
> [  176.562176] WARNING: CPU: 50 PID: 1522 at drivers/video/fbdev/core/fb_defio.c:67
> fb_deferred_io_mkwrite+0x215/0x280
> 
> <snip>
> 
> [  176.562258] Call Trace:
> [  176.562260]  <TASK>
> [  176.562263]  ? show_regs+0x6c/0x80
> [  176.562269]  ? __warn+0x8d/0x150
> [  176.562273]  ? fb_deferred_io_mkwrite+0x215/0x280
> [  176.562275]  ? report_bug+0x182/0x1b0
> [  176.562280]  ? handle_bug+0x133/0x1a0
> [  176.562283]  ? exc_invalid_op+0x18/0x80
> [  176.562284]  ? asm_exc_invalid_op+0x1b/0x20
> [  176.562289]  ? fb_deferred_io_mkwrite+0x215/0x280
> [  176.562291]  ? fb_deferred_io_mkwrite+0x215/0x280
> [  176.562293]  do_page_mkwrite+0x4d/0xb0
> [  176.562296]  do_wp_page+0xe8/0xd50
> [  176.562300]  ? ___pte_offset_map+0x1c/0x1b0
> [  176.562304]  __handle_mm_fault+0xbe1/0x10e0
> [  176.562307]  handle_mm_fault+0x17f/0x2e0
> [  176.562309]  do_user_addr_fault+0x2d1/0x8d0
> [  176.562314]  exc_page_fault+0x85/0x1e0
> [  176.562318]  asm_exc_page_fault+0x27/0x30
> 
> Looks this is because driver is unbind still Xorg is trying to write
> to memory which is causing some page faults. I have confirmed PID 1522
> is of Xorg. I think this is because we need to cancel the framebuffer
> deferred work after flushing it.

Does this new issue occur even after moving hvfb_putmem()
into the destroy() function?  I'm hoping it doesn't. I've
looked at the fb_deferred_io code, and can't quite figure out
how that deferred I/O work is supposed to get cancelled. Or
maybe it's just not supposed to get started again after the flush.

If the new issue still happens, that seems like more of a flaw
in the fb deferred I/O mechanism not shutting itself down
properly.

Michael

> 
> After adding the below in hvfb_remove I don't see this issue anymore.
> Although as the issue is not very frequent I am not 100% sure.
> 
> 	cancel_delayed_work_sync(&info->deferred_work);
> 
> If you think this is reasonable I can add this as well in V2.
> 

^ permalink raw reply

* Re: [PATCH] [PATCH v2] staging: sm750fb: Make g_fbmode truly constant
From: kernel test robot @ 2025-02-23 14:57 UTC (permalink / raw)
  To: Madhur Kumar, sudipm.mukherjee, teddy.wang
  Cc: oe-kbuild-all, gregkh, linux-fbdev, linux-staging, linux-kernel,
	Madhur Kumar, Dan Carpenter
In-Reply-To: <20250222201514.15730-1-madhurkumar004@gmail.com>

Hi Madhur,

kernel test robot noticed the following build errors:

[auto build test ERROR on staging/staging-testing]

url:    https://github.com/intel-lab-lkp/linux/commits/Madhur-Kumar/staging-sm750fb-Make-g_fbmode-truly-constant/20250223-041638
base:   staging/staging-testing
patch link:    https://lore.kernel.org/r/20250222201514.15730-1-madhurkumar004%40gmail.com
patch subject: [PATCH] [PATCH v2] staging: sm750fb: Make g_fbmode truly constant
config: sparc64-randconfig-001-20250223 (https://download.01.org/0day-ci/archive/20250223/202502232236.CRIQt4Wb-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250223/202502232236.CRIQt4Wb-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502232236.CRIQt4Wb-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/staging/sm750fb/sm750.c: In function 'lynxfb_set_fbinfo':
>> drivers/staging/sm750fb/sm750.c:788:33: error: assignment of read-only location 'g_fbmode[index]'
     788 |                 g_fbmode[index] = g_def_fbmode;
         |                                 ^
   drivers/staging/sm750fb/sm750.c:790:41: error: assignment of read-only location 'g_fbmode[index]'
     790 |                         g_fbmode[index] = g_fbmode[0];
         |                                         ^
   drivers/staging/sm750fb/sm750.c: In function 'sm750fb_setup':
>> drivers/staging/sm750fb/sm750.c:951:45: error: assignment of read-only location 'g_fbmode[0]'
     951 |                                 g_fbmode[0] = opt;
         |                                             ^
   drivers/staging/sm750fb/sm750.c:955:45: error: assignment of read-only location 'g_fbmode[1]'
     955 |                                 g_fbmode[1] = opt;
         |                                             ^


vim +788 drivers/staging/sm750fb/sm750.c

81dee67e215b23 Sudip Mukherjee         2015-03-03  715  
81dee67e215b23 Sudip Mukherjee         2015-03-03  716  static int lynxfb_set_fbinfo(struct fb_info *info, int index)
81dee67e215b23 Sudip Mukherjee         2015-03-03  717  {
81dee67e215b23 Sudip Mukherjee         2015-03-03  718  	int i;
81dee67e215b23 Sudip Mukherjee         2015-03-03  719  	struct lynxfb_par *par;
e359b6a863e19f Mike Rapoport           2015-10-26  720  	struct sm750_dev *sm750_dev;
81dee67e215b23 Sudip Mukherjee         2015-03-03  721  	struct lynxfb_crtc *crtc;
81dee67e215b23 Sudip Mukherjee         2015-03-03  722  	struct lynxfb_output *output;
81dee67e215b23 Sudip Mukherjee         2015-03-03  723  	struct fb_var_screeninfo *var;
81dee67e215b23 Sudip Mukherjee         2015-03-03  724  	struct fb_fix_screeninfo *fix;
81dee67e215b23 Sudip Mukherjee         2015-03-03  725  
81dee67e215b23 Sudip Mukherjee         2015-03-03  726  	const struct fb_videomode *pdb[] = {
81dee67e215b23 Sudip Mukherjee         2015-03-03  727  		lynx750_ext, NULL, vesa_modes,
81dee67e215b23 Sudip Mukherjee         2015-03-03  728  	};
81dee67e215b23 Sudip Mukherjee         2015-03-03  729  	int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
d5d66cfea2ca28 Kelsey Skunberg         2019-04-27  730  	static const char * const mdb_desc[] = {
81dee67e215b23 Sudip Mukherjee         2015-03-03  731  		"driver prepared modes",
81dee67e215b23 Sudip Mukherjee         2015-03-03  732  		"kernel prepared default modedb",
81dee67e215b23 Sudip Mukherjee         2015-03-03  733  		"kernel HELPERS prepared vesa_modes",
81dee67e215b23 Sudip Mukherjee         2015-03-03  734  	};
81dee67e215b23 Sudip Mukherjee         2015-03-03  735  
70407df77665c0 Michel von Czettritz    2015-03-26  736  	static const char *fixId[2] = {
81dee67e215b23 Sudip Mukherjee         2015-03-03  737  		"sm750_fb1", "sm750_fb2",
81dee67e215b23 Sudip Mukherjee         2015-03-03  738  	};
81dee67e215b23 Sudip Mukherjee         2015-03-03  739  
81dee67e215b23 Sudip Mukherjee         2015-03-03  740  	int ret, line_length;
81dee67e215b23 Sudip Mukherjee         2015-03-03  741  
81dee67e215b23 Sudip Mukherjee         2015-03-03  742  	ret = 0;
81dee67e215b23 Sudip Mukherjee         2015-03-03  743  	par = (struct lynxfb_par *)info->par;
e359b6a863e19f Mike Rapoport           2015-10-26  744  	sm750_dev = par->dev;
81dee67e215b23 Sudip Mukherjee         2015-03-03  745  	crtc = &par->crtc;
81dee67e215b23 Sudip Mukherjee         2015-03-03  746  	output = &par->output;
81dee67e215b23 Sudip Mukherjee         2015-03-03  747  	var = &info->var;
81dee67e215b23 Sudip Mukherjee         2015-03-03  748  	fix = &info->fix;
81dee67e215b23 Sudip Mukherjee         2015-03-03  749  
81dee67e215b23 Sudip Mukherjee         2015-03-03  750  	/* set index */
81dee67e215b23 Sudip Mukherjee         2015-03-03  751  	par->index = index;
81dee67e215b23 Sudip Mukherjee         2015-03-03  752  	output->channel = &crtc->channel;
81dee67e215b23 Sudip Mukherjee         2015-03-03  753  	sm750fb_set_drv(par);
81dee67e215b23 Sudip Mukherjee         2015-03-03  754  
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  755  	/*
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  756  	 * set current cursor variable and proc pointer,
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  757  	 * must be set after crtc member initialized
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  758  	 */
fdc234d85210d9 Benjamin Philip         2021-07-28  759  	crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
e359b6a863e19f Mike Rapoport           2015-10-26  760  	crtc->cursor.mmio = sm750_dev->pvReg +
e359b6a863e19f Mike Rapoport           2015-10-26  761  		0x800f0 + (int)crtc->channel * 0x140;
81dee67e215b23 Sudip Mukherjee         2015-03-03  762  
81dee67e215b23 Sudip Mukherjee         2015-03-03  763  	pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
cd33da26036ea5 Christopher Carbone     2022-08-23  764  	crtc->cursor.max_h = 64;
cd33da26036ea5 Christopher Carbone     2022-08-23  765  	crtc->cursor.max_w = 64;
39f9137268ee3d Benjamin Philip         2021-07-26  766  	crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
e359b6a863e19f Mike Rapoport           2015-10-26  767  	crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
81dee67e215b23 Sudip Mukherjee         2015-03-03  768  
3de08a2d14ff8c Lorenzo Stoakes         2015-03-20  769  	memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
f7c8a046577e09 Thomas Zimmermann       2023-11-27  770  	if (!g_hwcursor)
52d0744d751d8f Arnd Bergmann           2016-11-09  771  		sm750_hw_cursor_disable(&crtc->cursor);
81dee67e215b23 Sudip Mukherjee         2015-03-03  772  
81dee67e215b23 Sudip Mukherjee         2015-03-03  773  	/* set info->fbops, must be set before fb_find_mode */
e359b6a863e19f Mike Rapoport           2015-10-26  774  	if (!sm750_dev->accel_off) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  775  		/* use 2d acceleration */
f7c8a046577e09 Thomas Zimmermann       2023-11-27  776  		if (!g_hwcursor)
f7c8a046577e09 Thomas Zimmermann       2023-11-27  777  			info->fbops = &lynxfb_ops_accel;
f7c8a046577e09 Thomas Zimmermann       2023-11-27  778  		else
f7c8a046577e09 Thomas Zimmermann       2023-11-27  779  			info->fbops = &lynxfb_ops_accel_with_cursor;
f7c8a046577e09 Thomas Zimmermann       2023-11-27  780  	} else {
f7c8a046577e09 Thomas Zimmermann       2023-11-27  781  		if (!g_hwcursor)
81dee67e215b23 Sudip Mukherjee         2015-03-03  782  			info->fbops = &lynxfb_ops;
f7c8a046577e09 Thomas Zimmermann       2023-11-27  783  		else
f7c8a046577e09 Thomas Zimmermann       2023-11-27  784  			info->fbops = &lynxfb_ops_with_cursor;
f7c8a046577e09 Thomas Zimmermann       2023-11-27  785  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  786  
81dee67e215b23 Sudip Mukherjee         2015-03-03  787  	if (!g_fbmode[index]) {
81dee67e215b23 Sudip Mukherjee         2015-03-03 @788  		g_fbmode[index] = g_def_fbmode;
81dee67e215b23 Sudip Mukherjee         2015-03-03  789  		if (index)
81dee67e215b23 Sudip Mukherjee         2015-03-03  790  			g_fbmode[index] = g_fbmode[0];
81dee67e215b23 Sudip Mukherjee         2015-03-03  791  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  792  
81dee67e215b23 Sudip Mukherjee         2015-03-03  793  	for (i = 0; i < 3; i++) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  794  		ret = fb_find_mode(var, info, g_fbmode[index],
81dee67e215b23 Sudip Mukherjee         2015-03-03  795  				   pdb[i], cdb[i], NULL, 8);
81dee67e215b23 Sudip Mukherjee         2015-03-03  796  
81dee67e215b23 Sudip Mukherjee         2015-03-03  797  		if (ret == 1) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  798  			pr_info("success! use specified mode:%s in %s\n",
81dee67e215b23 Sudip Mukherjee         2015-03-03  799  				g_fbmode[index],
81dee67e215b23 Sudip Mukherjee         2015-03-03  800  				mdb_desc[i]);
81dee67e215b23 Sudip Mukherjee         2015-03-03  801  			break;
81dee67e215b23 Sudip Mukherjee         2015-03-03  802  		} else if (ret == 2) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  803  			pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
81dee67e215b23 Sudip Mukherjee         2015-03-03  804  				g_fbmode[index],
81dee67e215b23 Sudip Mukherjee         2015-03-03  805  				mdb_desc[i]);
81dee67e215b23 Sudip Mukherjee         2015-03-03  806  			break;
81dee67e215b23 Sudip Mukherjee         2015-03-03  807  		} else if (ret == 3) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  808  			pr_warn("wanna use default mode\n");
4bd9503d0becdb Michel von Czettritz    2015-03-26  809  			/*break;*/
81dee67e215b23 Sudip Mukherjee         2015-03-03  810  		} else if (ret == 4) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  811  			pr_warn("fall back to any valid mode\n");
81dee67e215b23 Sudip Mukherjee         2015-03-03  812  		} else {
3318bb5e945f70 Michel von Czettritz    2015-03-26  813  			pr_warn("ret = %d,fb_find_mode failed,with %s\n",
3318bb5e945f70 Michel von Czettritz    2015-03-26  814  				ret,
3318bb5e945f70 Michel von Czettritz    2015-03-26  815  				mdb_desc[i]);
81dee67e215b23 Sudip Mukherjee         2015-03-03  816  		}
81dee67e215b23 Sudip Mukherjee         2015-03-03  817  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  818  
81dee67e215b23 Sudip Mukherjee         2015-03-03  819  	/* some member of info->var had been set by fb_find_mode */
81dee67e215b23 Sudip Mukherjee         2015-03-03  820  
271dbae3c6a1da Prasant Jalan           2017-04-01  821  	pr_info("Member of info->var is :\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  822  		"xres=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  823  		"yres=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  824  		"xres_virtual=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  825  		"yres_virtual=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  826  		"xoffset=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  827  		"yoffset=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  828  		"bits_per_pixel=%d\n"
271dbae3c6a1da Prasant Jalan           2017-04-01  829  		" ...\n",
3318bb5e945f70 Michel von Czettritz    2015-03-26  830  		var->xres,
3318bb5e945f70 Michel von Czettritz    2015-03-26  831  		var->yres,
3318bb5e945f70 Michel von Czettritz    2015-03-26  832  		var->xres_virtual,
3318bb5e945f70 Michel von Czettritz    2015-03-26  833  		var->yres_virtual,
3318bb5e945f70 Michel von Czettritz    2015-03-26  834  		var->xoffset,
3318bb5e945f70 Michel von Czettritz    2015-03-26  835  		var->yoffset,
3318bb5e945f70 Michel von Czettritz    2015-03-26  836  		var->bits_per_pixel);
81dee67e215b23 Sudip Mukherjee         2015-03-03  837  
81dee67e215b23 Sudip Mukherjee         2015-03-03  838  	/* set par */
81dee67e215b23 Sudip Mukherjee         2015-03-03  839  	par->info = info;
81dee67e215b23 Sudip Mukherjee         2015-03-03  840  
81dee67e215b23 Sudip Mukherjee         2015-03-03  841  	/* set info */
e3a3f9f5123683 Mike Rapoport           2015-10-26  842  	line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
e3a3f9f5123683 Mike Rapoport           2015-10-26  843  			    crtc->line_pad);
81dee67e215b23 Sudip Mukherjee         2015-03-03  844  
81dee67e215b23 Sudip Mukherjee         2015-03-03  845  	info->pseudo_palette = &par->pseudo_palette[0];
cc59bde1c920ab Benjamin Philip         2021-07-28  846  	info->screen_base = crtc->v_screen;
81dee67e215b23 Sudip Mukherjee         2015-03-03  847  	pr_debug("screen_base vaddr = %p\n", info->screen_base);
81dee67e215b23 Sudip Mukherjee         2015-03-03  848  	info->screen_size = line_length * var->yres_virtual;
81dee67e215b23 Sudip Mukherjee         2015-03-03  849  
81dee67e215b23 Sudip Mukherjee         2015-03-03  850  	/* set info->fix */
81dee67e215b23 Sudip Mukherjee         2015-03-03  851  	fix->type = FB_TYPE_PACKED_PIXELS;
81dee67e215b23 Sudip Mukherjee         2015-03-03  852  	fix->type_aux = 0;
81dee67e215b23 Sudip Mukherjee         2015-03-03  853  	fix->xpanstep = crtc->xpanstep;
81dee67e215b23 Sudip Mukherjee         2015-03-03  854  	fix->ypanstep = crtc->ypanstep;
81dee67e215b23 Sudip Mukherjee         2015-03-03  855  	fix->ywrapstep = crtc->ywrapstep;
81dee67e215b23 Sudip Mukherjee         2015-03-03  856  	fix->accel = FB_ACCEL_SMI;
81dee67e215b23 Sudip Mukherjee         2015-03-03  857  
9c15db83a86bf8 Kumar Kartikeya Dwivedi 2021-01-31  858  	strscpy(fix->id, fixId[index], sizeof(fix->id));
81dee67e215b23 Sudip Mukherjee         2015-03-03  859  
fdc234d85210d9 Benjamin Philip         2021-07-28  860  	fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;
81dee67e215b23 Sudip Mukherjee         2015-03-03  861  	pr_info("fix->smem_start = %lx\n", fix->smem_start);
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  862  	/*
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  863  	 * according to mmap experiment from user space application,
81dee67e215b23 Sudip Mukherjee         2015-03-03  864  	 * fix->mmio_len should not larger than virtual size
81dee67e215b23 Sudip Mukherjee         2015-03-03  865  	 * (xres_virtual x yres_virtual x ByPP)
81dee67e215b23 Sudip Mukherjee         2015-03-03  866  	 * Below line maybe buggy when user mmap fb dev node and write
81dee67e215b23 Sudip Mukherjee         2015-03-03  867  	 * data into the bound over virtual size
d11ac7cbcc266c Sudip Mukherjee         2015-08-07  868  	 */
81dee67e215b23 Sudip Mukherjee         2015-03-03  869  	fix->smem_len = crtc->vidmem_size;
81dee67e215b23 Sudip Mukherjee         2015-03-03  870  	pr_info("fix->smem_len = %x\n", fix->smem_len);
81dee67e215b23 Sudip Mukherjee         2015-03-03  871  	info->screen_size = fix->smem_len;
81dee67e215b23 Sudip Mukherjee         2015-03-03  872  	fix->line_length = line_length;
e359b6a863e19f Mike Rapoport           2015-10-26  873  	fix->mmio_start = sm750_dev->vidreg_start;
81dee67e215b23 Sudip Mukherjee         2015-03-03  874  	pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
e359b6a863e19f Mike Rapoport           2015-10-26  875  	fix->mmio_len = sm750_dev->vidreg_size;
81dee67e215b23 Sudip Mukherjee         2015-03-03  876  	pr_info("fix->mmio_len = %x\n", fix->mmio_len);
b610e1193a917f Matej Dujava            2020-04-30  877  
b610e1193a917f Matej Dujava            2020-04-30  878  	lynxfb_set_visual_mode(info);
81dee67e215b23 Sudip Mukherjee         2015-03-03  879  
81dee67e215b23 Sudip Mukherjee         2015-03-03  880  	/* set var */
81dee67e215b23 Sudip Mukherjee         2015-03-03  881  	var->activate = FB_ACTIVATE_NOW;
81dee67e215b23 Sudip Mukherjee         2015-03-03  882  	var->accel_flags = 0;
81dee67e215b23 Sudip Mukherjee         2015-03-03  883  	var->vmode = FB_VMODE_NONINTERLACED;
81dee67e215b23 Sudip Mukherjee         2015-03-03  884  
81dee67e215b23 Sudip Mukherjee         2015-03-03  885  	pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
81dee67e215b23 Sudip Mukherjee         2015-03-03  886  		 info->cmap.start, info->cmap.len,
81dee67e215b23 Sudip Mukherjee         2015-03-03  887  		 info->cmap.red, info->cmap.green, info->cmap.blue,
81dee67e215b23 Sudip Mukherjee         2015-03-03  888  		 info->cmap.transp);
81dee67e215b23 Sudip Mukherjee         2015-03-03  889  
61c507cf652da1 Michel von Czettritz    2015-03-26  890  	ret = fb_alloc_cmap(&info->cmap, 256, 0);
61c507cf652da1 Michel von Czettritz    2015-03-26  891  	if (ret < 0) {
008272072d61a8 Masanari Iida           2015-05-28  892  		pr_err("Could not allocate memory for cmap.\n");
81dee67e215b23 Sudip Mukherjee         2015-03-03  893  		goto exit;
81dee67e215b23 Sudip Mukherjee         2015-03-03  894  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  895  
81dee67e215b23 Sudip Mukherjee         2015-03-03  896  	pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
81dee67e215b23 Sudip Mukherjee         2015-03-03  897  		 info->cmap.start, info->cmap.len,
81dee67e215b23 Sudip Mukherjee         2015-03-03  898  		 info->cmap.red, info->cmap.green, info->cmap.blue,
81dee67e215b23 Sudip Mukherjee         2015-03-03  899  		 info->cmap.transp);
81dee67e215b23 Sudip Mukherjee         2015-03-03  900  
81dee67e215b23 Sudip Mukherjee         2015-03-03  901  exit:
81dee67e215b23 Sudip Mukherjee         2015-03-03  902  	lynxfb_ops_check_var(var, info);
81dee67e215b23 Sudip Mukherjee         2015-03-03  903  	return ret;
81dee67e215b23 Sudip Mukherjee         2015-03-03  904  }
81dee67e215b23 Sudip Mukherjee         2015-03-03  905  
81dee67e215b23 Sudip Mukherjee         2015-03-03  906  /*	chip specific g_option configuration routine */
700591a9adc8b1 Mike Rapoport           2015-10-26  907  static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
81dee67e215b23 Sudip Mukherjee         2015-03-03  908  {
81dee67e215b23 Sudip Mukherjee         2015-03-03  909  	char *opt;
81dee67e215b23 Sudip Mukherjee         2015-03-03  910  	int swap;
81dee67e215b23 Sudip Mukherjee         2015-03-03  911  
81dee67e215b23 Sudip Mukherjee         2015-03-03  912  	swap = 0;
81dee67e215b23 Sudip Mukherjee         2015-03-03  913  
1757d106a9ce8c Mike Rapoport           2015-10-26  914  	sm750_dev->initParm.chip_clk = 0;
1757d106a9ce8c Mike Rapoport           2015-10-26  915  	sm750_dev->initParm.mem_clk = 0;
1757d106a9ce8c Mike Rapoport           2015-10-26  916  	sm750_dev->initParm.master_clk = 0;
1757d106a9ce8c Mike Rapoport           2015-10-26  917  	sm750_dev->initParm.powerMode = 0;
1757d106a9ce8c Mike Rapoport           2015-10-26  918  	sm750_dev->initParm.setAllEngOff = 0;
1757d106a9ce8c Mike Rapoport           2015-10-26  919  	sm750_dev->initParm.resetMemory = 1;
81dee67e215b23 Sudip Mukherjee         2015-03-03  920  
81dee67e215b23 Sudip Mukherjee         2015-03-03  921  	/* defaultly turn g_hwcursor on for both view */
81dee67e215b23 Sudip Mukherjee         2015-03-03  922  	g_hwcursor = 3;
81dee67e215b23 Sudip Mukherjee         2015-03-03  923  
81dee67e215b23 Sudip Mukherjee         2015-03-03  924  	if (!src || !*src) {
c56de0967a658c Elise Lennion           2016-10-31  925  		dev_warn(&sm750_dev->pdev->dev, "no specific g_option.\n");
81dee67e215b23 Sudip Mukherjee         2015-03-03  926  		goto NO_PARAM;
81dee67e215b23 Sudip Mukherjee         2015-03-03  927  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  928  
0fa96e39279988 Sudip Mukherjee         2015-03-10  929  	while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
c56de0967a658c Elise Lennion           2016-10-31  930  		dev_info(&sm750_dev->pdev->dev, "opt=%s\n", opt);
c56de0967a658c Elise Lennion           2016-10-31  931  		dev_info(&sm750_dev->pdev->dev, "src=%s\n", src);
81dee67e215b23 Sudip Mukherjee         2015-03-03  932  
144634a6b42146 Katie Dunne             2017-02-19  933  		if (!strncmp(opt, "swap", strlen("swap"))) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  934  			swap = 1;
144634a6b42146 Katie Dunne             2017-02-19  935  		} else if (!strncmp(opt, "nocrt", strlen("nocrt"))) {
1757d106a9ce8c Mike Rapoport           2015-10-26  936  			sm750_dev->nocrt = 1;
144634a6b42146 Katie Dunne             2017-02-19  937  		} else if (!strncmp(opt, "36bit", strlen("36bit"))) {
1757d106a9ce8c Mike Rapoport           2015-10-26  938  			sm750_dev->pnltype = sm750_doubleTFT;
144634a6b42146 Katie Dunne             2017-02-19  939  		} else if (!strncmp(opt, "18bit", strlen("18bit"))) {
1757d106a9ce8c Mike Rapoport           2015-10-26  940  			sm750_dev->pnltype = sm750_dualTFT;
144634a6b42146 Katie Dunne             2017-02-19  941  		} else if (!strncmp(opt, "24bit", strlen("24bit"))) {
1757d106a9ce8c Mike Rapoport           2015-10-26  942  			sm750_dev->pnltype = sm750_24TFT;
144634a6b42146 Katie Dunne             2017-02-19  943  		} else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  944  			g_hwcursor &= ~0x1;
144634a6b42146 Katie Dunne             2017-02-19  945  		} else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  946  			g_hwcursor &= ~0x2;
144634a6b42146 Katie Dunne             2017-02-19  947  		} else if (!strncmp(opt, "nohwc", strlen("nohwc"))) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  948  			g_hwcursor = 0;
144634a6b42146 Katie Dunne             2017-02-19  949  		} else {
81dee67e215b23 Sudip Mukherjee         2015-03-03  950  			if (!g_fbmode[0]) {
81dee67e215b23 Sudip Mukherjee         2015-03-03 @951  				g_fbmode[0] = opt;
cee9ba1c30d051 Abdul Rauf              2017-01-08  952  				dev_info(&sm750_dev->pdev->dev,
cee9ba1c30d051 Abdul Rauf              2017-01-08  953  					 "find fbmode0 : %s\n", g_fbmode[0]);
81dee67e215b23 Sudip Mukherjee         2015-03-03  954  			} else if (!g_fbmode[1]) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  955  				g_fbmode[1] = opt;
cee9ba1c30d051 Abdul Rauf              2017-01-08  956  				dev_info(&sm750_dev->pdev->dev,
cee9ba1c30d051 Abdul Rauf              2017-01-08  957  					 "find fbmode1 : %s\n", g_fbmode[1]);
81dee67e215b23 Sudip Mukherjee         2015-03-03  958  			} else {
c56de0967a658c Elise Lennion           2016-10-31  959  				dev_warn(&sm750_dev->pdev->dev, "How many view you wann set?\n");
81dee67e215b23 Sudip Mukherjee         2015-03-03  960  			}
81dee67e215b23 Sudip Mukherjee         2015-03-03  961  		}
81dee67e215b23 Sudip Mukherjee         2015-03-03  962  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  963  
81dee67e215b23 Sudip Mukherjee         2015-03-03  964  NO_PARAM:
e359b6a863e19f Mike Rapoport           2015-10-26  965  	if (sm750_dev->revid != SM750LE_REVISION_ID) {
a3f92cc94c6126 Mike Rapoport           2016-01-17  966  		if (sm750_dev->fb_count > 1) {
81dee67e215b23 Sudip Mukherjee         2015-03-03  967  			if (swap)
1757d106a9ce8c Mike Rapoport           2015-10-26  968  				sm750_dev->dataflow = sm750_dual_swap;
81dee67e215b23 Sudip Mukherjee         2015-03-03  969  			else
1757d106a9ce8c Mike Rapoport           2015-10-26  970  				sm750_dev->dataflow = sm750_dual_normal;
81dee67e215b23 Sudip Mukherjee         2015-03-03  971  		} else {
81dee67e215b23 Sudip Mukherjee         2015-03-03  972  			if (swap)
1757d106a9ce8c Mike Rapoport           2015-10-26  973  				sm750_dev->dataflow = sm750_simul_sec;
81dee67e215b23 Sudip Mukherjee         2015-03-03  974  			else
1757d106a9ce8c Mike Rapoport           2015-10-26  975  				sm750_dev->dataflow = sm750_simul_pri;
81dee67e215b23 Sudip Mukherjee         2015-03-03  976  		}
81dee67e215b23 Sudip Mukherjee         2015-03-03  977  	} else {
81dee67e215b23 Sudip Mukherjee         2015-03-03  978  		/* SM750LE only have one crt channel */
1757d106a9ce8c Mike Rapoport           2015-10-26  979  		sm750_dev->dataflow = sm750_simul_sec;
81dee67e215b23 Sudip Mukherjee         2015-03-03  980  		/* sm750le do not have complex attributes */
1757d106a9ce8c Mike Rapoport           2015-10-26  981  		sm750_dev->nocrt = 0;
81dee67e215b23 Sudip Mukherjee         2015-03-03  982  	}
81dee67e215b23 Sudip Mukherjee         2015-03-03  983  }
81dee67e215b23 Sudip Mukherjee         2015-03-03  984  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Saurabh Singh Sengar @ 2025-02-23 14:09 UTC (permalink / raw)
  To: Michael Kelley
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, deller@gmx.de, akpm@linux-foundation.org,
	linux-hyperv@vger.kernel.org, linux-fbdev@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	ssengar@microsoft.com
In-Reply-To: <SN6PR02MB4157F6CF7CACF45C398933C4D4C62@SN6PR02MB4157.namprd02.prod.outlook.com>

On Sat, Feb 22, 2025 at 08:16:53PM +0000, Michael Kelley wrote:
> From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 22, 2025 9:27 AM
> > 
> > On Wed, Feb 19, 2025 at 05:22:36AM +0000, Michael Kelley wrote:
> > > From: Saurabh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 15,
> > 2025 1:21 AM
> > > >
> > > > When a Hyper-V framebuffer device is unbind, hyperv_fb driver tries to
> > > > release the framebuffer forcefully. If this framebuffer is in use it
> > > > produce the following WARN and hence this framebuffer is never released.
> > > >
> > > > [   44.111220] WARNING: CPU: 35 PID: 1882 at drivers/video/fbdev/core/fb_info.c:70
> > framebuffer_release+0x2c/0x40
> > > > < snip >
> > > > [   44.111289] Call Trace:
> > > > [   44.111290]  <TASK>
> > > > [   44.111291]  ? show_regs+0x6c/0x80
> > > > [   44.111295]  ? __warn+0x8d/0x150
> > > > [   44.111298]  ? framebuffer_release+0x2c/0x40
> > > > [   44.111300]  ? report_bug+0x182/0x1b0
> > > > [   44.111303]  ? handle_bug+0x6e/0xb0
> > > > [   44.111306]  ? exc_invalid_op+0x18/0x80
> > > > [   44.111308]  ? asm_exc_invalid_op+0x1b/0x20
> > > > [   44.111311]  ? framebuffer_release+0x2c/0x40
> > > > [   44.111313]  ? hvfb_remove+0x86/0xa0 [hyperv_fb]
> > > > [   44.111315]  vmbus_remove+0x24/0x40 [hv_vmbus]
> > > > [   44.111323]  device_remove+0x40/0x80
> > > > [   44.111325]  device_release_driver_internal+0x20b/0x270
> > > > [   44.111327]  ? bus_find_device+0xb3/0xf0
> > > >
> > > > Fix this by moving the release of framebuffer to fb_ops.fb_destroy function
> > > > so that framebuffer framework handles it gracefully
> > >
> > > These changes look good for solving the specific problem where
> > > the reference count WARN is produced. But there is another
> > > problem of the same type that happens when doing unbind
> > > of a hyperv_fb device that is in use (i.e., /dev/fb0 is open and
> > > mmap'ed by some user space program).
> > >
> > > For this additional problem, there are three sub-cases,
> > > depending on what memory gets mmap'ed into user space.
> > > Two of the three sub-cases have a problem.
> > >
> > > 1) When Hyper-V FB uses deferred I/O, the vmalloc dio memory
> > > is what get mapped into user space. When hyperv_fb is unbound,
> > > the vmalloc dio memory is freed. But the memory doesn't actually
> > > get freed if it is still mmap'ed into user space. The deferred I/O
> > > mechanism is stopped, but user space can keep writing to the
> > > memory even though the pixels don't get copied to the actual
> > > framebuffer any longer.  When the user space program terminates
> > > (or unmaps the memory), the memory will be freed. So this case
> > > is OK, though perhaps a bit dubious.
> > >
> > > 2) When Hyper-V FB is in a Gen 1 VM, and the frame buffer size
> > > is <= 4 MiB, a normal kernel allocation is used for the
> > > memory that is mmap'ed to user space. If this memory
> > > is freed when hyperv_fb is unbound, bad things happen
> > > because the memory is still being written to via the user space
> > > mmap. There are multiple "BUG: Bad page state in process
> > > bash  pfn:106c65" errors followed by stack traces.
> > >
> > > 3) Similarly in a Gen 1 VM, if the frame buffer size is > 4 MiB,
> > > CMA memory is allocated (assuming it is available). This CMA
> > > memory gets mapped into user space. When hyperv_fb is
> > > unbound, that memory is freed. But CMA complains that the
> > > ref count on the pages is not zero. Here's the dmesg output:
> > >
> > > [  191.629780] ------------[ cut here ]------------
> > > [  191.629784] 200 pages are still in use!
> > > [  191.629789] WARNING: CPU: 3 PID: 1115 at mm/page_alloc.c:6757
> > free_contig_range+0x15e/0x170
> > >
> > > Stack trace is:
> > >
> > > [  191.629847]  ? __warn+0x97/0x160
> > > [  191.629849]  ? free_contig_range+0x15e/0x170
> > > [  191.629849]  ? report_bug+0x1bb/0x1d0
> > > [  191.629851]  ? console_unlock+0xdd/0x1e0
> > > [  191.629854]  ? handle_bug+0x60/0xa0
> > > [  191.629857]  ? exc_invalid_op+0x1d/0x80
> > > [  191.629859]  ? asm_exc_invalid_op+0x1f/0x30
> > > [  191.629862]  ? free_contig_range+0x15e/0x170
> > > [  191.629862]  ? free_contig_range+0x15e/0x170
> > > [  191.629863]  cma_release+0xc6/0x150
> > > [  191.629865]  dma_free_contiguous+0x34/0x70
> > > [  191.629868]  dma_direct_free+0xd3/0x130
> > > [  191.629869]  dma_free_attrs+0x6b/0x130
> > > [  191.629872]  hvfb_putmem.isra.0+0x99/0xd0 [hyperv_fb]
> > > [  191.629874]  hvfb_remove+0x75/0x80 [hyperv_fb]
> > > [  191.629876]  vmbus_remove+0x28/0x40 [hv_vmbus]
> > > [  191.629883]  device_remove+0x43/0x70
> > > [  191.629886]  device_release_driver_internal+0xbd/0x140
> > > [  191.629888]  device_driver_detach+0x18/0x20
> > > [  191.629890]  unbind_store+0x8f/0xa0
> > > [  191.629891]  drv_attr_store+0x25/0x40
> > > [  191.629892]  sysfs_kf_write+0x3f/0x50
> > > [  191.629894]  kernfs_fop_write_iter+0x142/0x1d0
> > > [  191.629896]  vfs_write+0x31b/0x450
> > > [  191.629898]  ksys_write+0x6e/0xe0
> > > [  191.629899]  __x64_sys_write+0x1e/0x30
> > > [  191.629900]  x64_sys_call+0x16bf/0x2150
> > > [  191.629903]  do_syscall_64+0x4e/0x110
> > > [  191.629904]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > >
> > > For all three cases, I think the memory freeing and iounmap() operations
> > > can be moved to the new hvfb_destroy() function so that the memory
> > > is cleaned up only when there aren't any users. While these additional
> > > changes could be done as a separate patch, it seems to me like they are all
> > > part of the same underlying issue as the reference count problem, and
> > > could be combined into this patch.
> > >
> > > Michael
> > >
> > 
> > Thanks for your review.
> > 
> > I had considered moving the entire `hvfb_putmem()` function to `destroy`,
> > but I was hesitant for two reasons:
> > 
> >   1. I wasn’t aware of any scenario where this would be useful. However,
> >      your explanation has convinced me that it is necessary.
> >   2. `hvfb_release_phymem()` relies on the `hdev` pointer, which requires
> >      multiple `container_of` operations to derive it from the `info` pointer.
> >      I was unsure if the complexity was justified, but it seems worthwhile now.
> > 
> > I will move `hvfb_putmem()` to the `destroy` function in V2, and I hope this
> > will address all the cases you mentioned.
> > 
> 
> Yes, that's what I expect needs to happen, though I haven't looked at the
> details of making sure all the needed data structures are still around. Like
> you, I just had this sense that hvfb_putmem() might need to be moved as
> well, so I tried to produce a failure scenario to prove it, which turned out
> to be easy.
> 
> Michael

I will add this in V2 as well. But I have found an another issue which is
not very frequent.


[  176.562153] ------------[ cut here ]------------
[  176.562159] fb0: fb_WARN_ON_ONCE(pageref->page != page)
[  176.562176] WARNING: CPU: 50 PID: 1522 at drivers/video/fbdev/core/fb_defio.c:67 fb_deferred_io_mkwrite+0x215/0x280

<snip>

[  176.562258] Call Trace:
[  176.562260]  <TASK>
[  176.562263]  ? show_regs+0x6c/0x80
[  176.562269]  ? __warn+0x8d/0x150
[  176.562273]  ? fb_deferred_io_mkwrite+0x215/0x280
[  176.562275]  ? report_bug+0x182/0x1b0
[  176.562280]  ? handle_bug+0x133/0x1a0
[  176.562283]  ? exc_invalid_op+0x18/0x80
[  176.562284]  ? asm_exc_invalid_op+0x1b/0x20
[  176.562289]  ? fb_deferred_io_mkwrite+0x215/0x280
[  176.562291]  ? fb_deferred_io_mkwrite+0x215/0x280
[  176.562293]  do_page_mkwrite+0x4d/0xb0
[  176.562296]  do_wp_page+0xe8/0xd50
[  176.562300]  ? ___pte_offset_map+0x1c/0x1b0
[  176.562304]  __handle_mm_fault+0xbe1/0x10e0
[  176.562307]  handle_mm_fault+0x17f/0x2e0
[  176.562309]  do_user_addr_fault+0x2d1/0x8d0
[  176.562314]  exc_page_fault+0x85/0x1e0
[  176.562318]  asm_exc_page_fault+0x27/0x30

Looks this is because driver is unbind still Xorg is trying to write
to memory which is causing some page faults. I have confirmed PID 1522
is of Xorg. I think this is because we need to cancel the framebuffer
deferred work after flushing it.

After adding the below in hvfb_remove I don't see this issue anymore.
Although as the issue is not very frequent I am not 100% sure.

	cancel_delayed_work_sync(&info->deferred_work);

If you think this is reasonable I can add this as well in V2.


- Saurabh

^ permalink raw reply

* Re: [PATCH] [PATCH v2] staging: sm750fb: Make g_fbmode truly constant
From: kernel test robot @ 2025-02-23  9:34 UTC (permalink / raw)
  To: Madhur Kumar, sudipm.mukherjee, teddy.wang
  Cc: llvm, oe-kbuild-all, gregkh, linux-fbdev, linux-staging,
	linux-kernel, Madhur Kumar, Dan Carpenter
In-Reply-To: <20250222201514.15730-1-madhurkumar004@gmail.com>

Hi Madhur,

kernel test robot noticed the following build errors:

[auto build test ERROR on staging/staging-testing]

url:    https://github.com/intel-lab-lkp/linux/commits/Madhur-Kumar/staging-sm750fb-Make-g_fbmode-truly-constant/20250223-041638
base:   staging/staging-testing
patch link:    https://lore.kernel.org/r/20250222201514.15730-1-madhurkumar004%40gmail.com
patch subject: [PATCH] [PATCH v2] staging: sm750fb: Make g_fbmode truly constant
config: x86_64-buildonly-randconfig-001-20250223 (https://download.01.org/0day-ci/archive/20250223/202502231754.pnnw8mMg-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250223/202502231754.pnnw8mMg-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502231754.pnnw8mMg-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/staging/sm750fb/sm750.c:7:
   In file included from include/linux/mm.h:2224:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> drivers/staging/sm750fb/sm750.c:788:19: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
     788 |                 g_fbmode[index] = g_def_fbmode;
         |                 ~~~~~~~~~~~~~~~ ^
   drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
      36 | static const char * const g_fbmode[] = {NULL, NULL};
         | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/staging/sm750fb/sm750.c:790:20: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
     790 |                         g_fbmode[index] = g_fbmode[0];
         |                         ~~~~~~~~~~~~~~~ ^
   drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
      36 | static const char * const g_fbmode[] = {NULL, NULL};
         | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/staging/sm750fb/sm750.c:951:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
     951 |                                 g_fbmode[0] = opt;
         |                                 ~~~~~~~~~~~ ^
   drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
      36 | static const char * const g_fbmode[] = {NULL, NULL};
         | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/staging/sm750fb/sm750.c:955:17: error: cannot assign to variable 'g_fbmode' with const-qualified type 'const char *const[2]'
     955 |                                 g_fbmode[1] = opt;
         |                                 ~~~~~~~~~~~ ^
   drivers/staging/sm750fb/sm750.c:36:27: note: variable 'g_fbmode' declared const here
      36 | static const char * const g_fbmode[] = {NULL, NULL};
         | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
   3 warnings and 4 errors generated.


vim +788 drivers/staging/sm750fb/sm750.c

81dee67e215b23f Sudip Mukherjee         2015-03-03  715  
81dee67e215b23f Sudip Mukherjee         2015-03-03  716  static int lynxfb_set_fbinfo(struct fb_info *info, int index)
81dee67e215b23f Sudip Mukherjee         2015-03-03  717  {
81dee67e215b23f Sudip Mukherjee         2015-03-03  718  	int i;
81dee67e215b23f Sudip Mukherjee         2015-03-03  719  	struct lynxfb_par *par;
e359b6a863e19f2 Mike Rapoport           2015-10-26  720  	struct sm750_dev *sm750_dev;
81dee67e215b23f Sudip Mukherjee         2015-03-03  721  	struct lynxfb_crtc *crtc;
81dee67e215b23f Sudip Mukherjee         2015-03-03  722  	struct lynxfb_output *output;
81dee67e215b23f Sudip Mukherjee         2015-03-03  723  	struct fb_var_screeninfo *var;
81dee67e215b23f Sudip Mukherjee         2015-03-03  724  	struct fb_fix_screeninfo *fix;
81dee67e215b23f Sudip Mukherjee         2015-03-03  725  
81dee67e215b23f Sudip Mukherjee         2015-03-03  726  	const struct fb_videomode *pdb[] = {
81dee67e215b23f Sudip Mukherjee         2015-03-03  727  		lynx750_ext, NULL, vesa_modes,
81dee67e215b23f Sudip Mukherjee         2015-03-03  728  	};
81dee67e215b23f Sudip Mukherjee         2015-03-03  729  	int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
d5d66cfea2ca281 Kelsey Skunberg         2019-04-27  730  	static const char * const mdb_desc[] = {
81dee67e215b23f Sudip Mukherjee         2015-03-03  731  		"driver prepared modes",
81dee67e215b23f Sudip Mukherjee         2015-03-03  732  		"kernel prepared default modedb",
81dee67e215b23f Sudip Mukherjee         2015-03-03  733  		"kernel HELPERS prepared vesa_modes",
81dee67e215b23f Sudip Mukherjee         2015-03-03  734  	};
81dee67e215b23f Sudip Mukherjee         2015-03-03  735  
70407df77665c0e Michel von Czettritz    2015-03-26  736  	static const char *fixId[2] = {
81dee67e215b23f Sudip Mukherjee         2015-03-03  737  		"sm750_fb1", "sm750_fb2",
81dee67e215b23f Sudip Mukherjee         2015-03-03  738  	};
81dee67e215b23f Sudip Mukherjee         2015-03-03  739  
81dee67e215b23f Sudip Mukherjee         2015-03-03  740  	int ret, line_length;
81dee67e215b23f Sudip Mukherjee         2015-03-03  741  
81dee67e215b23f Sudip Mukherjee         2015-03-03  742  	ret = 0;
81dee67e215b23f Sudip Mukherjee         2015-03-03  743  	par = (struct lynxfb_par *)info->par;
e359b6a863e19f2 Mike Rapoport           2015-10-26  744  	sm750_dev = par->dev;
81dee67e215b23f Sudip Mukherjee         2015-03-03  745  	crtc = &par->crtc;
81dee67e215b23f Sudip Mukherjee         2015-03-03  746  	output = &par->output;
81dee67e215b23f Sudip Mukherjee         2015-03-03  747  	var = &info->var;
81dee67e215b23f Sudip Mukherjee         2015-03-03  748  	fix = &info->fix;
81dee67e215b23f Sudip Mukherjee         2015-03-03  749  
81dee67e215b23f Sudip Mukherjee         2015-03-03  750  	/* set index */
81dee67e215b23f Sudip Mukherjee         2015-03-03  751  	par->index = index;
81dee67e215b23f Sudip Mukherjee         2015-03-03  752  	output->channel = &crtc->channel;
81dee67e215b23f Sudip Mukherjee         2015-03-03  753  	sm750fb_set_drv(par);
81dee67e215b23f Sudip Mukherjee         2015-03-03  754  
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  755  	/*
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  756  	 * set current cursor variable and proc pointer,
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  757  	 * must be set after crtc member initialized
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  758  	 */
fdc234d85210d91 Benjamin Philip         2021-07-28  759  	crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
e359b6a863e19f2 Mike Rapoport           2015-10-26  760  	crtc->cursor.mmio = sm750_dev->pvReg +
e359b6a863e19f2 Mike Rapoport           2015-10-26  761  		0x800f0 + (int)crtc->channel * 0x140;
81dee67e215b23f Sudip Mukherjee         2015-03-03  762  
81dee67e215b23f Sudip Mukherjee         2015-03-03  763  	pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
cd33da26036ea54 Christopher Carbone     2022-08-23  764  	crtc->cursor.max_h = 64;
cd33da26036ea54 Christopher Carbone     2022-08-23  765  	crtc->cursor.max_w = 64;
39f9137268ee3df Benjamin Philip         2021-07-26  766  	crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
e359b6a863e19f2 Mike Rapoport           2015-10-26  767  	crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
81dee67e215b23f Sudip Mukherjee         2015-03-03  768  
3de08a2d14ff8c7 Lorenzo Stoakes         2015-03-20  769  	memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
f7c8a046577e09d Thomas Zimmermann       2023-11-27  770  	if (!g_hwcursor)
52d0744d751d8f1 Arnd Bergmann           2016-11-09  771  		sm750_hw_cursor_disable(&crtc->cursor);
81dee67e215b23f Sudip Mukherjee         2015-03-03  772  
81dee67e215b23f Sudip Mukherjee         2015-03-03  773  	/* set info->fbops, must be set before fb_find_mode */
e359b6a863e19f2 Mike Rapoport           2015-10-26  774  	if (!sm750_dev->accel_off) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  775  		/* use 2d acceleration */
f7c8a046577e09d Thomas Zimmermann       2023-11-27  776  		if (!g_hwcursor)
f7c8a046577e09d Thomas Zimmermann       2023-11-27  777  			info->fbops = &lynxfb_ops_accel;
f7c8a046577e09d Thomas Zimmermann       2023-11-27  778  		else
f7c8a046577e09d Thomas Zimmermann       2023-11-27  779  			info->fbops = &lynxfb_ops_accel_with_cursor;
f7c8a046577e09d Thomas Zimmermann       2023-11-27  780  	} else {
f7c8a046577e09d Thomas Zimmermann       2023-11-27  781  		if (!g_hwcursor)
81dee67e215b23f Sudip Mukherjee         2015-03-03  782  			info->fbops = &lynxfb_ops;
f7c8a046577e09d Thomas Zimmermann       2023-11-27  783  		else
f7c8a046577e09d Thomas Zimmermann       2023-11-27  784  			info->fbops = &lynxfb_ops_with_cursor;
f7c8a046577e09d Thomas Zimmermann       2023-11-27  785  	}
81dee67e215b23f Sudip Mukherjee         2015-03-03  786  
81dee67e215b23f Sudip Mukherjee         2015-03-03  787  	if (!g_fbmode[index]) {
81dee67e215b23f Sudip Mukherjee         2015-03-03 @788  		g_fbmode[index] = g_def_fbmode;
81dee67e215b23f Sudip Mukherjee         2015-03-03  789  		if (index)
81dee67e215b23f Sudip Mukherjee         2015-03-03  790  			g_fbmode[index] = g_fbmode[0];
81dee67e215b23f Sudip Mukherjee         2015-03-03  791  	}
81dee67e215b23f Sudip Mukherjee         2015-03-03  792  
81dee67e215b23f Sudip Mukherjee         2015-03-03  793  	for (i = 0; i < 3; i++) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  794  		ret = fb_find_mode(var, info, g_fbmode[index],
81dee67e215b23f Sudip Mukherjee         2015-03-03  795  				   pdb[i], cdb[i], NULL, 8);
81dee67e215b23f Sudip Mukherjee         2015-03-03  796  
81dee67e215b23f Sudip Mukherjee         2015-03-03  797  		if (ret == 1) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  798  			pr_info("success! use specified mode:%s in %s\n",
81dee67e215b23f Sudip Mukherjee         2015-03-03  799  				g_fbmode[index],
81dee67e215b23f Sudip Mukherjee         2015-03-03  800  				mdb_desc[i]);
81dee67e215b23f Sudip Mukherjee         2015-03-03  801  			break;
81dee67e215b23f Sudip Mukherjee         2015-03-03  802  		} else if (ret == 2) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  803  			pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
81dee67e215b23f Sudip Mukherjee         2015-03-03  804  				g_fbmode[index],
81dee67e215b23f Sudip Mukherjee         2015-03-03  805  				mdb_desc[i]);
81dee67e215b23f Sudip Mukherjee         2015-03-03  806  			break;
81dee67e215b23f Sudip Mukherjee         2015-03-03  807  		} else if (ret == 3) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  808  			pr_warn("wanna use default mode\n");
4bd9503d0becdb6 Michel von Czettritz    2015-03-26  809  			/*break;*/
81dee67e215b23f Sudip Mukherjee         2015-03-03  810  		} else if (ret == 4) {
81dee67e215b23f Sudip Mukherjee         2015-03-03  811  			pr_warn("fall back to any valid mode\n");
81dee67e215b23f Sudip Mukherjee         2015-03-03  812  		} else {
3318bb5e945f700 Michel von Czettritz    2015-03-26  813  			pr_warn("ret = %d,fb_find_mode failed,with %s\n",
3318bb5e945f700 Michel von Czettritz    2015-03-26  814  				ret,
3318bb5e945f700 Michel von Czettritz    2015-03-26  815  				mdb_desc[i]);
81dee67e215b23f Sudip Mukherjee         2015-03-03  816  		}
81dee67e215b23f Sudip Mukherjee         2015-03-03  817  	}
81dee67e215b23f Sudip Mukherjee         2015-03-03  818  
81dee67e215b23f Sudip Mukherjee         2015-03-03  819  	/* some member of info->var had been set by fb_find_mode */
81dee67e215b23f Sudip Mukherjee         2015-03-03  820  
271dbae3c6a1daa Prasant Jalan           2017-04-01  821  	pr_info("Member of info->var is :\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  822  		"xres=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  823  		"yres=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  824  		"xres_virtual=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  825  		"yres_virtual=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  826  		"xoffset=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  827  		"yoffset=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  828  		"bits_per_pixel=%d\n"
271dbae3c6a1daa Prasant Jalan           2017-04-01  829  		" ...\n",
3318bb5e945f700 Michel von Czettritz    2015-03-26  830  		var->xres,
3318bb5e945f700 Michel von Czettritz    2015-03-26  831  		var->yres,
3318bb5e945f700 Michel von Czettritz    2015-03-26  832  		var->xres_virtual,
3318bb5e945f700 Michel von Czettritz    2015-03-26  833  		var->yres_virtual,
3318bb5e945f700 Michel von Czettritz    2015-03-26  834  		var->xoffset,
3318bb5e945f700 Michel von Czettritz    2015-03-26  835  		var->yoffset,
3318bb5e945f700 Michel von Czettritz    2015-03-26  836  		var->bits_per_pixel);
81dee67e215b23f Sudip Mukherjee         2015-03-03  837  
81dee67e215b23f Sudip Mukherjee         2015-03-03  838  	/* set par */
81dee67e215b23f Sudip Mukherjee         2015-03-03  839  	par->info = info;
81dee67e215b23f Sudip Mukherjee         2015-03-03  840  
81dee67e215b23f Sudip Mukherjee         2015-03-03  841  	/* set info */
e3a3f9f5123683b Mike Rapoport           2015-10-26  842  	line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
e3a3f9f5123683b Mike Rapoport           2015-10-26  843  			    crtc->line_pad);
81dee67e215b23f Sudip Mukherjee         2015-03-03  844  
81dee67e215b23f Sudip Mukherjee         2015-03-03  845  	info->pseudo_palette = &par->pseudo_palette[0];
cc59bde1c920ab6 Benjamin Philip         2021-07-28  846  	info->screen_base = crtc->v_screen;
81dee67e215b23f Sudip Mukherjee         2015-03-03  847  	pr_debug("screen_base vaddr = %p\n", info->screen_base);
81dee67e215b23f Sudip Mukherjee         2015-03-03  848  	info->screen_size = line_length * var->yres_virtual;
81dee67e215b23f Sudip Mukherjee         2015-03-03  849  
81dee67e215b23f Sudip Mukherjee         2015-03-03  850  	/* set info->fix */
81dee67e215b23f Sudip Mukherjee         2015-03-03  851  	fix->type = FB_TYPE_PACKED_PIXELS;
81dee67e215b23f Sudip Mukherjee         2015-03-03  852  	fix->type_aux = 0;
81dee67e215b23f Sudip Mukherjee         2015-03-03  853  	fix->xpanstep = crtc->xpanstep;
81dee67e215b23f Sudip Mukherjee         2015-03-03  854  	fix->ypanstep = crtc->ypanstep;
81dee67e215b23f Sudip Mukherjee         2015-03-03  855  	fix->ywrapstep = crtc->ywrapstep;
81dee67e215b23f Sudip Mukherjee         2015-03-03  856  	fix->accel = FB_ACCEL_SMI;
81dee67e215b23f Sudip Mukherjee         2015-03-03  857  
9c15db83a86bf83 Kumar Kartikeya Dwivedi 2021-01-31  858  	strscpy(fix->id, fixId[index], sizeof(fix->id));
81dee67e215b23f Sudip Mukherjee         2015-03-03  859  
fdc234d85210d91 Benjamin Philip         2021-07-28  860  	fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;
81dee67e215b23f Sudip Mukherjee         2015-03-03  861  	pr_info("fix->smem_start = %lx\n", fix->smem_start);
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  862  	/*
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  863  	 * according to mmap experiment from user space application,
81dee67e215b23f Sudip Mukherjee         2015-03-03  864  	 * fix->mmio_len should not larger than virtual size
81dee67e215b23f Sudip Mukherjee         2015-03-03  865  	 * (xres_virtual x yres_virtual x ByPP)
81dee67e215b23f Sudip Mukherjee         2015-03-03  866  	 * Below line maybe buggy when user mmap fb dev node and write
81dee67e215b23f Sudip Mukherjee         2015-03-03  867  	 * data into the bound over virtual size
d11ac7cbcc266c6 Sudip Mukherjee         2015-08-07  868  	 */
81dee67e215b23f Sudip Mukherjee         2015-03-03  869  	fix->smem_len = crtc->vidmem_size;
81dee67e215b23f Sudip Mukherjee         2015-03-03  870  	pr_info("fix->smem_len = %x\n", fix->smem_len);
81dee67e215b23f Sudip Mukherjee         2015-03-03  871  	info->screen_size = fix->smem_len;
81dee67e215b23f Sudip Mukherjee         2015-03-03  872  	fix->line_length = line_length;
e359b6a863e19f2 Mike Rapoport           2015-10-26  873  	fix->mmio_start = sm750_dev->vidreg_start;
81dee67e215b23f Sudip Mukherjee         2015-03-03  874  	pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
e359b6a863e19f2 Mike Rapoport           2015-10-26  875  	fix->mmio_len = sm750_dev->vidreg_size;
81dee67e215b23f Sudip Mukherjee         2015-03-03  876  	pr_info("fix->mmio_len = %x\n", fix->mmio_len);
b610e1193a917f4 Matej Dujava            2020-04-30  877  
b610e1193a917f4 Matej Dujava            2020-04-30  878  	lynxfb_set_visual_mode(info);
81dee67e215b23f Sudip Mukherjee         2015-03-03  879  
81dee67e215b23f Sudip Mukherjee         2015-03-03  880  	/* set var */
81dee67e215b23f Sudip Mukherjee         2015-03-03  881  	var->activate = FB_ACTIVATE_NOW;
81dee67e215b23f Sudip Mukherjee         2015-03-03  882  	var->accel_flags = 0;
81dee67e215b23f Sudip Mukherjee         2015-03-03  883  	var->vmode = FB_VMODE_NONINTERLACED;
81dee67e215b23f Sudip Mukherjee         2015-03-03  884  
81dee67e215b23f Sudip Mukherjee         2015-03-03  885  	pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
81dee67e215b23f Sudip Mukherjee         2015-03-03  886  		 info->cmap.start, info->cmap.len,
81dee67e215b23f Sudip Mukherjee         2015-03-03  887  		 info->cmap.red, info->cmap.green, info->cmap.blue,
81dee67e215b23f Sudip Mukherjee         2015-03-03  888  		 info->cmap.transp);
81dee67e215b23f Sudip Mukherjee         2015-03-03  889  
61c507cf652da1b Michel von Czettritz    2015-03-26  890  	ret = fb_alloc_cmap(&info->cmap, 256, 0);
61c507cf652da1b Michel von Czettritz    2015-03-26  891  	if (ret < 0) {
008272072d61a8c Masanari Iida           2015-05-28  892  		pr_err("Could not allocate memory for cmap.\n");
81dee67e215b23f Sudip Mukherjee         2015-03-03  893  		goto exit;
81dee67e215b23f Sudip Mukherjee         2015-03-03  894  	}
81dee67e215b23f Sudip Mukherjee         2015-03-03  895  
81dee67e215b23f Sudip Mukherjee         2015-03-03  896  	pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
81dee67e215b23f Sudip Mukherjee         2015-03-03  897  		 info->cmap.start, info->cmap.len,
81dee67e215b23f Sudip Mukherjee         2015-03-03  898  		 info->cmap.red, info->cmap.green, info->cmap.blue,
81dee67e215b23f Sudip Mukherjee         2015-03-03  899  		 info->cmap.transp);
81dee67e215b23f Sudip Mukherjee         2015-03-03  900  
81dee67e215b23f Sudip Mukherjee         2015-03-03  901  exit:
81dee67e215b23f Sudip Mukherjee         2015-03-03  902  	lynxfb_ops_check_var(var, info);
81dee67e215b23f Sudip Mukherjee         2015-03-03  903  	return ret;
81dee67e215b23f Sudip Mukherjee         2015-03-03  904  }
81dee67e215b23f Sudip Mukherjee         2015-03-03  905  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* RE: [PATCH] fbdev: hyperv_fb: Allow graceful removal of framebuffer
From: Michael Kelley @ 2025-02-22 20:16 UTC (permalink / raw)
  To: Saurabh Singh Sengar
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, deller@gmx.de, akpm@linux-foundation.org,
	linux-hyperv@vger.kernel.org, linux-fbdev@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	ssengar@microsoft.com
In-Reply-To: <20250222172715.GA28061@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>

From: Saurabh Singh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 22, 2025 9:27 AM
> 
> On Wed, Feb 19, 2025 at 05:22:36AM +0000, Michael Kelley wrote:
> > From: Saurabh Sengar <ssengar@linux.microsoft.com> Sent: Saturday, February 15,
> 2025 1:21 AM
> > >
> > > When a Hyper-V framebuffer device is unbind, hyperv_fb driver tries to
> > > release the framebuffer forcefully. If this framebuffer is in use it
> > > produce the following WARN and hence this framebuffer is never released.
> > >
> > > [   44.111220] WARNING: CPU: 35 PID: 1882 at drivers/video/fbdev/core/fb_info.c:70
> framebuffer_release+0x2c/0x40
> > > < snip >
> > > [   44.111289] Call Trace:
> > > [   44.111290]  <TASK>
> > > [   44.111291]  ? show_regs+0x6c/0x80
> > > [   44.111295]  ? __warn+0x8d/0x150
> > > [   44.111298]  ? framebuffer_release+0x2c/0x40
> > > [   44.111300]  ? report_bug+0x182/0x1b0
> > > [   44.111303]  ? handle_bug+0x6e/0xb0
> > > [   44.111306]  ? exc_invalid_op+0x18/0x80
> > > [   44.111308]  ? asm_exc_invalid_op+0x1b/0x20
> > > [   44.111311]  ? framebuffer_release+0x2c/0x40
> > > [   44.111313]  ? hvfb_remove+0x86/0xa0 [hyperv_fb]
> > > [   44.111315]  vmbus_remove+0x24/0x40 [hv_vmbus]
> > > [   44.111323]  device_remove+0x40/0x80
> > > [   44.111325]  device_release_driver_internal+0x20b/0x270
> > > [   44.111327]  ? bus_find_device+0xb3/0xf0
> > >
> > > Fix this by moving the release of framebuffer to fb_ops.fb_destroy function
> > > so that framebuffer framework handles it gracefully
> >
> > These changes look good for solving the specific problem where
> > the reference count WARN is produced. But there is another
> > problem of the same type that happens when doing unbind
> > of a hyperv_fb device that is in use (i.e., /dev/fb0 is open and
> > mmap'ed by some user space program).
> >
> > For this additional problem, there are three sub-cases,
> > depending on what memory gets mmap'ed into user space.
> > Two of the three sub-cases have a problem.
> >
> > 1) When Hyper-V FB uses deferred I/O, the vmalloc dio memory
> > is what get mapped into user space. When hyperv_fb is unbound,
> > the vmalloc dio memory is freed. But the memory doesn't actually
> > get freed if it is still mmap'ed into user space. The deferred I/O
> > mechanism is stopped, but user space can keep writing to the
> > memory even though the pixels don't get copied to the actual
> > framebuffer any longer.  When the user space program terminates
> > (or unmaps the memory), the memory will be freed. So this case
> > is OK, though perhaps a bit dubious.
> >
> > 2) When Hyper-V FB is in a Gen 1 VM, and the frame buffer size
> > is <= 4 MiB, a normal kernel allocation is used for the
> > memory that is mmap'ed to user space. If this memory
> > is freed when hyperv_fb is unbound, bad things happen
> > because the memory is still being written to via the user space
> > mmap. There are multiple "BUG: Bad page state in process
> > bash  pfn:106c65" errors followed by stack traces.
> >
> > 3) Similarly in a Gen 1 VM, if the frame buffer size is > 4 MiB,
> > CMA memory is allocated (assuming it is available). This CMA
> > memory gets mapped into user space. When hyperv_fb is
> > unbound, that memory is freed. But CMA complains that the
> > ref count on the pages is not zero. Here's the dmesg output:
> >
> > [  191.629780] ------------[ cut here ]------------
> > [  191.629784] 200 pages are still in use!
> > [  191.629789] WARNING: CPU: 3 PID: 1115 at mm/page_alloc.c:6757
> free_contig_range+0x15e/0x170
> >
> > Stack trace is:
> >
> > [  191.629847]  ? __warn+0x97/0x160
> > [  191.629849]  ? free_contig_range+0x15e/0x170
> > [  191.629849]  ? report_bug+0x1bb/0x1d0
> > [  191.629851]  ? console_unlock+0xdd/0x1e0
> > [  191.629854]  ? handle_bug+0x60/0xa0
> > [  191.629857]  ? exc_invalid_op+0x1d/0x80
> > [  191.629859]  ? asm_exc_invalid_op+0x1f/0x30
> > [  191.629862]  ? free_contig_range+0x15e/0x170
> > [  191.629862]  ? free_contig_range+0x15e/0x170
> > [  191.629863]  cma_release+0xc6/0x150
> > [  191.629865]  dma_free_contiguous+0x34/0x70
> > [  191.629868]  dma_direct_free+0xd3/0x130
> > [  191.629869]  dma_free_attrs+0x6b/0x130
> > [  191.629872]  hvfb_putmem.isra.0+0x99/0xd0 [hyperv_fb]
> > [  191.629874]  hvfb_remove+0x75/0x80 [hyperv_fb]
> > [  191.629876]  vmbus_remove+0x28/0x40 [hv_vmbus]
> > [  191.629883]  device_remove+0x43/0x70
> > [  191.629886]  device_release_driver_internal+0xbd/0x140
> > [  191.629888]  device_driver_detach+0x18/0x20
> > [  191.629890]  unbind_store+0x8f/0xa0
> > [  191.629891]  drv_attr_store+0x25/0x40
> > [  191.629892]  sysfs_kf_write+0x3f/0x50
> > [  191.629894]  kernfs_fop_write_iter+0x142/0x1d0
> > [  191.629896]  vfs_write+0x31b/0x450
> > [  191.629898]  ksys_write+0x6e/0xe0
> > [  191.629899]  __x64_sys_write+0x1e/0x30
> > [  191.629900]  x64_sys_call+0x16bf/0x2150
> > [  191.629903]  do_syscall_64+0x4e/0x110
> > [  191.629904]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> >
> > For all three cases, I think the memory freeing and iounmap() operations
> > can be moved to the new hvfb_destroy() function so that the memory
> > is cleaned up only when there aren't any users. While these additional
> > changes could be done as a separate patch, it seems to me like they are all
> > part of the same underlying issue as the reference count problem, and
> > could be combined into this patch.
> >
> > Michael
> >
> 
> Thanks for your review.
> 
> I had considered moving the entire `hvfb_putmem()` function to `destroy`,
> but I was hesitant for two reasons:
> 
>   1. I wasn’t aware of any scenario where this would be useful. However,
>      your explanation has convinced me that it is necessary.
>   2. `hvfb_release_phymem()` relies on the `hdev` pointer, which requires
>      multiple `container_of` operations to derive it from the `info` pointer.
>      I was unsure if the complexity was justified, but it seems worthwhile now.
> 
> I will move `hvfb_putmem()` to the `destroy` function in V2, and I hope this
> will address all the cases you mentioned.
> 

Yes, that's what I expect needs to happen, though I haven't looked at the
details of making sure all the needed data structures are still around. Like
you, I just had this sense that hvfb_putmem() might need to be moved as
well, so I tried to produce a failure scenario to prove it, which turned out
to be easy.

Michael

^ permalink raw reply

* [PATCH] [PATCH v2] staging: sm750fb: Make g_fbmode truly constant
From: Madhur Kumar @ 2025-02-22 20:15 UTC (permalink / raw)
  To: sudipm.mukherjee, teddy.wang
  Cc: gregkh, linux-fbdev, linux-staging, linux-kernel, Madhur Kumar,
	Dan Carpenter

Declare g_fbmode as a pointer to constant data. This ensures that both
array and its element are immutable.

Reviewed-by: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Madhur Kumar <madhurkumar004@gmail.com>
---
Changes in v2:
- Added commit message
---
 drivers/staging/sm750fb/sm750.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 04c1b32a2..aa154032f 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -33,7 +33,7 @@
 static int g_hwcursor = 1;
 static int g_noaccel;
 static int g_nomtrr;
-static const char *g_fbmode[] = {NULL, NULL};
+static const char * const g_fbmode[] = {NULL, NULL};
 static const char *g_def_fbmode = "1024x768-32@60";
 static char *g_settings;
 static int g_dualview;
-- 
2.48.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox