Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH 1/3] dummycon: only build module if there are users
From: Helge Deller @ 2025-03-09  1:46 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: Arnd Bergmann, linux-fbdev, dri-devel, linux-kernel
In-Reply-To: <20250225164436.56654-1-arnd@kernel.org>

On 2/25/25 17:44, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> Dummycon is used as a fallback conswitchp for vgacon and fbcon
> in the VT code, and there are no references to it if all three
> are disabled, so just leave it out of the kernel image for
> configurations without those.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>   drivers/video/console/Kconfig | 3 +--
>   1 file changed, 1 insertion(+), 2 deletions(-)


whole series applied to fbdev git tree.

Thanks!
Helge

^ permalink raw reply

* Re: [PATCH] fbdev: lcdcfb: Register sysfs groups through driver core
From: Helge Deller @ 2025-03-09  2:00 UTC (permalink / raw)
  To: oushixiong1025
  Cc: Thomas Zimmermann, Laurent Pinchart, Lee Jones,
	Uwe Kleine-König, Arnd Bergmann, linux-fbdev, dri-devel,
	linux-kernel, Shixiong Ou
In-Reply-To: <20250219084427.244985-1-oushixiong1025@163.com>

On 2/19/25 09:44, oushixiong1025@163.com wrote:
> From: Shixiong Ou <oushixiong@kylinos.cn>
>
> [WHY]
>     1. The driver forgot to call device_remove_file()
>     in sh_mobile_lcdc_overlay_fb_unregister(), and there was
>     no error handling when calling device_create_file() failed.
>
>     2. This should probably use device_add_group() instead of
>     individual files to simplify both creation and removal. [Arnd]
>
>     3. The driver core can register and cleanup sysfs groups already.
>     as commit 95cdd538e0e5 ("fbdev: efifb: Register sysfs groups
>     through driver core").
>
> [HOW]
>     Register sysfs groups through driver core.
>
> Signed-off-by: Shixiong Ou <oushixiong@kylinos.cn>
> ---
>   drivers/video/fbdev/sh_mobile_lcdcfb.c | 29 ++++++++++++--------------
>   1 file changed, 13 insertions(+), 16 deletions(-)

applied.

Thanks!
Helge

^ permalink raw reply

* Re: [PATCH] fbdev: fsl-diu-fb: add missing device_remove_file()
From: Helge Deller @ 2025-03-09  2:28 UTC (permalink / raw)
  To: oushixiong1025, Timur Tabi
  Cc: linux-fbdev, dri-devel, linux-kernel, Shixiong Ou
In-Reply-To: <20250208102359.279333-1-oushixiong1025@163.com>

On 2/8/25 11:23, oushixiong1025@163.com wrote:
> From: Shixiong Ou <oushixiong@kylinos.cn>
>
> Call device_remove_file() when driver remove.
>
> Signed-off-by: Shixiong Ou <oushixiong@kylinos.cn>
> ---
>   drivers/video/fbdev/fsl-diu-fb.c | 4 ++++
>   1 file changed, 4 insertions(+)
>
> diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
> index 5ac8201c3533..8c91af9a9156 100644
> --- a/drivers/video/fbdev/fsl-diu-fb.c
> +++ b/drivers/video/fbdev/fsl-diu-fb.c
> @@ -1807,6 +1807,7 @@ static int fsl_diu_probe(struct platform_device *pdev)
>   	if (ret) {
>   		dev_err(&pdev->dev, "could not create sysfs file %s\n",
>   			data->dev_attr.attr.name);
> +		goto error;

this seems to miss calling free_irq() then at least...

Helge

^ permalink raw reply

* Re: [PATCH 1/1] fbdev: hyperv_fb: Fix hang in kdump kernel when on Hyper-V Gen 2 VMs
From: Helge Deller @ 2025-03-09  2:58 UTC (permalink / raw)
  To: mhklinux, haiyangz, wei.liu, decui, javierm, thomas.tai
  Cc: tzimmermann, kasong, dri-devel, linux-fbdev, linux-kernel,
	linux-hyperv
In-Reply-To: <20250218230130.3207-1-mhklinux@outlook.com>

On 2/19/25 00:01, mhkelley58@gmail.com wrote:
> From: Michael Kelley <mhklinux@outlook.com>
>
> Gen 2 Hyper-V VMs boot via EFI and have a standard EFI framebuffer
> device. When the kdump kernel runs in such a VM, loading the efifb
> driver may hang because of accessing the framebuffer at the wrong
> memory address.
>
> The scenario occurs when the hyperv_fb driver in the original kernel
> moves the framebuffer to a different MMIO address because of conflicts
> with an already-running efifb or simplefb driver. The hyperv_fb driver
> then informs Hyper-V of the change, which is allowed by the Hyper-V FB
> VMBus device protocol. However, when the kexec command loads the kdump
> kernel into crash memory via the kexec_file_load() system call, the
> system call doesn't know the framebuffer has moved, and it sets up the
> kdump screen_info using the original framebuffer address. The transition
> to the kdump kernel does not go through the Hyper-V host, so Hyper-V
> does not reset the framebuffer address like it would do on a reboot.
> When efifb tries to run, it accesses a non-existent framebuffer
> address, which traps to the Hyper-V host. After many such accesses,
> the Hyper-V host thinks the guest is being malicious, and throttles
> the guest to the point that it runs very slowly or appears to have hung.
>
> When the kdump kernel is loaded into crash memory via the kexec_load()
> system call, the problem does not occur. In this case, the kexec command
> builds the screen_info table itself in user space from data returned
> by the FBIOGET_FSCREENINFO ioctl against /dev/fb0, which gives it the
> new framebuffer location.
>
> This problem was originally reported in 2020 [1], resulting in commit
> 3cb73bc3fa2a ("hyperv_fb: Update screen_info after removing old
> framebuffer"). This commit solved the problem by setting orig_video_isVGA
> to 0, so the kdump kernel was unaware of the EFI framebuffer. The efifb
> driver did not try to load, and no hang occurred. But in 2024, commit
> c25a19afb81c ("fbdev/hyperv_fb: Do not clear global screen_info")
> effectively reverted 3cb73bc3fa2a. Commit c25a19afb81c has no reference
> to 3cb73bc3fa2a, so perhaps it was done without knowing the implications
> that were reported with 3cb73bc3fa2a. In any case, as of commit
> c25a19afb81c, the original problem came back again.
>
> Interestingly, the hyperv_drm driver does not have this problem because
> it never moves the framebuffer. The difference is that the hyperv_drm
> driver removes any conflicting framebuffers *before* allocating an MMIO
> address, while the hyperv_fb drivers removes conflicting framebuffers
> *after* allocating an MMIO address. With the "after" ordering, hyperv_fb
> may encounter a conflict and move the framebuffer to a different MMIO
> address. But the conflict is essentially bogus because it is removed
> a few lines of code later.
>
> Rather than fix the problem with the approach from 2020 in commit
> 3cb73bc3fa2a, instead slightly reorder the steps in hyperv_fb so
> conflicting framebuffers are removed before allocating an MMIO address.
> Then the default framebuffer MMIO address should always be available, and
> there's never any confusion about which framebuffer address the kdump
> kernel should use -- it's always the original address provided by
> the Hyper-V host. This approach is already used by the hyperv_drm
> driver, and is consistent with the usage guidelines at the head of
> the module with the function aperture_remove_conflicting_devices().
>
> This approach also solves a related minor problem when kexec_load()
> is used to load the kdump kernel. With current code, unbinding and
> rebinding the hyperv_fb driver could result in the framebuffer moving
> back to the default framebuffer address, because on the rebind there
> are no conflicts. If such a move is done after the kdump kernel is
> loaded with the new framebuffer address, at kdump time it could again
> have the wrong address.
>
> This problem and fix are described in terms of the kdump kernel, but
> it can also occur with any kernel started via kexec.
>
> See extensive discussion of the problem and solution at [2].
>
> [1] https://lore.kernel.org/linux-hyperv/20201014092429.1415040-1-kasong@redhat.com/
> [2] https://lore.kernel.org/linux-hyperv/BLAPR10MB521793485093FDB448F7B2E5FDE92@BLAPR10MB5217.namprd10.prod.outlook.com/
>
> Reported-by: Thomas Tai <thomas.tai@oracle.com>
> Fixes: c25a19afb81c ("fbdev/hyperv_fb: Do not clear global screen_info")
> Signed-off-by: Michael Kelley <mhklinux@outlook.com>
> ---
> The "Fixes" tag uses commit c25a19afb81c because that's where the problem
> was re-exposed, and how far back a stable backport is needed. But I've
> taken a completely different, and hopefully better, approach in the
> solution that isn't related to the code changes in c25a19afb81c.
>
>   drivers/video/fbdev/hyperv_fb.c | 20 +++++++++++++-------
>   1 file changed, 13 insertions(+), 7 deletions(-)

applied to fbdev tree.

Thanks!
Helge

^ permalink raw reply

* Re: [PATCH 0/1] Refactoring of framebuffer drawing routines
From: Helge Deller @ 2025-03-09  3:13 UTC (permalink / raw)
  To: Zsolt Kajtar, linux-fbdev, dri-devel
In-Reply-To: <20250224205908.26336-1-soci@c64.rulez.org>

On 2/24/25 21:59, Zsolt Kajtar wrote:
> 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

I'd like to give this patch a chance to live in for-next for a few days, although
I don't like such a big rewrite as one patch. But in this case it's probably necessary
to make an exception.

Important: please add a proper commit message to your patch!
Explain why you do that, and what advantages it gives.
Describe what should be different. Is something maybe faster or slower?

Then, the patch did not applied for me and git mentioned that the patch
was corrupt. Not sure yet, if the problem is on my side...

Helge


>   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
>


^ permalink raw reply

* RE: [PATCH 1/1] fbdev: hyperv_fb: Fix hang in kdump kernel when on Hyper-V Gen 2 VMs
From: Michael Kelley @ 2025-03-09  4:10 UTC (permalink / raw)
  To: Helge Deller, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, javierm@redhat.com, thomas.tai@oracle.com
  Cc: tzimmermann@suse.de, kasong@redhat.com,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org
In-Reply-To: <24668c7d-6333-423e-bd48-28af1431b263@gmx.de>

From: Helge Deller <deller@gmx.de> Sent: Saturday, March 8, 2025 6:59 PM
> 
> On 2/19/25 00:01, mhkelley58@gmail.com wrote:
> > From: Michael Kelley <mhklinux@outlook.com>
> >
[snip]

> >
> > Reported-by: Thomas Tai <thomas.tai@oracle.com>
> > Fixes: c25a19afb81c ("fbdev/hyperv_fb: Do not clear global screen_info")
> > Signed-off-by: Michael Kelley <mhklinux@outlook.com>
> > ---
> > The "Fixes" tag uses commit c25a19afb81c because that's where the problem
> > was re-exposed, and how far back a stable backport is needed. But I've
> > taken a completely different, and hopefully better, approach in the
> > solution that isn't related to the code changes in c25a19afb81c.
> >
> >   drivers/video/fbdev/hyperv_fb.c | 20 +++++++++++++-------
> >   1 file changed, 13 insertions(+), 7 deletions(-)
> 
> applied to fbdev tree.
> 

Thank you!

Related, I noticed the patch "fbdev: hyperv_fb: iounmap() the correct
memory when removing a device" is also in the fbdev for-next branch.
Wei Liu previously applied this patch to the hyperv-fixes tree (see [1])
and it's already in linux-next. Won't having it also in fbdev produce a
merge conflict?

Michael

[1] https://lore.kernel.org/linux-hyperv/Z6wHDw8BssJyQHiM@liuwe-devbox-debian-v2/

^ permalink raw reply

* [PATCH v2] fbdev: fsl-diu-fb: add missing device_remove_file()
From: Shixiong Ou @ 2025-03-09  8:16 UTC (permalink / raw)
  To: Timur Tabi
  Cc: Helge Deller, linux-fbdev, dri-devel, linux-kernel, Shixiong Ou

From: Shixiong Ou <oushixiong@kylinos.cn>

Call device_remove_file() when driver remove.

Signed-off-by: Shixiong Ou <oushixiong@kylinos.cn>
---
v1->v2:
	add has_sysfs_attrs flag.

 drivers/video/fbdev/fsl-diu-fb.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
index 5ac8201c3533..57f7fe6a4c76 100644
--- a/drivers/video/fbdev/fsl-diu-fb.c
+++ b/drivers/video/fbdev/fsl-diu-fb.c
@@ -384,6 +384,7 @@ struct fsl_diu_data {
 	__le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);
 	uint8_t edid_data[EDID_LENGTH];
 	bool has_edid;
+	bool has_dev_attr;
 } __aligned(32);
 
 /* Determine the DMA address of a member of the fsl_diu_data structure */
@@ -1809,6 +1810,7 @@ static int fsl_diu_probe(struct platform_device *pdev)
 			data->dev_attr.attr.name);
 	}
 
+	data->has_dev_attr = true;
 	dev_set_drvdata(&pdev->dev, data);
 	return 0;
 
@@ -1827,6 +1829,10 @@ static void fsl_diu_remove(struct platform_device *pdev)
 	int i;
 
 	data = dev_get_drvdata(&pdev->dev);
+
+	if (data->has_dev_attr)
+		device_remove_file(&pdev->dev, &data->dev_attr);
+
 	disable_lcdc(&data->fsl_diu_info[0]);
 
 	free_irq(data->irq, data->diu_reg);
-- 
2.43.0


^ permalink raw reply related

* [syzbot] Monthly fbdev report (Mar 2025)
From: syzbot @ 2025-03-09 12:42 UTC (permalink / raw)
  To: deller, dri-devel, linux-fbdev, linux-kernel, syzkaller-bugs

Hello fbdev maintainers/developers,

This is a 31-day syzbot report for the fbdev subsystem.
All related reports/information can be found at:
https://syzkaller.appspot.com/upstream/s/fbdev

During the period, 1 new issues were detected and 0 were fixed.
In total, 7 issues are still open and 25 have already been fixed.

Some of the still happening issues:

Ref Crashes Repro Title
<1> 844     Yes   KASAN: vmalloc-out-of-bounds Write in imageblit (4)
                  https://syzkaller.appspot.com/bug?extid=c4b7aa0513823e2ea880
<2> 15      Yes   KASAN: global-out-of-bounds Read in bit_putcs (3)
                  https://syzkaller.appspot.com/bug?extid=793cf822d213be1a74f2
<3> 8       No    BUG: unable to handle kernel paging request in bitfill_aligned (4)
                  https://syzkaller.appspot.com/bug?extid=66bde8e1e4161d4b2cca
<4> 7       Yes   KASAN: slab-out-of-bounds Read in fbcon_prepare_logo
                  https://syzkaller.appspot.com/bug?extid=0c815b25cdb3678e7083

---
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.

To disable reminders for individual bugs, reply with the following command:
#syz set <Ref> no-reminders

To change bug's subsystems, reply with:
#syz set <Ref> subsystems: new-subsystem

You may send multiple commands in a single email message.

^ permalink raw reply

* Re: [PATCH 1/1] fbdev: hyperv_fb: Fix hang in kdump kernel when on Hyper-V Gen 2 VMs
From: Helge Deller @ 2025-03-09 14:20 UTC (permalink / raw)
  To: Michael Kelley, wei.liu@kernel.org
  Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org
In-Reply-To: <SN6PR02MB4157594508B80319444C6C24D4D72@SN6PR02MB4157.namprd02.prod.outlook.com>

On 3/9/25 05:10, Michael Kelley wrote:
> From: Helge Deller <deller@gmx.de> Sent: Saturday, March 8, 2025 6:59 PM
>>
>> On 2/19/25 00:01, mhkelley58@gmail.com wrote:
>>> From: Michael Kelley <mhklinux@outlook.com>
>>>
> [snip]
>
>>>
>>> Reported-by: Thomas Tai <thomas.tai@oracle.com>
>>> Fixes: c25a19afb81c ("fbdev/hyperv_fb: Do not clear global screen_info")
>>> Signed-off-by: Michael Kelley <mhklinux@outlook.com>
>>> ---
>>> The "Fixes" tag uses commit c25a19afb81c because that's where the problem
>>> was re-exposed, and how far back a stable backport is needed. But I've
>>> taken a completely different, and hopefully better, approach in the
>>> solution that isn't related to the code changes in c25a19afb81c.
>>>
>>>    drivers/video/fbdev/hyperv_fb.c | 20 +++++++++++++-------
>>>    1 file changed, 13 insertions(+), 7 deletions(-)
>>
>> applied to fbdev tree.
>>
>
> Thank you!
>
> Related, I noticed the patch "fbdev: hyperv_fb: iounmap() the correct
> memory when removing a device" is also in the fbdev for-next branch.
> Wei Liu previously applied this patch to the hyperv-fixes tree (see [1])
> and it's already in linux-next. Won't having it also in fbdev produce a
> merge conflict?
> [1] https://lore.kernel.org/linux-hyperv/Z6wHDw8BssJyQHiM@liuwe-devbox-debian-v2/

Thanks Michael!
I now dropped that patch from the fbdev tree to avoid collisions.

Btw, I'm fine if we agree that all hyperv-fbdev fixes & patches go through
hyperv or other trees. Just let me know.

Helge

^ permalink raw reply

* [PATCH RESEND 1/2] Refactoring the fbcon packed pixel drawing routines
From: Zsolt Kajtar @ 2025-03-09 18:47 UTC (permalink / raw)
  To: linux-fbdev, dri-devel; +Cc: Zsolt Kajtar
In-Reply-To: <20250309184716.13732-1-soci@c64.rulez.org>

The original version duplicated more or less the same algorithms for
both system and i/o memory.

In this version the drawing algorithms (copy/fill/blit) are separate
from the memory access (system and i/o). The two parts are getting
combined in the loadable module sources. This also makes it more robust
against wrong memory access type or alignment mistakes as there's no
direct pointer access or arithmetic in the algorithm sources anymore.

Due to liberal use of inlining the compiled result is a single function
in all 6 cases, without unnecessary function calls. Unlike earlier the
use of macros could be minimized as apparently both gcc and clang is
capable now to do the same with inline functions just as well.

What wasn't quite the same in the two variants is the support for pixel
order reversing. This version is capable to do that for both system and
I/O memory, and not only for the latter. As demand for low bits per
pixel modes isn't high there's a configuration option to enable this
separately for the CFB and SYS modules.

The pixel reversing algorithm is different than earlier and was designed
so that it can take advantage of bit order reversing instructions on
architectures which have them. And even for higher bits per pixel modes
like four bpp.

One of the shortcomings of the earlier version was the incomplete
support for foreign endian framebuffers. Now all three drawing
algorithms produce correct output on both endians with native and
foreign framebuffers. This is one of the important differences even if
otherwise the algorithms don't look too different than before.

All three routines work now with aligned native word accesses. As a
consequence blitting isn't limited to 32 bits on 64 bit architectures as
it was before.

The old routines silently assumed that rows are a multiple of the word
size. Due to how the new routines function this isn't a requirement any
more and access will be done aligned regardless. However if the
framebuffer is configured like that then some of the fast paths won't be
available.

As this code is supposed to be running on all supported architectures it
wasn't optimized for a particular one. That doesn't mean I haven't
looked at the disassembly. That's where I noticed that it isn't a good
idea to use the fallback bitreversing code for example.

The low bits per pixel modes should be faster than before as the new
routines can blit 4 pixels at a time.

On the higher bits per pixel modes I retained the specialized aligned
routines so it should be more or less the same, except on 64 bit
architectures. There the blitting word size is double now which means 32
BPP isn't done a single pixel a time now.

The code was tested on x86, amd64, mips32 and mips64. The latter two in
big endian configuration. Originally thought I can get away with the
first two, but with such bit twisting code byte ordering is tricky and
not really possible to get right without actually verifying it.

While writing such routines isn't rocket science a lot of time was spent
on making sure that pixel ordering, foreign byte order, various bits per
pixels, cpu endianness and word size will give the expected result in
all sorts of combinations without making it overly complicated or full
with special cases.

Signed-off-by: Zsolt Kajtar <soci@c64.rulez.org>
---
 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 ++
 13 files changed, 1464 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/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..53f1d5385
--- /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

* [PATCH RESEND 2/2] Adding contact info for packed pixel drawing
From: Zsolt Kajtar @ 2025-03-09 18:47 UTC (permalink / raw)
  To: linux-fbdev, dri-devel; +Cc: Zsolt Kajtar
In-Reply-To: <20250309184716.13732-1-soci@c64.rulez.org>

Due to the nature of changes this is probably necessary. Even if these
drawing routines got way more testing than my patch submission scripts.

Signed-off-by: Zsolt Kajtar <soci@c64.rulez.org>
---
 MAINTAINERS | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0248c9eb3..ac9e23881 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9114,6 +9114,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
-- 
2.30.2


^ permalink raw reply related

* [PATCH RESEND 0/2] Refactoring the fbcon packed pixel drawing routines
From: Zsolt Kajtar @ 2025-03-09 18:47 UTC (permalink / raw)
  To: linux-fbdev, dri-devel; +Cc: Zsolt Kajtar

This is the same patch as before just updated to latest fbdev
master and with better description. And hopefully sent intact this
time.

Zsolt Kajtar (2):
  Refactoring the fbcon packed pixel drawing routines
  Adding contact info for packed pixel drawing

 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 0/9] Remove pcf50633
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

I've split this up based on the subcomponents to make the size
of each patch sensible.

Dave

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>


Dr. David Alan Gilbert (9):
  mfd: pcf50633-adc:  Remove
  backlight: pcf50633-backlight: Remove
  rtc: pcf50633: Remove
  mfd: pcF50633-gpio: Remove
  Input: pcf50633-input - Remove
  regulator: pcf50633-regulator: Remove
  power: supply: pcf50633: Remove charger
  mfd: pcf50633: Remove irq code
  mfd: pcf50633: Remove remains

 arch/mips/configs/ip27_defconfig             |   3 -
 drivers/input/misc/Kconfig                   |   7 -
 drivers/input/misc/Makefile                  |   1 -
 drivers/input/misc/pcf50633-input.c          | 113 -----
 drivers/mfd/Kconfig                          |  24 -
 drivers/mfd/Makefile                         |   4 -
 drivers/mfd/pcf50633-adc.c                   | 255 ----------
 drivers/mfd/pcf50633-core.c                  | 304 ------------
 drivers/mfd/pcf50633-gpio.c                  |  92 ----
 drivers/mfd/pcf50633-irq.c                   | 312 -------------
 drivers/power/supply/Kconfig                 |   6 -
 drivers/power/supply/Makefile                |   1 -
 drivers/power/supply/pcf50633-charger.c      | 466 -------------------
 drivers/regulator/Kconfig                    |   7 -
 drivers/regulator/Makefile                   |   1 -
 drivers/regulator/pcf50633-regulator.c       | 124 -----
 drivers/rtc/Kconfig                          |   7 -
 drivers/rtc/Makefile                         |   1 -
 drivers/rtc/rtc-pcf50633.c                   | 284 -----------
 drivers/video/backlight/Kconfig              |   7 -
 drivers/video/backlight/Makefile             |   1 -
 drivers/video/backlight/pcf50633-backlight.c | 154 ------
 include/linux/mfd/pcf50633/adc.h             |  69 ---
 include/linux/mfd/pcf50633/backlight.h       |  42 --
 include/linux/mfd/pcf50633/core.h            | 232 ---------
 include/linux/mfd/pcf50633/gpio.h            |  48 --
 include/linux/mfd/pcf50633/mbc.h             | 130 ------
 include/linux/mfd/pcf50633/pmic.h            |  68 ---
 28 files changed, 2763 deletions(-)
 delete mode 100644 drivers/input/misc/pcf50633-input.c
 delete mode 100644 drivers/mfd/pcf50633-adc.c
 delete mode 100644 drivers/mfd/pcf50633-core.c
 delete mode 100644 drivers/mfd/pcf50633-gpio.c
 delete mode 100644 drivers/mfd/pcf50633-irq.c
 delete mode 100644 drivers/power/supply/pcf50633-charger.c
 delete mode 100644 drivers/regulator/pcf50633-regulator.c
 delete mode 100644 drivers/rtc/rtc-pcf50633.c
 delete mode 100644 drivers/video/backlight/pcf50633-backlight.c
 delete mode 100644 include/linux/mfd/pcf50633/adc.h
 delete mode 100644 include/linux/mfd/pcf50633/backlight.h
 delete mode 100644 include/linux/mfd/pcf50633/core.h
 delete mode 100644 include/linux/mfd/pcf50633/gpio.h
 delete mode 100644 include/linux/mfd/pcf50633/mbc.h
 delete mode 100644 include/linux/mfd/pcf50633/pmic.h

-- 
2.48.1


^ permalink raw reply

* [PATCH 1/9] mfd: pcf50633-adc:  Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 arch/mips/configs/ip27_defconfig  |   1 -
 drivers/mfd/Kconfig               |   7 -
 drivers/mfd/Makefile              |   1 -
 drivers/mfd/pcf50633-adc.c        | 255 ------------------------------
 drivers/mfd/pcf50633-core.c       |   2 -
 include/linux/mfd/pcf50633/adc.h  |  69 --------
 include/linux/mfd/pcf50633/core.h |   1 -
 7 files changed, 336 deletions(-)
 delete mode 100644 drivers/mfd/pcf50633-adc.c
 delete mode 100644 include/linux/mfd/pcf50633/adc.h

diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index b08a199767d1..66085bb71bc4 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -256,7 +256,6 @@ CONFIG_I2C_STUB=m
 # CONFIG_HWMON is not set
 CONFIG_THERMAL=y
 CONFIG_MFD_PCF50633=m
-CONFIG_PCF50633_ADC=m
 CONFIG_PCF50633_GPIO=m
 # CONFIG_VGA_ARB is not set
 CONFIG_LEDS_LP3944=m
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6b0682af6e32..051272126fe1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1129,13 +1129,6 @@ config MFD_PCF50633
 	  facilities, and registers devices for the various functions
 	  so that function-specific drivers can bind to them.
 
-config PCF50633_ADC
-	tristate "NXP PCF50633 ADC"
-	depends on MFD_PCF50633
-	help
-	  Say yes here if you want to include support for ADC in the
-	  NXP PCF50633 chip.
-
 config PCF50633_GPIO
 	tristate "NXP PCF50633 GPIO"
 	depends on MFD_PCF50633
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9220eaf7cf12..e085da3f13c3 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,7 +185,6 @@ obj-$(CONFIG_MFD_MT6397)	+= mt6397.o
 
 pcf50633-objs			:= pcf50633-core.o pcf50633-irq.o
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
-obj-$(CONFIG_PCF50633_ADC)	+= pcf50633-adc.o
 obj-$(CONFIG_PCF50633_GPIO)	+= pcf50633-gpio.o
 obj-$(CONFIG_RZ_MTU3)		+= rz-mtu3.o
 obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
deleted file mode 100644
index 1fbba0e666d5..000000000000
--- a/drivers/mfd/pcf50633-adc.c
+++ /dev/null
@@ -1,255 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 ADC Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- *
- *  NOTE: This driver does not yet support subtractive ADC mode, which means
- *  you can do only one measurement per read request.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/completion.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/adc.h>
-
-struct pcf50633_adc_request {
-	int mux;
-	int avg;
-	void (*callback)(struct pcf50633 *, void *, int);
-	void *callback_param;
-};
-
-struct pcf50633_adc_sync_request {
-	int result;
-	struct completion completion;
-};
-
-#define PCF50633_MAX_ADC_FIFO_DEPTH 8
-
-struct pcf50633_adc {
-	struct pcf50633 *pcf;
-
-	/* Private stuff */
-	struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];
-	int queue_head;
-	int queue_tail;
-	struct mutex queue_mutex;
-};
-
-static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)
-{
-	return platform_get_drvdata(pcf->adc_pdev);
-}
-
-static void adc_setup(struct pcf50633 *pcf, int channel, int avg)
-{
-	channel &= PCF50633_ADCC1_ADCMUX_MASK;
-
-	/* kill ratiometric, but enable ACCSW biasing */
-	pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
-	pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
-
-	/* start ADC conversion on selected channel */
-	pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
-		    PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
-}
-
-static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
-{
-	struct pcf50633_adc *adc = __to_adc(pcf);
-	int head;
-
-	head = adc->queue_head;
-
-	if (!adc->queue[head])
-		return;
-
-	adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
-}
-
-static int
-adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
-{
-	struct pcf50633_adc *adc = __to_adc(pcf);
-	int head, tail;
-
-	mutex_lock(&adc->queue_mutex);
-
-	head = adc->queue_head;
-	tail = adc->queue_tail;
-
-	if (adc->queue[tail]) {
-		mutex_unlock(&adc->queue_mutex);
-		dev_err(pcf->dev, "ADC queue is full, dropping request\n");
-		return -EBUSY;
-	}
-
-	adc->queue[tail] = req;
-	if (head == tail)
-		trigger_next_adc_job_if_any(pcf);
-	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
-
-	mutex_unlock(&adc->queue_mutex);
-
-	return 0;
-}
-
-static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
-	int result)
-{
-	struct pcf50633_adc_sync_request *req = param;
-
-	req->result = result;
-	complete(&req->completion);
-}
-
-int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
-{
-	struct pcf50633_adc_sync_request req;
-	int ret;
-
-	init_completion(&req.completion);
-
-	ret = pcf50633_adc_async_read(pcf, mux, avg,
-		pcf50633_adc_sync_read_callback, &req);
-	if (ret)
-		return ret;
-
-	wait_for_completion(&req.completion);
-
-	return req.result;
-}
-EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
-
-int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
-			     void (*callback)(struct pcf50633 *, void *, int),
-			     void *callback_param)
-{
-	struct pcf50633_adc_request *req;
-	int ret;
-
-	/* req is freed when the result is ready, in interrupt handler */
-	req = kmalloc(sizeof(*req), GFP_KERNEL);
-	if (!req)
-		return -ENOMEM;
-
-	req->mux = mux;
-	req->avg = avg;
-	req->callback = callback;
-	req->callback_param = callback_param;
-
-	ret = adc_enqueue_request(pcf, req);
-	if (ret)
-		kfree(req);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
-
-static int adc_result(struct pcf50633 *pcf)
-{
-	u8 adcs1, adcs3;
-	u16 result;
-
-	adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);
-	adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);
-	result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
-
-	dev_dbg(pcf->dev, "adc result = %d\n", result);
-
-	return result;
-}
-
-static void pcf50633_adc_irq(int irq, void *data)
-{
-	struct pcf50633_adc *adc = data;
-	struct pcf50633 *pcf = adc->pcf;
-	struct pcf50633_adc_request *req;
-	int head, res;
-
-	mutex_lock(&adc->queue_mutex);
-	head = adc->queue_head;
-
-	req = adc->queue[head];
-	if (WARN_ON(!req)) {
-		dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
-		mutex_unlock(&adc->queue_mutex);
-		return;
-	}
-	adc->queue[head] = NULL;
-	adc->queue_head = (head + 1) &
-				      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
-
-	res = adc_result(pcf);
-	trigger_next_adc_job_if_any(pcf);
-
-	mutex_unlock(&adc->queue_mutex);
-
-	req->callback(pcf, req->callback_param, res);
-	kfree(req);
-}
-
-static int pcf50633_adc_probe(struct platform_device *pdev)
-{
-	struct pcf50633_adc *adc;
-
-	adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
-	if (!adc)
-		return -ENOMEM;
-
-	adc->pcf = dev_to_pcf50633(pdev->dev.parent);
-	platform_set_drvdata(pdev, adc);
-
-	pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
-					pcf50633_adc_irq, adc);
-
-	mutex_init(&adc->queue_mutex);
-
-	return 0;
-}
-
-static void pcf50633_adc_remove(struct platform_device *pdev)
-{
-	struct pcf50633_adc *adc = platform_get_drvdata(pdev);
-	int i, head;
-
-	pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);
-
-	mutex_lock(&adc->queue_mutex);
-	head = adc->queue_head;
-
-	if (WARN_ON(adc->queue[head]))
-		dev_err(adc->pcf->dev,
-			"adc driver removed with request pending\n");
-
-	for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)
-		kfree(adc->queue[i]);
-
-	mutex_unlock(&adc->queue_mutex);
-}
-
-static struct platform_driver pcf50633_adc_driver = {
-	.driver = {
-		.name = "pcf50633-adc",
-	},
-	.probe = pcf50633_adc_probe,
-	.remove = pcf50633_adc_remove,
-};
-
-module_platform_driver(pcf50633_adc_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 adc driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-adc");
-
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 014a68711b18..70f8bbb89f76 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -210,7 +210,6 @@ static int pcf50633_probe(struct i2c_client *client)
 	pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
-	pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
 
 
@@ -263,7 +262,6 @@ static void pcf50633_remove(struct i2c_client *client)
 	platform_device_unregister(pcf->input_pdev);
 	platform_device_unregister(pcf->rtc_pdev);
 	platform_device_unregister(pcf->mbc_pdev);
-	platform_device_unregister(pcf->adc_pdev);
 	platform_device_unregister(pcf->bl_pdev);
 
 	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
diff --git a/include/linux/mfd/pcf50633/adc.h b/include/linux/mfd/pcf50633/adc.h
deleted file mode 100644
index 6a81896d4889..000000000000
--- a/include/linux/mfd/pcf50633/adc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * adc.h  -- Driver for NXP PCF50633 ADC
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * All rights reserved.
- */
-
-#ifndef __LINUX_MFD_PCF50633_ADC_H
-#define __LINUX_MFD_PCF50633_ADC_H
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/platform_device.h>
-
-/* ADC Registers */
-#define PCF50633_REG_ADCC3		0x52
-#define PCF50633_REG_ADCC2		0x53
-#define PCF50633_REG_ADCC1		0x54
-#define PCF50633_REG_ADCS1		0x55
-#define PCF50633_REG_ADCS2		0x56
-#define PCF50633_REG_ADCS3		0x57
-
-#define PCF50633_ADCC1_ADCSTART		0x01
-#define PCF50633_ADCC1_RES_8BIT		0x02
-#define PCF50633_ADCC1_RES_10BIT	0x00
-#define PCF50633_ADCC1_AVERAGE_NO	0x00
-#define PCF50633_ADCC1_AVERAGE_4	0x04
-#define PCF50633_ADCC1_AVERAGE_8	0x08
-#define PCF50633_ADCC1_AVERAGE_16	0x0c
-#define PCF50633_ADCC1_MUX_BATSNS_RES	0x00
-#define PCF50633_ADCC1_MUX_BATSNS_SUBTR	0x10
-#define PCF50633_ADCC1_MUX_ADCIN2_RES	0x20
-#define PCF50633_ADCC1_MUX_ADCIN2_SUBTR	0x30
-#define PCF50633_ADCC1_MUX_BATTEMP	0x60
-#define PCF50633_ADCC1_MUX_ADCIN1	0x70
-#define PCF50633_ADCC1_AVERAGE_MASK	0x0c
-#define PCF50633_ADCC1_ADCMUX_MASK	0xf0
-
-#define PCF50633_ADCC2_RATIO_NONE	0x00
-#define PCF50633_ADCC2_RATIO_BATTEMP	0x01
-#define PCF50633_ADCC2_RATIO_ADCIN1	0x02
-#define PCF50633_ADCC2_RATIO_BOTH	0x03
-#define PCF50633_ADCC2_RATIOSETTL_100US 0x04
-
-#define PCF50633_ADCC3_ACCSW_EN		0x01
-#define PCF50633_ADCC3_NTCSW_EN		0x04
-#define PCF50633_ADCC3_RES_DIV_TWO	0x10
-#define PCF50633_ADCC3_RES_DIV_THREE	0x00
-
-#define PCF50633_ADCS3_REF_NTCSW	0x00
-#define PCF50633_ADCS3_REF_ACCSW	0x10
-#define PCF50633_ADCS3_REF_2V0		0x20
-#define PCF50633_ADCS3_REF_VISA		0x30
-#define PCF50633_ADCS3_REF_2V0_2	0x70
-#define PCF50633_ADCS3_ADCRDY		0x80
-
-#define PCF50633_ADCS3_ADCDAT1L_MASK	0x03
-#define PCF50633_ADCS3_ADCDAT2L_MASK	0x0c
-#define PCF50633_ADCS3_ADCDAT2L_SHIFT	2
-#define PCF50633_ASCS3_REF_MASK		0x70
-
-extern int
-pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
-		void (*callback)(struct pcf50633 *, void *, int),
-		void *callback_param);
-extern int
-pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg);
-
-#endif /* __LINUX_PCF50633_ADC_H */
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index 539f27f8bd89..cbf3054fd8fe 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -151,7 +151,6 @@ struct pcf50633 {
 
 	struct platform_device *rtc_pdev;
 	struct platform_device *mbc_pdev;
-	struct platform_device *adc_pdev;
 	struct platform_device *input_pdev;
 	struct platform_device *bl_pdev;
 	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
-- 
2.48.1


^ permalink raw reply related

* [PATCH 2/9] backlight: pcf50633-backlight: Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/mfd/pcf50633-core.c                  |   2 -
 drivers/video/backlight/Kconfig              |   7 -
 drivers/video/backlight/Makefile             |   1 -
 drivers/video/backlight/pcf50633-backlight.c | 154 -------------------
 include/linux/mfd/pcf50633/backlight.h       |  42 -----
 include/linux/mfd/pcf50633/core.h            |   4 -
 6 files changed, 210 deletions(-)
 delete mode 100644 drivers/video/backlight/pcf50633-backlight.c
 delete mode 100644 include/linux/mfd/pcf50633/backlight.h

diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 70f8bbb89f76..08aa68ef2fbc 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -210,7 +210,6 @@ static int pcf50633_probe(struct i2c_client *client)
 	pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
-	pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
 
 
 	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
@@ -262,7 +261,6 @@ static void pcf50633_remove(struct i2c_client *client)
 	platform_device_unregister(pcf->input_pdev);
 	platform_device_unregister(pcf->rtc_pdev);
 	platform_device_unregister(pcf->mbc_pdev);
-	platform_device_unregister(pcf->bl_pdev);
 
 	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
 		platform_device_unregister(pcf->regulator_pdev[i]);
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 3614a5d29c71..ef4ac1ac7520 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -359,13 +359,6 @@ config BACKLIGHT_88PM860X
 	help
 	  Say Y to enable the backlight driver for Marvell 88PM8606.
 
-config BACKLIGHT_PCF50633
-	tristate "Backlight driver for NXP PCF50633 MFD"
-	depends on MFD_PCF50633
-	help
-	  If you have a backlight driven by a NXP PCF50633 MFD, say Y here to
-	  enable its driver.
-
 config BACKLIGHT_AAT2870
 	tristate "AnalogicTech AAT2870 Backlight"
 	depends on MFD_AAT2870_CORE
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 8fc98f760a8a..21bf62bcaccf 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -49,7 +49,6 @@ obj-$(CONFIG_BACKLIGHT_MP3309C)		+= mp3309c.o
 obj-$(CONFIG_BACKLIGHT_MT6370)		+= mt6370-backlight.o
 obj-$(CONFIG_BACKLIGHT_OMAP1)		+= omap1_bl.o
 obj-$(CONFIG_BACKLIGHT_PANDORA)		+= pandora_bl.o
-obj-$(CONFIG_BACKLIGHT_PCF50633)	+= pcf50633-backlight.o
 obj-$(CONFIG_BACKLIGHT_PWM)		+= pwm_bl.o
 obj-$(CONFIG_BACKLIGHT_QCOM_WLED)	+= qcom-wled.o
 obj-$(CONFIG_BACKLIGHT_RT4831)		+= rt4831-backlight.o
diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c
deleted file mode 100644
index 157be2f366df..000000000000
--- a/drivers/video/backlight/pcf50633-backlight.c
+++ /dev/null
@@ -1,154 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- *      PCF50633 backlight device driver
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-
-#include <linux/backlight.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/backlight.h>
-
-struct pcf50633_bl {
-	struct pcf50633 *pcf;
-	struct backlight_device *bl;
-
-	unsigned int brightness;
-	unsigned int brightness_limit;
-};
-
-/*
- * pcf50633_bl_set_brightness_limit
- *
- * Update the brightness limit for the pc50633 backlight. The actual brightness
- * will not go above the limit. This is useful to limit power drain for example
- * on low battery.
- *
- * @dev: Pointer to a pcf50633 device
- * @limit: The brightness limit. Valid values are 0-63
- */
-int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit)
-{
-	struct pcf50633_bl *pcf_bl = platform_get_drvdata(pcf->bl_pdev);
-
-	if (!pcf_bl)
-		return -ENODEV;
-
-	pcf_bl->brightness_limit = limit & 0x3f;
-	backlight_update_status(pcf_bl->bl);
-
-	return 0;
-}
-
-static int pcf50633_bl_update_status(struct backlight_device *bl)
-{
-	struct pcf50633_bl *pcf_bl = bl_get_data(bl);
-	unsigned int new_brightness;
-
-
-	if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK) ||
-		bl->props.power != BACKLIGHT_POWER_ON)
-		new_brightness = 0;
-	else if (bl->props.brightness < pcf_bl->brightness_limit)
-		new_brightness = bl->props.brightness;
-	else
-		new_brightness = pcf_bl->brightness_limit;
-
-
-	if (pcf_bl->brightness == new_brightness)
-		return 0;
-
-	if (new_brightness) {
-		pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDOUT,
-					new_brightness);
-		if (!pcf_bl->brightness)
-			pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 1);
-	} else {
-		pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 0);
-	}
-
-	pcf_bl->brightness = new_brightness;
-
-	return 0;
-}
-
-static int pcf50633_bl_get_brightness(struct backlight_device *bl)
-{
-	struct pcf50633_bl *pcf_bl = bl_get_data(bl);
-
-	return pcf_bl->brightness;
-}
-
-static const struct backlight_ops pcf50633_bl_ops = {
-	.get_brightness = pcf50633_bl_get_brightness,
-	.update_status	= pcf50633_bl_update_status,
-	.options	= BL_CORE_SUSPENDRESUME,
-};
-
-static int pcf50633_bl_probe(struct platform_device *pdev)
-{
-	struct pcf50633_bl *pcf_bl;
-	struct device *parent = pdev->dev.parent;
-	struct pcf50633_platform_data *pcf50633_data = dev_get_platdata(parent);
-	struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data;
-	struct backlight_properties bl_props;
-
-	pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL);
-	if (!pcf_bl)
-		return -ENOMEM;
-
-	memset(&bl_props, 0, sizeof(bl_props));
-	bl_props.type = BACKLIGHT_RAW;
-	bl_props.max_brightness = 0x3f;
-	bl_props.power = BACKLIGHT_POWER_ON;
-
-	if (pdata) {
-		bl_props.brightness = pdata->default_brightness;
-		pcf_bl->brightness_limit = pdata->default_brightness_limit;
-	} else {
-		bl_props.brightness = 0x3f;
-		pcf_bl->brightness_limit = 0x3f;
-	}
-
-	pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent);
-
-	pcf_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
-						&pdev->dev, pcf_bl,
-						&pcf50633_bl_ops, &bl_props);
-
-	if (IS_ERR(pcf_bl->bl))
-		return PTR_ERR(pcf_bl->bl);
-
-	platform_set_drvdata(pdev, pcf_bl);
-
-	pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time);
-
-	/*
-	 * Should be different from bl_props.brightness, so we do not exit
-	 * update_status early the first time it's called
-	 */
-	pcf_bl->brightness = pcf_bl->bl->props.brightness + 1;
-
-	backlight_update_status(pcf_bl->bl);
-
-	return 0;
-}
-
-static struct platform_driver pcf50633_bl_driver = {
-	.probe =	pcf50633_bl_probe,
-	.driver = {
-		.name = "pcf50633-backlight",
-	},
-};
-
-module_platform_driver(pcf50633_bl_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("PCF50633 backlight driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-backlight");
diff --git a/include/linux/mfd/pcf50633/backlight.h b/include/linux/mfd/pcf50633/backlight.h
deleted file mode 100644
index fd4a4f8d6c13..000000000000
--- a/include/linux/mfd/pcf50633/backlight.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- *      PCF50633 backlight device driver
- */
-
-#ifndef __LINUX_MFD_PCF50633_BACKLIGHT
-#define __LINUX_MFD_PCF50633_BACKLIGHT
-
-/*
-* @default_brightness: Backlight brightness is initialized to this value
-*
-* Brightness to be used after the driver has been probed.
-* Valid range 0-63.
-*
-* @default_brightness_limit: The actual brightness is limited by this value
-*
-* Brightness limit to be used after the driver has been probed. This is useful
-* when it is not known how much power is available for the backlight during
-* probe.
-* Valid range 0-63. Can be changed later with pcf50633_bl_set_brightness_limit.
-*
-* @ramp_time: Display ramp time when changing brightness
-*
-* When changing the backlights brightness the change is not instant, instead
-* it fades smooth from one state to another. This value specifies how long
-* the fade should take. The lower the value the higher the fade time.
-* Valid range 0-255
-*/
-struct pcf50633_bl_platform_data {
-	unsigned int	default_brightness;
-	unsigned int	default_brightness_limit;
-	uint8_t		ramp_time;
-};
-
-
-struct pcf50633;
-
-int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit);
-
-#endif
-
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index cbf3054fd8fe..42e0412fa98f 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -15,7 +15,6 @@
 #include <linux/regulator/machine.h>
 #include <linux/pm.h>
 #include <linux/power_supply.h>
-#include <linux/mfd/pcf50633/backlight.h>
 
 struct pcf50633;
 struct regmap;
@@ -42,8 +41,6 @@ struct pcf50633_platform_data {
 	void (*force_shutdown)(struct pcf50633 *);
 
 	u8 resumers[5];
-
-	struct pcf50633_bl_platform_data *backlight_data;
 };
 
 struct pcf50633_irq {
@@ -152,7 +149,6 @@ struct pcf50633 {
 	struct platform_device *rtc_pdev;
 	struct platform_device *mbc_pdev;
 	struct platform_device *input_pdev;
-	struct platform_device *bl_pdev;
 	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
 };
 
-- 
2.48.1


^ permalink raw reply related

* [PATCH 3/9] rtc: pcf50633: Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/mfd/pcf50633-core.c       |   2 -
 drivers/rtc/Kconfig               |   7 -
 drivers/rtc/Makefile              |   1 -
 drivers/rtc/rtc-pcf50633.c        | 284 ------------------------------
 include/linux/mfd/pcf50633/core.h |   1 -
 5 files changed, 295 deletions(-)
 delete mode 100644 drivers/rtc/rtc-pcf50633.c

diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 08aa68ef2fbc..d991a77f6dd2 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -208,7 +208,6 @@ static int pcf50633_probe(struct i2c_client *client)
 
 	/* Create sub devices */
 	pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
-	pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
 
 
@@ -259,7 +258,6 @@ static void pcf50633_remove(struct i2c_client *client)
 	pcf50633_irq_free(pcf);
 
 	platform_device_unregister(pcf->input_pdev);
-	platform_device_unregister(pcf->rtc_pdev);
 	platform_device_unregister(pcf->mbc_pdev);
 
 	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0bbbf778ecfa..838bdc138ffe 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1321,13 +1321,6 @@ config RTC_DRV_SPEAR
 	 If you say Y here you will get support for the RTC found on
 	 spear
 
-config RTC_DRV_PCF50633
-	depends on MFD_PCF50633
-	tristate "NXP PCF50633 RTC"
-	help
-	  If you say yes here you get support for the RTC subsystem of the
-	  NXP PCF50633 used in embedded systems.
-
 config RTC_DRV_AB8500
 	tristate "ST-Ericsson AB8500 RTC"
 	depends on AB8500_CORE
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 489b4ab07068..31473b3276d9 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -126,7 +126,6 @@ obj-$(CONFIG_RTC_DRV_PALMAS)	+= rtc-palmas.o
 obj-$(CONFIG_RTC_DRV_PCAP)	+= rtc-pcap.o
 obj-$(CONFIG_RTC_DRV_PCF2123)	+= rtc-pcf2123.o
 obj-$(CONFIG_RTC_DRV_PCF2127)	+= rtc-pcf2127.o
-obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
 obj-$(CONFIG_RTC_DRV_PCF85063)	+= rtc-pcf85063.o
 obj-$(CONFIG_RTC_DRV_PCF8523)	+= rtc-pcf8523.o
 obj-$(CONFIG_RTC_DRV_PCF85363)	+= rtc-pcf85363.o
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
deleted file mode 100644
index c019c4d91c7d..000000000000
--- a/drivers/rtc/rtc-pcf50633.c
+++ /dev/null
@@ -1,284 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 RTC Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-#include <linux/err.h>
-
-#include <linux/mfd/pcf50633/core.h>
-
-#define PCF50633_REG_RTCSC	0x59 /* Second */
-#define PCF50633_REG_RTCMN	0x5a /* Minute */
-#define PCF50633_REG_RTCHR	0x5b /* Hour */
-#define PCF50633_REG_RTCWD	0x5c /* Weekday */
-#define PCF50633_REG_RTCDT	0x5d /* Day */
-#define PCF50633_REG_RTCMT	0x5e /* Month */
-#define PCF50633_REG_RTCYR	0x5f /* Year */
-#define PCF50633_REG_RTCSCA	0x60 /* Alarm Second */
-#define PCF50633_REG_RTCMNA	0x61 /* Alarm Minute */
-#define PCF50633_REG_RTCHRA	0x62 /* Alarm Hour */
-#define PCF50633_REG_RTCWDA	0x63 /* Alarm Weekday */
-#define PCF50633_REG_RTCDTA	0x64 /* Alarm Day */
-#define PCF50633_REG_RTCMTA	0x65 /* Alarm Month */
-#define PCF50633_REG_RTCYRA	0x66 /* Alarm Year */
-
-enum pcf50633_time_indexes {
-	PCF50633_TI_SEC,
-	PCF50633_TI_MIN,
-	PCF50633_TI_HOUR,
-	PCF50633_TI_WKDAY,
-	PCF50633_TI_DAY,
-	PCF50633_TI_MONTH,
-	PCF50633_TI_YEAR,
-	PCF50633_TI_EXTENT /* always last */
-};
-
-struct pcf50633_time {
-	u_int8_t time[PCF50633_TI_EXTENT];
-};
-
-struct pcf50633_rtc {
-	int alarm_enabled;
-	int alarm_pending;
-
-	struct pcf50633 *pcf;
-	struct rtc_device *rtc_dev;
-};
-
-static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
-{
-	rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);
-	rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);
-	rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
-	rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
-	rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
-	rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1;
-	rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
-}
-
-static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
-{
-	pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);
-	pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);
-	pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
-	pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
-	pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
-	pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1);
-	pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
-}
-
-static int
-pcf50633_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
-	struct pcf50633_rtc *rtc = dev_get_drvdata(dev);
-	int err;
-
-	if (enabled)
-		err = pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
-	else
-		err = pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	if (err < 0)
-		return err;
-
-	rtc->alarm_enabled = enabled;
-
-	return 0;
-}
-
-static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
-	struct pcf50633_rtc *rtc;
-	struct pcf50633_time pcf_tm;
-	int ret;
-
-	rtc = dev_get_drvdata(dev);
-
-	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,
-					    PCF50633_TI_EXTENT,
-					    &pcf_tm.time[0]);
-	if (ret != PCF50633_TI_EXTENT) {
-		dev_err(dev, "Failed to read time\n");
-		return -EIO;
-	}
-
-	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
-		pcf_tm.time[PCF50633_TI_DAY],
-		pcf_tm.time[PCF50633_TI_MONTH],
-		pcf_tm.time[PCF50633_TI_YEAR],
-		pcf_tm.time[PCF50633_TI_HOUR],
-		pcf_tm.time[PCF50633_TI_MIN],
-		pcf_tm.time[PCF50633_TI_SEC]);
-
-	pcf2rtc_time(tm, &pcf_tm);
-
-	dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
-
-	return 0;
-}
-
-static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
-	struct pcf50633_rtc *rtc;
-	struct pcf50633_time pcf_tm;
-	int alarm_masked, ret = 0;
-
-	rtc = dev_get_drvdata(dev);
-
-	dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
-
-	rtc2pcf_time(&pcf_tm, tm);
-
-	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
-		pcf_tm.time[PCF50633_TI_DAY],
-		pcf_tm.time[PCF50633_TI_MONTH],
-		pcf_tm.time[PCF50633_TI_YEAR],
-		pcf_tm.time[PCF50633_TI_HOUR],
-		pcf_tm.time[PCF50633_TI_MIN],
-		pcf_tm.time[PCF50633_TI_SEC]);
-
-
-	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	if (!alarm_masked)
-		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	/* Returns 0 on success */
-	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,
-					     PCF50633_TI_EXTENT,
-					     &pcf_tm.time[0]);
-
-	if (!alarm_masked)
-		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	return ret;
-}
-
-static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
-	struct pcf50633_rtc *rtc;
-	struct pcf50633_time pcf_tm;
-	int ret = 0;
-
-	rtc = dev_get_drvdata(dev);
-
-	alrm->enabled = rtc->alarm_enabled;
-	alrm->pending = rtc->alarm_pending;
-
-	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
-				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
-	if (ret != PCF50633_TI_EXTENT) {
-		dev_err(dev, "Failed to read time\n");
-		return -EIO;
-	}
-
-	pcf2rtc_time(&alrm->time, &pcf_tm);
-
-	return rtc_valid_tm(&alrm->time);
-}
-
-static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
-	struct pcf50633_rtc *rtc;
-	struct pcf50633_time pcf_tm;
-	int alarm_masked, ret = 0;
-
-	rtc = dev_get_drvdata(dev);
-
-	rtc2pcf_time(&pcf_tm, &alrm->time);
-
-	/* do like mktime does and ignore tm_wday */
-	pcf_tm.time[PCF50633_TI_WKDAY] = 7;
-
-	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	/* disable alarm interrupt */
-	if (!alarm_masked)
-		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
-
-	/* Returns 0 on success */
-	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
-				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
-	if (!alrm->enabled)
-		rtc->alarm_pending = 0;
-
-	if (!alarm_masked || alrm->enabled)
-		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
-	rtc->alarm_enabled = alrm->enabled;
-
-	return ret;
-}
-
-static const struct rtc_class_ops pcf50633_rtc_ops = {
-	.read_time		= pcf50633_rtc_read_time,
-	.set_time		= pcf50633_rtc_set_time,
-	.read_alarm		= pcf50633_rtc_read_alarm,
-	.set_alarm		= pcf50633_rtc_set_alarm,
-	.alarm_irq_enable	= pcf50633_rtc_alarm_irq_enable,
-};
-
-static void pcf50633_rtc_irq(int irq, void *data)
-{
-	struct pcf50633_rtc *rtc = data;
-
-	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
-	rtc->alarm_pending = 1;
-}
-
-static int pcf50633_rtc_probe(struct platform_device *pdev)
-{
-	struct pcf50633_rtc *rtc;
-
-	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
-	if (!rtc)
-		return -ENOMEM;
-
-	rtc->pcf = dev_to_pcf50633(pdev->dev.parent);
-	platform_set_drvdata(pdev, rtc);
-	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "pcf50633-rtc",
-				&pcf50633_rtc_ops, THIS_MODULE);
-
-	if (IS_ERR(rtc->rtc_dev))
-		return PTR_ERR(rtc->rtc_dev);
-
-	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
-					pcf50633_rtc_irq, rtc);
-	return 0;
-}
-
-static void pcf50633_rtc_remove(struct platform_device *pdev)
-{
-	struct pcf50633_rtc *rtc;
-
-	rtc = platform_get_drvdata(pdev);
-	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
-}
-
-static struct platform_driver pcf50633_rtc_driver = {
-	.driver = {
-		.name = "pcf50633-rtc",
-	},
-	.probe = pcf50633_rtc_probe,
-	.remove = pcf50633_rtc_remove,
-};
-
-module_platform_driver(pcf50633_rtc_driver);
-
-MODULE_DESCRIPTION("PCF50633 RTC driver");
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_LICENSE("GPL");
-
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index 42e0412fa98f..f5ab3e64c230 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -146,7 +146,6 @@ struct pcf50633 {
 
 	int onkey1s_held;
 
-	struct platform_device *rtc_pdev;
 	struct platform_device *mbc_pdev;
 	struct platform_device *input_pdev;
 	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
-- 
2.48.1


^ permalink raw reply related

* [PATCH 4/9] mfd: pcF50633-gpio: Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 arch/mips/configs/ip27_defconfig  |  1 -
 drivers/mfd/Kconfig               |  7 ---
 drivers/mfd/Makefile              |  1 -
 drivers/mfd/pcf50633-gpio.c       | 92 -------------------------------
 include/linux/mfd/pcf50633/gpio.h | 48 ----------------
 5 files changed, 149 deletions(-)
 delete mode 100644 drivers/mfd/pcf50633-gpio.c
 delete mode 100644 include/linux/mfd/pcf50633/gpio.h

diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 66085bb71bc4..0a9ec09aff65 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -256,7 +256,6 @@ CONFIG_I2C_STUB=m
 # CONFIG_HWMON is not set
 CONFIG_THERMAL=y
 CONFIG_MFD_PCF50633=m
-CONFIG_PCF50633_GPIO=m
 # CONFIG_VGA_ARB is not set
 CONFIG_LEDS_LP3944=m
 CONFIG_LEDS_PCA955X=m
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 051272126fe1..766453ef6c2d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1129,13 +1129,6 @@ config MFD_PCF50633
 	  facilities, and registers devices for the various functions
 	  so that function-specific drivers can bind to them.
 
-config PCF50633_GPIO
-	tristate "NXP PCF50633 GPIO"
-	depends on MFD_PCF50633
-	help
-	  Say yes here if you want to include support GPIO for pins on
-	  the PCF50633 chip.
-
 config MFD_PM8XXX
 	tristate "Qualcomm PM8xxx PMIC chips driver"
 	depends on ARM || HEXAGON || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e085da3f13c3..d769e40251c8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,7 +185,6 @@ obj-$(CONFIG_MFD_MT6397)	+= mt6397.o
 
 pcf50633-objs			:= pcf50633-core.o pcf50633-irq.o
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
-obj-$(CONFIG_PCF50633_GPIO)	+= pcf50633-gpio.o
 obj-$(CONFIG_RZ_MTU3)		+= rz-mtu3.o
 obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
 obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c
deleted file mode 100644
index 3e368219479a..000000000000
--- a/drivers/mfd/pcf50633-gpio.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 GPIO Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/gpio.h>
-#include <linux/mfd/pcf50633/pmic.h>
-
-static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
-	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT,
-	[PCF50633_REGULATOR_DOWN1]	= PCF50633_REG_DOWN1OUT,
-	[PCF50633_REGULATOR_DOWN2]	= PCF50633_REG_DOWN2OUT,
-	[PCF50633_REGULATOR_MEMLDO]	= PCF50633_REG_MEMLDOOUT,
-	[PCF50633_REGULATOR_LDO1]	= PCF50633_REG_LDO1OUT,
-	[PCF50633_REGULATOR_LDO2]	= PCF50633_REG_LDO2OUT,
-	[PCF50633_REGULATOR_LDO3]	= PCF50633_REG_LDO3OUT,
-	[PCF50633_REGULATOR_LDO4]	= PCF50633_REG_LDO4OUT,
-	[PCF50633_REGULATOR_LDO5]	= PCF50633_REG_LDO5OUT,
-	[PCF50633_REGULATOR_LDO6]	= PCF50633_REG_LDO6OUT,
-	[PCF50633_REGULATOR_HCLDO]	= PCF50633_REG_HCLDOOUT,
-};
-
-int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val)
-{
-	u8 reg;
-
-	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
-
-	return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_set);
-
-u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio)
-{
-	u8 reg, val;
-
-	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
-	val = pcf50633_reg_read(pcf, reg) & 0x07;
-
-	return val;
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_get);
-
-int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert)
-{
-	u8 val, reg;
-
-	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
-	val = !!invert << 3;
-
-	return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set);
-
-int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio)
-{
-	u8 reg, val;
-
-	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
-	val = pcf50633_reg_read(pcf, reg);
-
-	return val & (1 << 3);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get);
-
-int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf,
-					int gpio, int regulator, int on)
-{
-	u8 reg, val, mask;
-
-	/* the *ENA register is always one after the *OUT register */
-	reg = pcf50633_regulator_registers[regulator] + 1;
-
-	val = !!on << (gpio - PCF50633_GPIO1);
-	mask = 1 << (gpio - PCF50633_GPIO1);
-
-	return pcf50633_reg_set_bit_mask(pcf, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set);
-
-MODULE_DESCRIPTION("NXP PCF50633 GPIO Driver");
-MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/pcf50633/gpio.h b/include/linux/mfd/pcf50633/gpio.h
deleted file mode 100644
index f589e35795f1..000000000000
--- a/include/linux/mfd/pcf50633/gpio.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * gpio.h -- GPIO driver for NXP PCF50633
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * All rights reserved.
- */
-
-#ifndef __LINUX_MFD_PCF50633_GPIO_H
-#define __LINUX_MFD_PCF50633_GPIO_H
-
-#include <linux/mfd/pcf50633/core.h>
-
-#define PCF50633_GPIO1		1
-#define PCF50633_GPIO2		2
-#define PCF50633_GPIO3		3
-#define PCF50633_GPO		4
-
-#define PCF50633_REG_GPIO1CFG	0x14
-#define PCF50633_REG_GPIO2CFG	0x15
-#define PCF50633_REG_GPIO3CFG	0x16
-#define PCF50633_REG_GPOCFG 	0x17
-
-#define PCF50633_GPOCFG_GPOSEL_MASK	0x07
-
-enum pcf50633_reg_gpocfg {
-	PCF50633_GPOCFG_GPOSEL_0	= 0x00,
-	PCF50633_GPOCFG_GPOSEL_LED_NFET	= 0x01,
-	PCF50633_GPOCFG_GPOSEL_SYSxOK	= 0x02,
-	PCF50633_GPOCFG_GPOSEL_CLK32K	= 0x03,
-	PCF50633_GPOCFG_GPOSEL_ADAPUSB	= 0x04,
-	PCF50633_GPOCFG_GPOSEL_USBxOK	= 0x05,
-	PCF50633_GPOCFG_GPOSEL_ACTPH4	= 0x06,
-	PCF50633_GPOCFG_GPOSEL_1	= 0x07,
-	PCF50633_GPOCFG_GPOSEL_INVERSE	= 0x08,
-};
-
-int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val);
-u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio);
-
-int pcf50633_gpio_invert_set(struct pcf50633 *, int gpio, int invert);
-int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio);
-
-int pcf50633_gpio_power_supply_set(struct pcf50633 *,
-					int gpio, int regulator, int on);
-#endif /* __LINUX_MFD_PCF50633_GPIO_H */
-
-
-- 
2.48.1


^ permalink raw reply related

* [PATCH 5/9] Input: pcf50633-input - Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/input/misc/Kconfig          |   7 --
 drivers/input/misc/Makefile         |   1 -
 drivers/input/misc/pcf50633-input.c | 113 ----------------------------
 drivers/mfd/pcf50633-core.c         |   2 -
 include/linux/mfd/pcf50633/core.h   |   1 -
 5 files changed, 124 deletions(-)
 delete mode 100644 drivers/input/misc/pcf50633-input.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 13d135257e06..62819144bd8c 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -584,13 +584,6 @@ config INPUT_PALMAS_PWRBUTTON
 	  To compile this driver as a module, choose M here. The module will
 	  be called palmas_pwrbutton.
 
-config INPUT_PCF50633_PMU
-	tristate "PCF50633 PMU events"
-	depends on MFD_PCF50633
-	help
-	 Say Y to include support for delivering  PMU events via  input
-	 layer on NXP PCF50633.
-
 config INPUT_PCF8574
 	tristate "PCF8574 Keypad input device"
 	depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 6d91804d0a6f..d468c8140b93 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -59,7 +59,6 @@ obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)	+= mc13783-pwrbutton.o
 obj-$(CONFIG_INPUT_MMA8450)		+= mma8450.o
 obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON)	+= palmas-pwrbutton.o
 obj-$(CONFIG_INPUT_PCAP)		+= pcap_keys.o
-obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)		+= pcf8574_keypad.o
 obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
 obj-$(CONFIG_INPUT_PM8941_PWRKEY)	+= pm8941-pwrkey.o
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
deleted file mode 100644
index 6d046e236ba6..000000000000
--- a/drivers/input/misc/pcf50633-input.c
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Input Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/pcf50633/core.h>
-
-#define PCF50633_OOCSTAT_ONKEY	0x01
-#define PCF50633_REG_OOCSTAT	0x12
-#define PCF50633_REG_OOCMODE	0x10
-
-struct pcf50633_input {
-	struct pcf50633 *pcf;
-	struct input_dev *input_dev;
-};
-
-static void
-pcf50633_input_irq(int irq, void *data)
-{
-	struct pcf50633_input *input;
-	int onkey_released;
-
-	input = data;
-
-	/* We report only one event depending on the key press status */
-	onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
-						& PCF50633_OOCSTAT_ONKEY;
-
-	if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
-		input_report_key(input->input_dev, KEY_POWER, 1);
-	else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
-		input_report_key(input->input_dev, KEY_POWER, 0);
-
-	input_sync(input->input_dev);
-}
-
-static int pcf50633_input_probe(struct platform_device *pdev)
-{
-	struct pcf50633_input *input;
-	struct input_dev *input_dev;
-	int ret;
-
-
-	input = kzalloc(sizeof(*input), GFP_KERNEL);
-	if (!input)
-		return -ENOMEM;
-
-	input_dev = input_allocate_device();
-	if (!input_dev) {
-		kfree(input);
-		return -ENOMEM;
-	}
-
-	platform_set_drvdata(pdev, input);
-	input->pcf = dev_to_pcf50633(pdev->dev.parent);
-	input->input_dev = input_dev;
-
-	input_dev->name = "PCF50633 PMU events";
-	input_dev->id.bustype = BUS_I2C;
-	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
-	set_bit(KEY_POWER, input_dev->keybit);
-
-	ret = input_register_device(input_dev);
-	if (ret) {
-		input_free_device(input_dev);
-		kfree(input);
-		return ret;
-	}
-	pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
-				pcf50633_input_irq, input);
-	pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
-				pcf50633_input_irq, input);
-
-	return 0;
-}
-
-static void pcf50633_input_remove(struct platform_device *pdev)
-{
-	struct pcf50633_input *input  = platform_get_drvdata(pdev);
-
-	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
-	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
-
-	input_unregister_device(input->input_dev);
-	kfree(input);
-}
-
-static struct platform_driver pcf50633_input_driver = {
-	.driver = {
-		.name = "pcf50633-input",
-	},
-	.probe = pcf50633_input_probe,
-	.remove = pcf50633_input_remove,
-};
-module_platform_driver(pcf50633_input_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 input driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-input");
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index d991a77f6dd2..f12359f42140 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -207,7 +207,6 @@ static int pcf50633_probe(struct i2c_client *client)
 	pcf50633_irq_init(pcf, client->irq);
 
 	/* Create sub devices */
-	pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
 
 
@@ -257,7 +256,6 @@ static void pcf50633_remove(struct i2c_client *client)
 	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
 	pcf50633_irq_free(pcf);
 
-	platform_device_unregister(pcf->input_pdev);
 	platform_device_unregister(pcf->mbc_pdev);
 
 	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index f5ab3e64c230..8d63059f1153 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -147,7 +147,6 @@ struct pcf50633 {
 	int onkey1s_held;
 
 	struct platform_device *mbc_pdev;
-	struct platform_device *input_pdev;
 	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
 };
 
-- 
2.48.1


^ permalink raw reply related

* [PATCH 6/9] regulator: pcf50633-regulator: Remove
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/mfd/pcf50633-core.c            |  35 +------
 drivers/regulator/Kconfig              |   7 --
 drivers/regulator/Makefile             |   1 -
 drivers/regulator/pcf50633-regulator.c | 124 -------------------------
 include/linux/mfd/pcf50633/core.h      |   1 -
 5 files changed, 1 insertion(+), 167 deletions(-)
 delete mode 100644 drivers/regulator/pcf50633-regulator.c

diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index f12359f42140..be90ca30b75b 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -166,9 +166,8 @@ static const struct regmap_config pcf50633_regmap_config = {
 static int pcf50633_probe(struct i2c_client *client)
 {
 	struct pcf50633 *pcf;
-	struct platform_device *pdev;
 	struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev);
-	int i, j, ret;
+	int ret;
 	int version, variant;
 
 	if (!client->irq) {
@@ -210,26 +209,6 @@ static int pcf50633_probe(struct i2c_client *client)
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
 
 
-	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
-		pdev = platform_device_alloc("pcf50633-regulator", i);
-		if (!pdev) {
-			ret = -ENOMEM;
-			goto err2;
-		}
-
-		pdev->dev.parent = pcf->dev;
-		ret = platform_device_add_data(pdev, &pdata->reg_init_data[i],
-					       sizeof(pdata->reg_init_data[i]));
-		if (ret)
-			goto err;
-
-		ret = platform_device_add(pdev);
-		if (ret)
-			goto err;
-
-		pcf->regulator_pdev[i] = pdev;
-	}
-
 	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
 	if (ret)
 		dev_warn(pcf->dev, "error creating sysfs entries\n");
@@ -238,28 +217,16 @@ static int pcf50633_probe(struct i2c_client *client)
 		pdata->probe_done(pcf);
 
 	return 0;
-
-err:
-	platform_device_put(pdev);
-err2:
-	for (j = 0; j < i; j++)
-		platform_device_put(pcf->regulator_pdev[j]);
-
-	return ret;
 }
 
 static void pcf50633_remove(struct i2c_client *client)
 {
 	struct pcf50633 *pcf = i2c_get_clientdata(client);
-	int i;
 
 	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
 	pcf50633_irq_free(pcf);
 
 	platform_device_unregister(pcf->mbc_pdev);
-
-	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
-		platform_device_unregister(pcf->regulator_pdev[i]);
 }
 
 static const struct i2c_device_id pcf50633_id_table[] = {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 39297f7d8177..1236b3a1f93f 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -988,13 +988,6 @@ config REGULATOR_PCAP
 	 This driver provides support for the voltage regulators of the
 	 PCAP2 PMIC.
 
-config REGULATOR_PCF50633
-	tristate "NXP PCF50633 regulator driver"
-	depends on MFD_PCF50633
-	help
-	 Say Y here to support the voltage regulators and converters
-	 on PCF50633
-
 config REGULATOR_PF8X00
 	tristate "NXP PF8100/PF8121A/PF8200 regulator driver"
 	depends on I2C && OF
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3d5a803dce8a..8dca3567437f 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -132,7 +132,6 @@ obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
 obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
 obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
-obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o
 obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY)  += rpi-panel-attiny-regulator.o
 obj-$(CONFIG_REGULATOR_RC5T583)  += rc5t583-regulator.o
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
deleted file mode 100644
index 9f08a62c800e..000000000000
--- a/drivers/regulator/pcf50633-regulator.c
+++ /dev/null
@@ -1,124 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 PMIC Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte and Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/pmic.h>
-
-#define PCF50633_REGULATOR(_name, _id, _min_uV, _uV_step, _min_sel, _n) \
-	{							\
-		.name = _name,					\
-		.id = PCF50633_REGULATOR_##_id,			\
-		.ops = &pcf50633_regulator_ops,			\
-		.n_voltages = _n,				\
-		.min_uV = _min_uV,				\
-		.uV_step = _uV_step,				\
-		.linear_min_sel = _min_sel,			\
-		.type = REGULATOR_VOLTAGE,			\
-		.owner = THIS_MODULE,				\
-		.vsel_reg = PCF50633_REG_##_id##OUT,		\
-		.vsel_mask = 0xff,				\
-		.enable_reg = PCF50633_REG_##_id##OUT + 1,	\
-		.enable_mask = PCF50633_REGULATOR_ON,		\
-	}
-
-static const struct regulator_ops pcf50633_regulator_ops = {
-	.set_voltage_sel = regulator_set_voltage_sel_regmap,
-	.get_voltage_sel = regulator_get_voltage_sel_regmap,
-	.list_voltage = regulator_list_voltage_linear,
-	.map_voltage = regulator_map_voltage_linear,
-	.enable = regulator_enable_regmap,
-	.disable = regulator_disable_regmap,
-	.is_enabled = regulator_is_enabled_regmap,
-};
-
-static const struct regulator_desc regulators[] = {
-	[PCF50633_REGULATOR_AUTO] =
-		PCF50633_REGULATOR("auto", AUTO, 1800000, 25000, 0x2f, 128),
-	[PCF50633_REGULATOR_DOWN1] =
-		PCF50633_REGULATOR("down1", DOWN1, 625000, 25000, 0, 96),
-	[PCF50633_REGULATOR_DOWN2] =
-		PCF50633_REGULATOR("down2", DOWN2, 625000, 25000, 0, 96),
-	[PCF50633_REGULATOR_LDO1] =
-		PCF50633_REGULATOR("ldo1", LDO1, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_LDO2] =
-		PCF50633_REGULATOR("ldo2", LDO2, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_LDO3] =
-		PCF50633_REGULATOR("ldo3", LDO3, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_LDO4] =
-		PCF50633_REGULATOR("ldo4", LDO4, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_LDO5] =
-		PCF50633_REGULATOR("ldo5", LDO5, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_LDO6] =
-		PCF50633_REGULATOR("ldo6", LDO6, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_HCLDO] =
-		PCF50633_REGULATOR("hcldo", HCLDO, 900000, 100000, 0, 28),
-	[PCF50633_REGULATOR_MEMLDO] =
-		PCF50633_REGULATOR("memldo", MEMLDO, 900000, 100000, 0, 28),
-};
-
-static int pcf50633_regulator_probe(struct platform_device *pdev)
-{
-	struct regulator_dev *rdev;
-	struct pcf50633 *pcf;
-	struct regulator_config config = { };
-
-	/* Already set by core driver */
-	pcf = dev_to_pcf50633(pdev->dev.parent);
-
-	config.dev = &pdev->dev;
-	config.init_data = dev_get_platdata(&pdev->dev);
-	config.driver_data = pcf;
-	config.regmap = pcf->regmap;
-
-	rdev = devm_regulator_register(&pdev->dev, &regulators[pdev->id],
-				       &config);
-	if (IS_ERR(rdev))
-		return PTR_ERR(rdev);
-
-	platform_set_drvdata(pdev, rdev);
-
-	if (pcf->pdata->regulator_registered)
-		pcf->pdata->regulator_registered(pcf, pdev->id);
-
-	return 0;
-}
-
-static struct platform_driver pcf50633_regulator_driver = {
-	.driver = {
-		.name = "pcf50633-regulator",
-		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
-	},
-	.probe = pcf50633_regulator_probe,
-};
-
-static int __init pcf50633_regulator_init(void)
-{
-	return platform_driver_register(&pcf50633_regulator_driver);
-}
-subsys_initcall(pcf50633_regulator_init);
-
-static void __exit pcf50633_regulator_exit(void)
-{
-	platform_driver_unregister(&pcf50633_regulator_driver);
-}
-module_exit(pcf50633_regulator_exit);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 regulator driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-regulator");
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index 8d63059f1153..0bd7f7e46e46 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -147,7 +147,6 @@ struct pcf50633 {
 	int onkey1s_held;
 
 	struct platform_device *mbc_pdev;
-	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
 };
 
 enum pcf50633_reg_int1 {
-- 
2.48.1


^ permalink raw reply related

* [PATCH 7/9] power: supply: pcf50633: Remove charger
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/power/supply/Kconfig            |   6 -
 drivers/power/supply/Makefile           |   1 -
 drivers/power/supply/pcf50633-charger.c | 466 ------------------------
 3 files changed, 473 deletions(-)
 delete mode 100644 drivers/power/supply/pcf50633-charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 7b18358f194a..aa569badaf73 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -449,12 +449,6 @@ config CHARGER_88PM860X
 	help
 	  Say Y here to enable charger for Marvell 88PM860x chip.
 
-config CHARGER_PCF50633
-	tristate "NXP PCF50633 MBC"
-	depends on MFD_PCF50633
-	help
-	  Say Y to include support for NXP PCF50633 Main Battery Charger.
-
 config BATTERY_RX51
 	tristate "Nokia RX-51 (N900) battery driver"
 	depends on TWL4030_MADC
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index b55cc48a4c86..eedb00e377cb 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -62,7 +62,6 @@ obj-$(CONFIG_CHARGER_RT9467)	+= rt9467-charger.o
 obj-$(CONFIG_CHARGER_RT9471)	+= rt9471.o
 obj-$(CONFIG_BATTERY_TWL4030_MADC)	+= twl4030_madc_battery.o
 obj-$(CONFIG_CHARGER_88PM860X)	+= 88pm860x_charger.o
-obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o
 obj-$(CONFIG_BATTERY_RX51)	+= rx51_battery.o
 obj-$(CONFIG_AB8500_BM)		+= ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
 obj-$(CONFIG_CHARGER_CPCAP)	+= cpcap-charger.o
diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c
deleted file mode 100644
index 0136bc87b105..000000000000
--- a/drivers/power/supply/pcf50633-charger.c
+++ /dev/null
@@ -1,466 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Main Battery Charger Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/mbc.h>
-
-struct pcf50633_mbc {
-	struct pcf50633 *pcf;
-
-	int adapter_online;
-	int usb_online;
-
-	struct power_supply *usb;
-	struct power_supply *adapter;
-	struct power_supply *ac;
-};
-
-int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
-{
-	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
-	int ret = 0;
-	u8 bits;
-	u8 mbcs2, chgmod;
-	unsigned int mbcc5;
-
-	if (ma >= 1000) {
-		bits = PCF50633_MBCC7_USB_1000mA;
-		ma = 1000;
-	} else if (ma >= 500) {
-		bits = PCF50633_MBCC7_USB_500mA;
-		ma = 500;
-	} else if (ma >= 100) {
-		bits = PCF50633_MBCC7_USB_100mA;
-		ma = 100;
-	} else {
-		bits = PCF50633_MBCC7_USB_SUSPEND;
-		ma = 0;
-	}
-
-	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
-					PCF50633_MBCC7_USB_MASK, bits);
-	if (ret)
-		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
-	else
-		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
-
-	/*
-	 * We limit the charging current to be the USB current limit.
-	 * The reason is that on pcf50633, when it enters PMU Standby mode,
-	 * which it does when the device goes "off", the USB current limit
-	 * reverts to the variant default.  In at least one common case, that
-	 * default is 500mA.  By setting the charging current to be the same
-	 * as the USB limit we set here before PMU standby, we enforce it only
-	 * using the correct amount of current even when the USB current limit
-	 * gets reset to the wrong thing
-	 */
-
-	if (mbc->pcf->pdata->charger_reference_current_ma) {
-		mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
-		if (mbcc5 > 255)
-			mbcc5 = 255;
-		pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
-	}
-
-	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
-	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
-	/* If chgmod == BATFULL, setting chgena has no effect.
-	 * Datasheet says we need to set resume instead but when autoresume is
-	 * used resume doesn't work. Clear and set chgena instead.
-	 */
-	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
-		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
-				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
-	else {
-		pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
-				PCF50633_MBCC1_CHGENA);
-		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
-				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
-	}
-
-	power_supply_changed(mbc->usb);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
-
-int pcf50633_mbc_get_status(struct pcf50633 *pcf)
-{
-	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
-	int status = 0;
-	u8 chgmod;
-
-	if (!mbc)
-		return 0;
-
-	chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
-		& PCF50633_MBCS2_MBC_MASK;
-
-	if (mbc->usb_online)
-		status |= PCF50633_MBC_USB_ONLINE;
-	if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
-	    chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
-	    chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
-	    chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
-		status |= PCF50633_MBC_USB_ACTIVE;
-	if (mbc->adapter_online)
-		status |= PCF50633_MBC_ADAPTER_ONLINE;
-	if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
-	    chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
-	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
-	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
-		status |= PCF50633_MBC_ADAPTER_ACTIVE;
-
-	return status;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
-
-int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
-{
-	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
-
-	if (!mbc)
-		return 0;
-
-	return mbc->usb_online;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
-
-static ssize_t
-show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-
-	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
-	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
-	return sysfs_emit(buf, "%d\n", chgmod);
-}
-static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
-
-static ssize_t
-show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-						PCF50633_MBCC7_USB_MASK;
-	unsigned int ma;
-
-	if (usblim == PCF50633_MBCC7_USB_1000mA)
-		ma = 1000;
-	else if (usblim == PCF50633_MBCC7_USB_500mA)
-		ma = 500;
-	else if (usblim == PCF50633_MBCC7_USB_100mA)
-		ma = 100;
-	else
-		ma = 0;
-
-	return sysfs_emit(buf, "%u\n", ma);
-}
-
-static ssize_t set_usblim(struct device *dev,
-		struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-	unsigned long ma;
-	int ret;
-
-	ret = kstrtoul(buf, 10, &ma);
-	if (ret)
-		return ret;
-
-	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
-
-	return count;
-}
-
-static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
-
-static ssize_t
-show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-	u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
-	unsigned int ma;
-
-	if (!mbc->pcf->pdata->charger_reference_current_ma)
-		return -ENODEV;
-
-	ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
-
-	return sysfs_emit(buf, "%u\n", ma);
-}
-
-static ssize_t set_chglim(struct device *dev,
-		struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-	unsigned long ma;
-	unsigned int mbcc5;
-	int ret;
-
-	if (!mbc->pcf->pdata->charger_reference_current_ma)
-		return -ENODEV;
-
-	ret = kstrtoul(buf, 10, &ma);
-	if (ret)
-		return ret;
-
-	mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
-	if (mbcc5 > 255)
-		mbcc5 = 255;
-	pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
-
-	return count;
-}
-
-/*
- * This attribute allows to change MBC charging limit on the fly
- * independently of usb current limit. It also gets set automatically every
- * time usb current limit is changed.
- */
-static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
-
-static struct attribute *pcf50633_mbc_sysfs_attrs[] = {
-	&dev_attr_chgmode.attr,
-	&dev_attr_usb_curlim.attr,
-	&dev_attr_chg_curlim.attr,
-	NULL,
-};
-
-ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
-
-static void
-pcf50633_mbc_irq_handler(int irq, void *data)
-{
-	struct pcf50633_mbc *mbc = data;
-
-	/* USB */
-	if (irq == PCF50633_IRQ_USBINS) {
-		mbc->usb_online = 1;
-	} else if (irq == PCF50633_IRQ_USBREM) {
-		mbc->usb_online = 0;
-		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
-	}
-
-	/* Adapter */
-	if (irq == PCF50633_IRQ_ADPINS)
-		mbc->adapter_online = 1;
-	else if (irq == PCF50633_IRQ_ADPREM)
-		mbc->adapter_online = 0;
-
-	power_supply_changed(mbc->ac);
-	power_supply_changed(mbc->usb);
-	power_supply_changed(mbc->adapter);
-
-	if (mbc->pcf->pdata->mbc_event_callback)
-		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
-}
-
-static int adapter_get_property(struct power_supply *psy,
-			enum power_supply_property psp,
-			union power_supply_propval *val)
-{
-	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-	int ret = 0;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval =  mbc->adapter_online;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static int usb_get_property(struct power_supply *psy,
-			enum power_supply_property psp,
-			union power_supply_propval *val)
-{
-	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-	int ret = 0;
-	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-						PCF50633_MBCC7_USB_MASK;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = mbc->usb_online &&
-				(usblim <= PCF50633_MBCC7_USB_500mA);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static int ac_get_property(struct power_supply *psy,
-			enum power_supply_property psp,
-			union power_supply_propval *val)
-{
-	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-	int ret = 0;
-	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-						PCF50633_MBCC7_USB_MASK;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = mbc->usb_online &&
-				(usblim == PCF50633_MBCC7_USB_1000mA);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static enum power_supply_property power_props[] = {
-	POWER_SUPPLY_PROP_ONLINE,
-};
-
-static const u8 mbc_irq_handlers[] = {
-	PCF50633_IRQ_ADPINS,
-	PCF50633_IRQ_ADPREM,
-	PCF50633_IRQ_USBINS,
-	PCF50633_IRQ_USBREM,
-	PCF50633_IRQ_BATFULL,
-	PCF50633_IRQ_CHGHALT,
-	PCF50633_IRQ_THLIMON,
-	PCF50633_IRQ_THLIMOFF,
-	PCF50633_IRQ_USBLIMON,
-	PCF50633_IRQ_USBLIMOFF,
-	PCF50633_IRQ_LOWSYS,
-	PCF50633_IRQ_LOWBAT,
-};
-
-static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
-	.name		= "adapter",
-	.type		= POWER_SUPPLY_TYPE_MAINS,
-	.properties	= power_props,
-	.num_properties	= ARRAY_SIZE(power_props),
-	.get_property	= &adapter_get_property,
-};
-
-static const struct power_supply_desc pcf50633_mbc_usb_desc = {
-	.name		= "usb",
-	.type		= POWER_SUPPLY_TYPE_USB,
-	.properties	= power_props,
-	.num_properties	= ARRAY_SIZE(power_props),
-	.get_property	= usb_get_property,
-};
-
-static const struct power_supply_desc pcf50633_mbc_ac_desc = {
-	.name		= "ac",
-	.type		= POWER_SUPPLY_TYPE_MAINS,
-	.properties	= power_props,
-	.num_properties	= ARRAY_SIZE(power_props),
-	.get_property	= ac_get_property,
-};
-
-static int pcf50633_mbc_probe(struct platform_device *pdev)
-{
-	struct power_supply_config psy_cfg = {};
-	struct power_supply_config usb_psy_cfg;
-	struct pcf50633_mbc *mbc;
-	int i;
-	u8 mbcs1;
-
-	mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
-	if (!mbc)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, mbc);
-	mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
-
-	/* Set up IRQ handlers */
-	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
-		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
-					pcf50633_mbc_irq_handler, mbc);
-
-	psy_cfg.supplied_to		= mbc->pcf->pdata->batteries;
-	psy_cfg.num_supplicants		= mbc->pcf->pdata->num_batteries;
-	psy_cfg.drv_data		= mbc;
-
-	/* Create power supplies */
-	mbc->adapter = devm_power_supply_register(&pdev->dev,
-						  &pcf50633_mbc_adapter_desc,
-						  &psy_cfg);
-	if (IS_ERR(mbc->adapter)) {
-		dev_err(mbc->pcf->dev, "failed to register adapter\n");
-		return PTR_ERR(mbc->adapter);
-	}
-
-	usb_psy_cfg = psy_cfg;
-	usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups;
-
-	mbc->usb = devm_power_supply_register(&pdev->dev,
-					      &pcf50633_mbc_usb_desc,
-					      &usb_psy_cfg);
-	if (IS_ERR(mbc->usb)) {
-		dev_err(mbc->pcf->dev, "failed to register usb\n");
-		return PTR_ERR(mbc->usb);
-	}
-
-	mbc->ac = devm_power_supply_register(&pdev->dev,
-					     &pcf50633_mbc_ac_desc,
-					     &psy_cfg);
-	if (IS_ERR(mbc->ac)) {
-		dev_err(mbc->pcf->dev, "failed to register ac\n");
-		return PTR_ERR(mbc->ac);
-	}
-
-	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
-	if (mbcs1 & PCF50633_MBCS1_USBPRES)
-		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
-	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
-		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
-
-	return 0;
-}
-
-static void pcf50633_mbc_remove(struct platform_device *pdev)
-{
-	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
-	int i;
-
-	/* Remove IRQ handlers */
-	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
-		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
-}
-
-static struct platform_driver pcf50633_mbc_driver = {
-	.driver = {
-		.name = "pcf50633-mbc",
-	},
-	.probe = pcf50633_mbc_probe,
-	.remove = pcf50633_mbc_remove,
-};
-
-module_platform_driver(pcf50633_mbc_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 mbc driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-mbc");
-- 
2.48.1


^ permalink raw reply related

* [PATCH 8/9] mfd: pcf50633: Remove irq code
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

As part of the pcf50633 removal, take out it's irq code
(which includes one bit still called from the core, but it'll
go soon).

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 drivers/mfd/Makefile        |   2 +-
 drivers/mfd/pcf50633-core.c |   5 +-
 drivers/mfd/pcf50633-irq.c  | 312 ------------------------------------
 3 files changed, 2 insertions(+), 317 deletions(-)
 delete mode 100644 drivers/mfd/pcf50633-irq.c

diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d769e40251c8..f1c7a15c8f7b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -183,7 +183,7 @@ obj-$(CONFIG_MFD_MT6370)	+= mt6370.o
 mt6397-objs			:= mt6397-core.o mt6397-irq.o mt6358-irq.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397.o
 
-pcf50633-objs			:= pcf50633-core.o pcf50633-irq.o
+pcf50633-objs			:= pcf50633-core.o
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
 obj-$(CONFIG_RZ_MTU3)		+= rz-mtu3.o
 obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index be90ca30b75b..1637816a23d1 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -203,8 +203,6 @@ static int pcf50633_probe(struct i2c_client *client)
 	dev_info(pcf->dev, "Probed device version %d variant %d\n",
 							version, variant);
 
-	pcf50633_irq_init(pcf, client->irq);
-
 	/* Create sub devices */
 	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
 
@@ -224,7 +222,6 @@ static void pcf50633_remove(struct i2c_client *client)
 	struct pcf50633 *pcf = i2c_get_clientdata(client);
 
 	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
-	pcf50633_irq_free(pcf);
 
 	platform_device_unregister(pcf->mbc_pdev);
 }
@@ -238,7 +235,7 @@ MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
 static struct i2c_driver pcf50633_driver = {
 	.driver = {
 		.name	= "pcf50633",
-		.pm	= pm_sleep_ptr(&pcf50633_pm),
+		/* going.... .pm	= pm_sleep_ptr(&pcf50633_pm), */
 	},
 	.id_table = pcf50633_id_table,
 	.probe = pcf50633_probe,
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
deleted file mode 100644
index e85af7f1cb0b..000000000000
--- a/drivers/mfd/pcf50633-irq.c
+++ /dev/null
@@ -1,312 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Power Management Unit (PMU) driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- * 	   Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- */
-
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/mbc.h>
-
-int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
-			void (*handler) (int, void *), void *data)
-{
-	if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
-		return -EINVAL;
-
-	if (WARN_ON(pcf->irq_handler[irq].handler))
-		return -EBUSY;
-
-	mutex_lock(&pcf->lock);
-	pcf->irq_handler[irq].handler = handler;
-	pcf->irq_handler[irq].data = data;
-	mutex_unlock(&pcf->lock);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_register_irq);
-
-int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
-{
-	if (irq < 0 || irq >= PCF50633_NUM_IRQ)
-		return -EINVAL;
-
-	mutex_lock(&pcf->lock);
-	pcf->irq_handler[irq].handler = NULL;
-	mutex_unlock(&pcf->lock);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_free_irq);
-
-static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
-{
-	u8 reg, bit;
-	int idx;
-
-	idx = irq >> 3;
-	reg = PCF50633_REG_INT1M + idx;
-	bit = 1 << (irq & 0x07);
-
-	pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
-
-	mutex_lock(&pcf->lock);
-
-	if (mask)
-		pcf->mask_regs[idx] |= bit;
-	else
-		pcf->mask_regs[idx] &= ~bit;
-
-	mutex_unlock(&pcf->lock);
-
-	return 0;
-}
-
-int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
-{
-	dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
-
-	return __pcf50633_irq_mask_set(pcf, irq, 1);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
-
-int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
-{
-	dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
-
-	return __pcf50633_irq_mask_set(pcf, irq, 0);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
-
-int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
-{
-	u8 reg, bits;
-
-	reg =  irq >> 3;
-	bits = 1 << (irq & 0x07);
-
-	return pcf->mask_regs[reg] & bits;
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
-
-static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
-{
-	if (pcf->irq_handler[irq].handler)
-		pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
-}
-
-/* Maximum amount of time ONKEY is held before emergency action is taken */
-#define PCF50633_ONKEY1S_TIMEOUT 8
-
-static irqreturn_t pcf50633_irq(int irq, void *data)
-{
-	struct pcf50633 *pcf = data;
-	int ret, i, j;
-	u8 pcf_int[5], chgstat;
-
-	/* Read the 5 INT regs in one transaction */
-	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
-						ARRAY_SIZE(pcf_int), pcf_int);
-	if (ret != ARRAY_SIZE(pcf_int)) {
-		dev_err(pcf->dev, "Error reading INT registers\n");
-
-		/*
-		 * If this doesn't ACK the interrupt to the chip, we'll be
-		 * called once again as we're level triggered.
-		 */
-		goto out;
-	}
-
-	/* defeat 8s death from lowsys on A5 */
-	pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
-
-	/* We immediately read the usb and adapter status. We thus make sure
-	 * only of USBINS/USBREM IRQ handlers are called */
-	if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
-		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
-		if (chgstat & (0x3 << 4))
-			pcf_int[0] &= ~PCF50633_INT1_USBREM;
-		else
-			pcf_int[0] &= ~PCF50633_INT1_USBINS;
-	}
-
-	/* Make sure only one of ADPINS or ADPREM is set */
-	if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
-		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
-		if (chgstat & (0x3 << 4))
-			pcf_int[0] &= ~PCF50633_INT1_ADPREM;
-		else
-			pcf_int[0] &= ~PCF50633_INT1_ADPINS;
-	}
-
-	dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
-			"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
-			pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
-
-	/* Some revisions of the chip don't have a 8s standby mode on
-	 * ONKEY1S press. We try to manually do it in such cases. */
-	if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
-		dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
-							pcf->onkey1s_held);
-		if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
-			if (pcf->pdata->force_shutdown)
-				pcf->pdata->force_shutdown(pcf);
-	}
-
-	if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
-		dev_info(pcf->dev, "ONKEY1S held\n");
-		pcf->onkey1s_held = 1 ;
-
-		/* Unmask IRQ_SECOND */
-		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
-						PCF50633_INT1_SECOND);
-
-		/* Unmask IRQ_ONKEYR */
-		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
-						PCF50633_INT2_ONKEYR);
-	}
-
-	if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
-		pcf->onkey1s_held = 0;
-
-		/* Mask SECOND and ONKEYR interrupts */
-		if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
-			pcf50633_reg_set_bit_mask(pcf,
-					PCF50633_REG_INT1M,
-					PCF50633_INT1_SECOND,
-					PCF50633_INT1_SECOND);
-
-		if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
-			pcf50633_reg_set_bit_mask(pcf,
-					PCF50633_REG_INT2M,
-					PCF50633_INT2_ONKEYR,
-					PCF50633_INT2_ONKEYR);
-	}
-
-	/* Have we just resumed ? */
-	if (pcf->is_suspended) {
-		pcf->is_suspended = 0;
-
-		/* Set the resume reason filtering out non resumers */
-		for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
-			pcf->resume_reason[i] = pcf_int[i] &
-						pcf->pdata->resumers[i];
-
-		/* Make sure we don't pass on any ONKEY events to
-		 * userspace now */
-		pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
-	}
-
-	for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
-		/* Unset masked interrupts */
-		pcf_int[i] &= ~pcf->mask_regs[i];
-
-		for (j = 0; j < 8 ; j++)
-			if (pcf_int[i] & (1 << j))
-				pcf50633_irq_call_handler(pcf, (i * 8) + j);
-	}
-
-out:
-	return IRQ_HANDLED;
-}
-
-static int pcf50633_suspend(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct pcf50633 *pcf = i2c_get_clientdata(client);
-	int ret;
-	int i;
-	u8 res[5];
-
-
-	/* Make sure our interrupt handlers are not called
-	 * henceforth */
-	disable_irq(pcf->irq);
-
-	/* Save the masks */
-	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
-				ARRAY_SIZE(pcf->suspend_irq_masks),
-					pcf->suspend_irq_masks);
-	if (ret < 0) {
-		dev_err(pcf->dev, "error saving irq masks\n");
-		goto out;
-	}
-
-	/* Write wakeup irq masks */
-	for (i = 0; i < ARRAY_SIZE(res); i++)
-		res[i] = ~pcf->pdata->resumers[i];
-
-	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
-					ARRAY_SIZE(res), &res[0]);
-	if (ret < 0) {
-		dev_err(pcf->dev, "error writing wakeup irq masks\n");
-		goto out;
-	}
-
-	pcf->is_suspended = 1;
-
-out:
-	return ret;
-}
-
-static int pcf50633_resume(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct pcf50633 *pcf = i2c_get_clientdata(client);
-	int ret;
-
-	/* Write the saved mask registers */
-	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
-				ARRAY_SIZE(pcf->suspend_irq_masks),
-					pcf->suspend_irq_masks);
-	if (ret < 0)
-		dev_err(pcf->dev, "Error restoring saved suspend masks\n");
-
-	enable_irq(pcf->irq);
-
-	return ret;
-}
-
-EXPORT_GPL_SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
-
-int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
-{
-	int ret;
-
-	pcf->irq = irq;
-
-	/* Enable all interrupts except RTC SECOND */
-	pcf->mask_regs[0] = 0x80;
-	pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
-	pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
-	pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
-	pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
-	pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
-
-	ret = request_threaded_irq(irq, NULL, pcf50633_irq,
-					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-					"pcf50633", pcf);
-
-	if (ret)
-		dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
-
-	if (enable_irq_wake(irq) < 0)
-		dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
-			"in this hardware revision", irq);
-
-	return ret;
-}
-
-void pcf50633_irq_free(struct pcf50633 *pcf)
-{
-	free_irq(pcf->irq, pcf);
-}
-- 
2.48.1


^ permalink raw reply related

* [PATCH 9/9] mfd: pcf50633: Remove remains
From: linux @ 2025-03-09 19:36 UTC (permalink / raw)
  To: arnd, lee, dmitry.torokhov, sre, lgirdwood, broonie,
	alexandre.belloni, danielt, jingoohan1, deller, linus.walleij,
	brgl, tsbogend
  Cc: linux-mips, linux-input, linux-pm, linux-rtc, dri-devel,
	linux-fbdev, linux-gpio, linux-kernel, Dr. David Alan Gilbert
In-Reply-To: <20250309193612.251929-1-linux@treblig.org>

From: "Dr. David Alan Gilbert" <linux@treblig.org>

Remove the remaining parts of the 50633, the core, headers and glue.

The pcf50633 was used as part of the OpenMoko devices but
the support for its main chip was recently removed in:
commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support")

See https://lore.kernel.org/all/Z8z236h4B5A6Ki3D@gallifrey/

Remove it.

Signed-off-by: Dr. David Alan Gilbert <linux@treblig.org>
---
 arch/mips/configs/ip27_defconfig  |   1 -
 drivers/mfd/Kconfig               |  10 --
 drivers/mfd/Makefile              |   2 -
 drivers/mfd/pcf50633-core.c       | 260 ------------------------------
 include/linux/mfd/pcf50633/core.h | 224 -------------------------
 include/linux/mfd/pcf50633/mbc.h  | 130 ---------------
 include/linux/mfd/pcf50633/pmic.h |  68 --------
 7 files changed, 695 deletions(-)
 delete mode 100644 drivers/mfd/pcf50633-core.c
 delete mode 100644 include/linux/mfd/pcf50633/core.h
 delete mode 100644 include/linux/mfd/pcf50633/mbc.h
 delete mode 100644 include/linux/mfd/pcf50633/pmic.h

diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 0a9ec09aff65..c13c2f3cff9a 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -255,7 +255,6 @@ CONFIG_I2C_TAOS_EVM=m
 CONFIG_I2C_STUB=m
 # CONFIG_HWMON is not set
 CONFIG_THERMAL=y
-CONFIG_MFD_PCF50633=m
 # CONFIG_VGA_ARB is not set
 CONFIG_LEDS_LP3944=m
 CONFIG_LEDS_PCA955X=m
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 766453ef6c2d..1eeb62dac8d3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1119,16 +1119,6 @@ config MFD_RETU
 	  Retu and Tahvo are a multi-function devices found on Nokia
 	  Internet Tablets (770, N800 and N810).
 
-config MFD_PCF50633
-	tristate "NXP PCF50633"
-	depends on I2C
-	select REGMAP_I2C
-	help
-	  Say yes here if you have NXP PCF50633 chip on your board.
-	  This core driver provides register access and IRQ handling
-	  facilities, and registers devices for the various functions
-	  so that function-specific drivers can bind to them.
-
 config MFD_PM8XXX
 	tristate "Qualcomm PM8xxx PMIC chips driver"
 	depends on ARM || HEXAGON || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f1c7a15c8f7b..941e4ba58b24 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -183,8 +183,6 @@ obj-$(CONFIG_MFD_MT6370)	+= mt6370.o
 mt6397-objs			:= mt6397-core.o mt6397-irq.o mt6358-irq.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397.o
 
-pcf50633-objs			:= pcf50633-core.o
-obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
 obj-$(CONFIG_RZ_MTU3)		+= rz-mtu3.o
 obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
 obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
deleted file mode 100644
index 1637816a23d1..000000000000
--- a/drivers/mfd/pcf50633-core.c
+++ /dev/null
@@ -1,260 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Power Management Unit (PMU) driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- * 	   Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/regmap.h>
-#include <linux/err.h>
-
-#include <linux/mfd/pcf50633/core.h>
-
-/* Read a block of up to 32 regs  */
-int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
-					int nr_regs, u8 *data)
-{
-	int ret;
-
-	ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
-	if (ret != 0)
-		return ret;
-
-	return nr_regs;
-}
-EXPORT_SYMBOL_GPL(pcf50633_read_block);
-
-/* Write a block of up to 32 regs  */
-int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
-					int nr_regs, u8 *data)
-{
-	return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
-}
-EXPORT_SYMBOL_GPL(pcf50633_write_block);
-
-u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
-{
-	unsigned int val;
-	int ret;
-
-	ret = regmap_read(pcf->regmap, reg, &val);
-	if (ret < 0)
-		return -1;
-
-	return val;
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_read);
-
-int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
-{
-	return regmap_write(pcf->regmap, reg, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_write);
-
-int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
-{
-	return regmap_update_bits(pcf->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
-
-int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
-{
-	return regmap_update_bits(pcf->regmap, reg, val, 0);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
-
-/* sysfs attributes */
-static ssize_t dump_regs_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	struct pcf50633 *pcf = dev_get_drvdata(dev);
-	u8 dump[16];
-	int n, n1, idx = 0;
-	char *buf1 = buf;
-	static u8 address_no_read[] = { /* must be ascending */
-		PCF50633_REG_INT1,
-		PCF50633_REG_INT2,
-		PCF50633_REG_INT3,
-		PCF50633_REG_INT4,
-		PCF50633_REG_INT5,
-		0 /* terminator */
-	};
-
-	for (n = 0; n < 256; n += sizeof(dump)) {
-		for (n1 = 0; n1 < sizeof(dump); n1++)
-			if (n == address_no_read[idx]) {
-				idx++;
-				dump[n1] = 0x00;
-			} else
-				dump[n1] = pcf50633_reg_read(pcf, n + n1);
-
-		buf1 += sprintf(buf1, "%*ph\n", (int)sizeof(dump), dump);
-	}
-
-	return buf1 - buf;
-}
-static DEVICE_ATTR_ADMIN_RO(dump_regs);
-
-static ssize_t resume_reason_show(struct device *dev,
-				  struct device_attribute *attr, char *buf)
-{
-	struct pcf50633 *pcf = dev_get_drvdata(dev);
-	int n;
-
-	n = sprintf(buf, "%02x%02x%02x%02x%02x\n",
-				pcf->resume_reason[0],
-				pcf->resume_reason[1],
-				pcf->resume_reason[2],
-				pcf->resume_reason[3],
-				pcf->resume_reason[4]);
-
-	return n;
-}
-static DEVICE_ATTR_ADMIN_RO(resume_reason);
-
-static struct attribute *pcf_sysfs_entries[] = {
-	&dev_attr_dump_regs.attr,
-	&dev_attr_resume_reason.attr,
-	NULL,
-};
-
-static struct attribute_group pcf_attr_group = {
-	.name	= NULL,			/* put in device directory */
-	.attrs	= pcf_sysfs_entries,
-};
-
-static void
-pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
-						struct platform_device **pdev)
-{
-	int ret;
-
-	*pdev = platform_device_alloc(name, -1);
-	if (!*pdev) {
-		dev_err(pcf->dev, "Failed to allocate %s\n", name);
-		return;
-	}
-
-	(*pdev)->dev.parent = pcf->dev;
-
-	ret = platform_device_add(*pdev);
-	if (ret) {
-		dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret);
-		platform_device_put(*pdev);
-		*pdev = NULL;
-	}
-}
-
-static const struct regmap_config pcf50633_regmap_config = {
-	.reg_bits = 8,
-	.val_bits = 8,
-};
-
-static int pcf50633_probe(struct i2c_client *client)
-{
-	struct pcf50633 *pcf;
-	struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev);
-	int ret;
-	int version, variant;
-
-	if (!client->irq) {
-		dev_err(&client->dev, "Missing IRQ\n");
-		return -ENOENT;
-	}
-
-	pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
-	if (!pcf)
-		return -ENOMEM;
-
-	i2c_set_clientdata(client, pcf);
-	pcf->dev = &client->dev;
-	pcf->pdata = pdata;
-
-	mutex_init(&pcf->lock);
-
-	pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
-	if (IS_ERR(pcf->regmap)) {
-		ret = PTR_ERR(pcf->regmap);
-		dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret);
-		return ret;
-	}
-
-	version = pcf50633_reg_read(pcf, 0);
-	variant = pcf50633_reg_read(pcf, 1);
-	if (version < 0 || variant < 0) {
-		dev_err(pcf->dev, "Unable to probe pcf50633\n");
-		ret = -ENODEV;
-		return ret;
-	}
-
-	dev_info(pcf->dev, "Probed device version %d variant %d\n",
-							version, variant);
-
-	/* Create sub devices */
-	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
-
-
-	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
-	if (ret)
-		dev_warn(pcf->dev, "error creating sysfs entries\n");
-
-	if (pdata->probe_done)
-		pdata->probe_done(pcf);
-
-	return 0;
-}
-
-static void pcf50633_remove(struct i2c_client *client)
-{
-	struct pcf50633 *pcf = i2c_get_clientdata(client);
-
-	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
-
-	platform_device_unregister(pcf->mbc_pdev);
-}
-
-static const struct i2c_device_id pcf50633_id_table[] = {
-	{"pcf50633", 0x73},
-	{/* end of list */}
-};
-MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
-
-static struct i2c_driver pcf50633_driver = {
-	.driver = {
-		.name	= "pcf50633",
-		/* going.... .pm	= pm_sleep_ptr(&pcf50633_pm), */
-	},
-	.id_table = pcf50633_id_table,
-	.probe = pcf50633_probe,
-	.remove = pcf50633_remove,
-};
-
-static int __init pcf50633_init(void)
-{
-	return i2c_add_driver(&pcf50633_driver);
-}
-
-static void __exit pcf50633_exit(void)
-{
-	i2c_del_driver(&pcf50633_driver);
-}
-
-MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
-MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
-MODULE_LICENSE("GPL");
-
-subsys_initcall(pcf50633_init);
-module_exit(pcf50633_exit);
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
deleted file mode 100644
index 0bd7f7e46e46..000000000000
--- a/include/linux/mfd/pcf50633/core.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * core.h  -- Core driver for NXP PCF50633
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * All rights reserved.
- */
-
-#ifndef __LINUX_MFD_PCF50633_CORE_H
-#define __LINUX_MFD_PCF50633_CORE_H
-
-#include <linux/i2c.h>
-#include <linux/workqueue.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/machine.h>
-#include <linux/pm.h>
-#include <linux/power_supply.h>
-
-struct pcf50633;
-struct regmap;
-
-#define PCF50633_NUM_REGULATORS	11
-
-struct pcf50633_platform_data {
-	struct regulator_init_data reg_init_data[PCF50633_NUM_REGULATORS];
-
-	char **batteries;
-	int num_batteries;
-
-	/*
-	 * Should be set accordingly to the reference resistor used, see
-	 * I_{ch(ref)} charger reference current in the pcf50633 User
-	 * Manual.
-	 */
-	int charger_reference_current_ma;
-
-	/* Callbacks */
-	void (*probe_done)(struct pcf50633 *);
-	void (*mbc_event_callback)(struct pcf50633 *, int);
-	void (*regulator_registered)(struct pcf50633 *, int);
-	void (*force_shutdown)(struct pcf50633 *);
-
-	u8 resumers[5];
-};
-
-struct pcf50633_irq {
-	void (*handler) (int, void *);
-	void *data;
-};
-
-int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
-			void (*handler) (int, void *), void *data);
-int pcf50633_free_irq(struct pcf50633 *pcf, int irq);
-
-int pcf50633_irq_mask(struct pcf50633 *pcf, int irq);
-int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq);
-int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq);
-
-int pcf50633_read_block(struct pcf50633 *, u8 reg,
-					int nr_regs, u8 *data);
-int pcf50633_write_block(struct pcf50633 *pcf, u8 reg,
-					int nr_regs, u8 *data);
-u8 pcf50633_reg_read(struct pcf50633 *, u8 reg);
-int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val);
-
-int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val);
-int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 bits);
-
-/* Interrupt registers */
-
-#define PCF50633_REG_INT1	0x02
-#define PCF50633_REG_INT2	0x03
-#define PCF50633_REG_INT3	0x04
-#define PCF50633_REG_INT4	0x05
-#define PCF50633_REG_INT5	0x06
-
-#define PCF50633_REG_INT1M	0x07
-#define PCF50633_REG_INT2M	0x08
-#define PCF50633_REG_INT3M	0x09
-#define PCF50633_REG_INT4M	0x0a
-#define PCF50633_REG_INT5M	0x0b
-
-enum {
-	/* Chip IRQs */
-	PCF50633_IRQ_ADPINS,
-	PCF50633_IRQ_ADPREM,
-	PCF50633_IRQ_USBINS,
-	PCF50633_IRQ_USBREM,
-	PCF50633_IRQ_RESERVED1,
-	PCF50633_IRQ_RESERVED2,
-	PCF50633_IRQ_ALARM,
-	PCF50633_IRQ_SECOND,
-	PCF50633_IRQ_ONKEYR,
-	PCF50633_IRQ_ONKEYF,
-	PCF50633_IRQ_EXTON1R,
-	PCF50633_IRQ_EXTON1F,
-	PCF50633_IRQ_EXTON2R,
-	PCF50633_IRQ_EXTON2F,
-	PCF50633_IRQ_EXTON3R,
-	PCF50633_IRQ_EXTON3F,
-	PCF50633_IRQ_BATFULL,
-	PCF50633_IRQ_CHGHALT,
-	PCF50633_IRQ_THLIMON,
-	PCF50633_IRQ_THLIMOFF,
-	PCF50633_IRQ_USBLIMON,
-	PCF50633_IRQ_USBLIMOFF,
-	PCF50633_IRQ_ADCRDY,
-	PCF50633_IRQ_ONKEY1S,
-	PCF50633_IRQ_LOWSYS,
-	PCF50633_IRQ_LOWBAT,
-	PCF50633_IRQ_HIGHTMP,
-	PCF50633_IRQ_AUTOPWRFAIL,
-	PCF50633_IRQ_DWN1PWRFAIL,
-	PCF50633_IRQ_DWN2PWRFAIL,
-	PCF50633_IRQ_LEDPWRFAIL,
-	PCF50633_IRQ_LEDOVP,
-	PCF50633_IRQ_LDO1PWRFAIL,
-	PCF50633_IRQ_LDO2PWRFAIL,
-	PCF50633_IRQ_LDO3PWRFAIL,
-	PCF50633_IRQ_LDO4PWRFAIL,
-	PCF50633_IRQ_LDO5PWRFAIL,
-	PCF50633_IRQ_LDO6PWRFAIL,
-	PCF50633_IRQ_HCLDOPWRFAIL,
-	PCF50633_IRQ_HCLDOOVL,
-
-	/* Always last */
-	PCF50633_NUM_IRQ,
-};
-
-struct pcf50633 {
-	struct device *dev;
-	struct regmap *regmap;
-
-	struct pcf50633_platform_data *pdata;
-	int irq;
-	struct pcf50633_irq irq_handler[PCF50633_NUM_IRQ];
-	struct work_struct irq_work;
-	struct workqueue_struct *work_queue;
-	struct mutex lock;
-
-	u8 mask_regs[5];
-
-	u8 suspend_irq_masks[5];
-	u8 resume_reason[5];
-	int is_suspended;
-
-	int onkey1s_held;
-
-	struct platform_device *mbc_pdev;
-};
-
-enum pcf50633_reg_int1 {
-	PCF50633_INT1_ADPINS	= 0x01,	/* Adapter inserted */
-	PCF50633_INT1_ADPREM	= 0x02,	/* Adapter removed */
-	PCF50633_INT1_USBINS	= 0x04,	/* USB inserted */
-	PCF50633_INT1_USBREM	= 0x08,	/* USB removed */
-	/* reserved */
-	PCF50633_INT1_ALARM	= 0x40, /* RTC alarm time is reached */
-	PCF50633_INT1_SECOND	= 0x80,	/* RTC periodic second interrupt */
-};
-
-enum pcf50633_reg_int2 {
-	PCF50633_INT2_ONKEYR	= 0x01, /* ONKEY rising edge */
-	PCF50633_INT2_ONKEYF	= 0x02, /* ONKEY falling edge */
-	PCF50633_INT2_EXTON1R	= 0x04, /* EXTON1 rising edge */
-	PCF50633_INT2_EXTON1F	= 0x08, /* EXTON1 falling edge */
-	PCF50633_INT2_EXTON2R	= 0x10, /* EXTON2 rising edge */
-	PCF50633_INT2_EXTON2F	= 0x20, /* EXTON2 falling edge */
-	PCF50633_INT2_EXTON3R	= 0x40, /* EXTON3 rising edge */
-	PCF50633_INT2_EXTON3F	= 0x80, /* EXTON3 falling edge */
-};
-
-enum pcf50633_reg_int3 {
-	PCF50633_INT3_BATFULL	= 0x01, /* Battery full */
-	PCF50633_INT3_CHGHALT	= 0x02,	/* Charger halt */
-	PCF50633_INT3_THLIMON	= 0x04,
-	PCF50633_INT3_THLIMOFF	= 0x08,
-	PCF50633_INT3_USBLIMON	= 0x10,
-	PCF50633_INT3_USBLIMOFF	= 0x20,
-	PCF50633_INT3_ADCRDY	= 0x40, /* ADC result ready */
-	PCF50633_INT3_ONKEY1S	= 0x80,	/* ONKEY pressed 1 second */
-};
-
-enum pcf50633_reg_int4 {
-	PCF50633_INT4_LOWSYS		= 0x01,
-	PCF50633_INT4_LOWBAT		= 0x02,
-	PCF50633_INT4_HIGHTMP		= 0x04,
-	PCF50633_INT4_AUTOPWRFAIL	= 0x08,
-	PCF50633_INT4_DWN1PWRFAIL	= 0x10,
-	PCF50633_INT4_DWN2PWRFAIL	= 0x20,
-	PCF50633_INT4_LEDPWRFAIL	= 0x40,
-	PCF50633_INT4_LEDOVP		= 0x80,
-};
-
-enum pcf50633_reg_int5 {
-	PCF50633_INT5_LDO1PWRFAIL	= 0x01,
-	PCF50633_INT5_LDO2PWRFAIL	= 0x02,
-	PCF50633_INT5_LDO3PWRFAIL	= 0x04,
-	PCF50633_INT5_LDO4PWRFAIL	= 0x08,
-	PCF50633_INT5_LDO5PWRFAIL	= 0x10,
-	PCF50633_INT5_LDO6PWRFAIL	= 0x20,
-	PCF50633_INT5_HCLDOPWRFAIL	= 0x40,
-	PCF50633_INT5_HCLDOOVL		= 0x80,
-};
-
-/* misc. registers */
-#define PCF50633_REG_OOCSHDWN	0x0c
-
-/* LED registers */
-#define PCF50633_REG_LEDOUT 0x28
-#define PCF50633_REG_LEDENA 0x29
-#define PCF50633_REG_LEDCTL 0x2a
-#define PCF50633_REG_LEDDIM 0x2b
-
-static inline struct pcf50633 *dev_to_pcf50633(struct device *dev)
-{
-	return dev_get_drvdata(dev);
-}
-
-int pcf50633_irq_init(struct pcf50633 *pcf, int irq);
-void pcf50633_irq_free(struct pcf50633 *pcf);
-extern const struct dev_pm_ops pcf50633_pm;
-
-#endif
diff --git a/include/linux/mfd/pcf50633/mbc.h b/include/linux/mfd/pcf50633/mbc.h
deleted file mode 100644
index fa5cb9256d99..000000000000
--- a/include/linux/mfd/pcf50633/mbc.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * mbc.h  -- Driver for NXP PCF50633 Main Battery Charger
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * All rights reserved.
- */
-
-#ifndef __LINUX_MFD_PCF50633_MBC_H
-#define __LINUX_MFD_PCF50633_MBC_H
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/platform_device.h>
-
-#define PCF50633_REG_MBCC1	0x43
-#define PCF50633_REG_MBCC2	0x44
-#define PCF50633_REG_MBCC3	0x45
-#define PCF50633_REG_MBCC4	0x46
-#define PCF50633_REG_MBCC5	0x47
-#define PCF50633_REG_MBCC6	0x48
-#define PCF50633_REG_MBCC7	0x49
-#define PCF50633_REG_MBCC8	0x4a
-#define PCF50633_REG_MBCS1	0x4b
-#define PCF50633_REG_MBCS2	0x4c
-#define PCF50633_REG_MBCS3	0x4d
-
-enum pcf50633_reg_mbcc1 {
-	PCF50633_MBCC1_CHGENA		= 0x01,	/* Charger enable */
-	PCF50633_MBCC1_AUTOSTOP		= 0x02,
-	PCF50633_MBCC1_AUTORES		= 0x04, /* automatic resume */
-	PCF50633_MBCC1_RESUME		= 0x08, /* explicit resume cmd */
-	PCF50633_MBCC1_RESTART		= 0x10, /* restart charging */
-	PCF50633_MBCC1_PREWDTIME_60M	= 0x20,	/* max. precharging time */
-	PCF50633_MBCC1_WDTIME_1H	= 0x00,
-	PCF50633_MBCC1_WDTIME_2H	= 0x40,
-	PCF50633_MBCC1_WDTIME_4H	= 0x80,
-	PCF50633_MBCC1_WDTIME_6H	= 0xc0,
-};
-#define PCF50633_MBCC1_WDTIME_MASK	  0xc0
-
-enum pcf50633_reg_mbcc2 {
-	PCF50633_MBCC2_VBATCOND_2V7	= 0x00,
-	PCF50633_MBCC2_VBATCOND_2V85	= 0x01,
-	PCF50633_MBCC2_VBATCOND_3V0	= 0x02,
-	PCF50633_MBCC2_VBATCOND_3V15	= 0x03,
-	PCF50633_MBCC2_VMAX_4V		= 0x00,
-	PCF50633_MBCC2_VMAX_4V20	= 0x28,
-	PCF50633_MBCC2_VRESDEBTIME_64S	= 0x80,	/* debounce time (32/64sec) */
-};
-
-enum pcf50633_reg_mbcc7 {
-	PCF50633_MBCC7_USB_100mA	= 0x00,
-	PCF50633_MBCC7_USB_500mA	= 0x01,
-	PCF50633_MBCC7_USB_1000mA	= 0x02,
-	PCF50633_MBCC7_USB_SUSPEND	= 0x03,
-	PCF50633_MBCC7_BATTEMP_EN	= 0x04,
-	PCF50633_MBCC7_BATSYSIMAX_1A6	= 0x00,
-	PCF50633_MBCC7_BATSYSIMAX_1A8	= 0x40,
-	PCF50633_MBCC7_BATSYSIMAX_2A0	= 0x80,
-	PCF50633_MBCC7_BATSYSIMAX_2A2	= 0xc0,
-};
-#define PCF50633_MBCC7_USB_MASK 0x03
-
-enum pcf50633_reg_mbcc8 {
-	PCF50633_MBCC8_USBENASUS	= 0x10,
-};
-
-enum pcf50633_reg_mbcs1 {
-	PCF50633_MBCS1_USBPRES		= 0x01,
-	PCF50633_MBCS1_USBOK		= 0x02,
-	PCF50633_MBCS1_ADAPTPRES	= 0x04,
-	PCF50633_MBCS1_ADAPTOK		= 0x08,
-	PCF50633_MBCS1_TBAT_OK		= 0x00,
-	PCF50633_MBCS1_TBAT_ABOVE	= 0x10,
-	PCF50633_MBCS1_TBAT_BELOW	= 0x20,
-	PCF50633_MBCS1_TBAT_UNDEF	= 0x30,
-	PCF50633_MBCS1_PREWDTEXP	= 0x40,
-	PCF50633_MBCS1_WDTEXP		= 0x80,
-};
-
-enum pcf50633_reg_mbcs2_mbcmod {
-	PCF50633_MBCS2_MBC_PLAY		= 0x00,
-	PCF50633_MBCS2_MBC_USB_PRE	= 0x01,
-	PCF50633_MBCS2_MBC_USB_PRE_WAIT	= 0x02,
-	PCF50633_MBCS2_MBC_USB_FAST	= 0x03,
-	PCF50633_MBCS2_MBC_USB_FAST_WAIT = 0x04,
-	PCF50633_MBCS2_MBC_USB_SUSPEND	= 0x05,
-	PCF50633_MBCS2_MBC_ADP_PRE	= 0x06,
-	PCF50633_MBCS2_MBC_ADP_PRE_WAIT	= 0x07,
-	PCF50633_MBCS2_MBC_ADP_FAST	= 0x08,
-	PCF50633_MBCS2_MBC_ADP_FAST_WAIT = 0x09,
-	PCF50633_MBCS2_MBC_BAT_FULL	= 0x0a,
-	PCF50633_MBCS2_MBC_HALT		= 0x0b,
-};
-#define PCF50633_MBCS2_MBC_MASK		0x0f
-enum pcf50633_reg_mbcs2_chgstat {
-	PCF50633_MBCS2_CHGS_NONE	= 0x00,
-	PCF50633_MBCS2_CHGS_ADAPTER	= 0x10,
-	PCF50633_MBCS2_CHGS_USB		= 0x20,
-	PCF50633_MBCS2_CHGS_BOTH	= 0x30,
-};
-#define PCF50633_MBCS2_RESSTAT_AUTO	0x40
-
-enum pcf50633_reg_mbcs3 {
-	PCF50633_MBCS3_USBLIM_PLAY	= 0x01,
-	PCF50633_MBCS3_USBLIM_CGH	= 0x02,
-	PCF50633_MBCS3_TLIM_PLAY	= 0x04,
-	PCF50633_MBCS3_TLIM_CHG		= 0x08,
-	PCF50633_MBCS3_ILIM		= 0x10,	/* 1: Ibat > Icutoff */
-	PCF50633_MBCS3_VLIM		= 0x20,	/* 1: Vbat == Vmax */
-	PCF50633_MBCS3_VBATSTAT		= 0x40,	/* 1: Vbat > Vbatcond */
-	PCF50633_MBCS3_VRES		= 0x80, /* 1: Vbat > Vth(RES) */
-};
-
-#define PCF50633_MBCC2_VBATCOND_MASK	  0x03
-#define PCF50633_MBCC2_VMAX_MASK	  0x3c
-
-/* Charger status */
-#define PCF50633_MBC_USB_ONLINE		0x01
-#define PCF50633_MBC_USB_ACTIVE		0x02
-#define PCF50633_MBC_ADAPTER_ONLINE	0x04
-#define PCF50633_MBC_ADAPTER_ACTIVE	0x08
-
-int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma);
-
-int pcf50633_mbc_get_status(struct pcf50633 *);
-int pcf50633_mbc_get_usb_online_status(struct pcf50633 *);
-
-#endif
-
diff --git a/include/linux/mfd/pcf50633/pmic.h b/include/linux/mfd/pcf50633/pmic.h
deleted file mode 100644
index eac0c3d8e984..000000000000
--- a/include/linux/mfd/pcf50633/pmic.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __LINUX_MFD_PCF50633_PMIC_H
-#define __LINUX_MFD_PCF50633_PMIC_H
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/platform_device.h>
-
-#define PCF50633_REG_AUTOOUT	0x1a
-#define PCF50633_REG_AUTOENA	0x1b
-#define PCF50633_REG_AUTOCTL	0x1c
-#define PCF50633_REG_AUTOMXC	0x1d
-#define PCF50633_REG_DOWN1OUT	0x1e
-#define PCF50633_REG_DOWN1ENA	0x1f
-#define PCF50633_REG_DOWN1CTL	0x20
-#define PCF50633_REG_DOWN1MXC	0x21
-#define PCF50633_REG_DOWN2OUT	0x22
-#define PCF50633_REG_DOWN2ENA	0x23
-#define PCF50633_REG_DOWN2CTL	0x24
-#define PCF50633_REG_DOWN2MXC	0x25
-#define PCF50633_REG_MEMLDOOUT	0x26
-#define PCF50633_REG_MEMLDOENA	0x27
-#define PCF50633_REG_LDO1OUT	0x2d
-#define PCF50633_REG_LDO1ENA	0x2e
-#define PCF50633_REG_LDO2OUT	0x2f
-#define PCF50633_REG_LDO2ENA	0x30
-#define PCF50633_REG_LDO3OUT	0x31
-#define PCF50633_REG_LDO3ENA	0x32
-#define PCF50633_REG_LDO4OUT	0x33
-#define PCF50633_REG_LDO4ENA	0x34
-#define PCF50633_REG_LDO5OUT	0x35
-#define PCF50633_REG_LDO5ENA	0x36
-#define PCF50633_REG_LDO6OUT	0x37
-#define PCF50633_REG_LDO6ENA	0x38
-#define PCF50633_REG_HCLDOOUT	0x39
-#define PCF50633_REG_HCLDOENA	0x3a
-#define PCF50633_REG_HCLDOOVL	0x40
-
-enum pcf50633_regulator_enable {
-	PCF50633_REGULATOR_ON		= 0x01,
-	PCF50633_REGULATOR_ON_GPIO1	= 0x02,
-	PCF50633_REGULATOR_ON_GPIO2	= 0x04,
-	PCF50633_REGULATOR_ON_GPIO3	= 0x08,
-};
-#define PCF50633_REGULATOR_ON_MASK	0x0f
-
-enum pcf50633_regulator_phase {
-	PCF50633_REGULATOR_ACTPH1	= 0x00,
-	PCF50633_REGULATOR_ACTPH2	= 0x10,
-	PCF50633_REGULATOR_ACTPH3	= 0x20,
-	PCF50633_REGULATOR_ACTPH4	= 0x30,
-};
-#define PCF50633_REGULATOR_ACTPH_MASK	0x30
-
-enum pcf50633_regulator_id {
-	PCF50633_REGULATOR_AUTO,
-	PCF50633_REGULATOR_DOWN1,
-	PCF50633_REGULATOR_DOWN2,
-	PCF50633_REGULATOR_LDO1,
-	PCF50633_REGULATOR_LDO2,
-	PCF50633_REGULATOR_LDO3,
-	PCF50633_REGULATOR_LDO4,
-	PCF50633_REGULATOR_LDO5,
-	PCF50633_REGULATOR_LDO6,
-	PCF50633_REGULATOR_HCLDO,
-	PCF50633_REGULATOR_MEMLDO,
-};
-#endif
-
-- 
2.48.1


^ permalink raw reply related

* Re: [PATCH v2] fbdev: fsl-diu-fb: add missing device_remove_file()
From: Helge Deller @ 2025-03-09 19:42 UTC (permalink / raw)
  To: Shixiong Ou, Timur Tabi; +Cc: linux-fbdev, dri-devel, linux-kernel, Shixiong Ou
In-Reply-To: <20250309081607.27784-1-oushixiong1025@163.com>

On 3/9/25 09:16, Shixiong Ou wrote:
> From: Shixiong Ou <oushixiong@kylinos.cn>
>
> Call device_remove_file() when driver remove.
>
> Signed-off-by: Shixiong Ou <oushixiong@kylinos.cn>
> ---
> v1->v2:
> 	add has_sysfs_attrs flag.
>
>   drivers/video/fbdev/fsl-diu-fb.c | 6 ++++++
>   1 file changed, 6 insertions(+)
>
> diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
> index 5ac8201c3533..57f7fe6a4c76 100644
> --- a/drivers/video/fbdev/fsl-diu-fb.c
> +++ b/drivers/video/fbdev/fsl-diu-fb.c
> @@ -384,6 +384,7 @@ struct fsl_diu_data {
>   	__le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);
>   	uint8_t edid_data[EDID_LENGTH];
>   	bool has_edid;
> +	bool has_dev_attr;
>   } __aligned(32);
>
>   /* Determine the DMA address of a member of the fsl_diu_data structure */
> @@ -1809,6 +1810,7 @@ static int fsl_diu_probe(struct platform_device *pdev)
>   			data->dev_attr.attr.name);
>   	}
>
> +	data->has_dev_attr = true;
>   	dev_set_drvdata(&pdev->dev, data);
>   	return 0;
>
> @@ -1827,6 +1829,10 @@ static void fsl_diu_remove(struct platform_device *pdev)
>   	int i;
>
>   	data = dev_get_drvdata(&pdev->dev);
> +
> +	if (data->has_dev_attr)

Looking at other drivers (e.g. drivers/net/can/usb/esd_usb.c) it seems
that device_remove_file() is ok even if it's not fully initialized...

I think you can drop those extra checks.

Helge


> +		device_remove_file(&pdev->dev, &data->dev_attr);
> +
>   	disable_lcdc(&data->fsl_diu_info[0]);
>
>   	free_irq(data->irq, data->diu_reg);


^ permalink raw reply

* Re: [PATCH 0/1] Refactoring of framebuffer drawing routines
From: Helge Deller @ 2025-03-09 20:13 UTC (permalink / raw)
  To: Zsolt Kajtar, linux-fbdev, dri-devel, Thomas Zimmermann
In-Reply-To: <20250224205908.26336-1-soci@c64.rulez.org>

On 2/24/25 21:59, Zsolt Kajtar wrote:
> 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

I've applied that series to the fbdev git tree so that it gets some testing...

Helge

^ permalink raw reply

* Re: [PATCH 1/1] fbdev: hyperv_fb: Fix hang in kdump kernel when on Hyper-V Gen 2 VMs
From: Wei Liu @ 2025-03-09 23:52 UTC (permalink / raw)
  To: Helge Deller
  Cc: Michael Kelley, wei.liu@kernel.org,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org
In-Reply-To: <fc8f7246-2e05-4433-85d8-65dbed723826@gmx.de>

On Sun, Mar 09, 2025 at 03:20:04PM +0100, Helge Deller wrote:
> On 3/9/25 05:10, Michael Kelley wrote:
> > From: Helge Deller <deller@gmx.de> Sent: Saturday, March 8, 2025 6:59 PM
> > > 
> > > On 2/19/25 00:01, mhkelley58@gmail.com wrote:
> > > > From: Michael Kelley <mhklinux@outlook.com>
> > > > 
> > [snip]
> > 
> > > > 
> > > > Reported-by: Thomas Tai <thomas.tai@oracle.com>
> > > > Fixes: c25a19afb81c ("fbdev/hyperv_fb: Do not clear global screen_info")
> > > > Signed-off-by: Michael Kelley <mhklinux@outlook.com>
> > > > ---
> > > > The "Fixes" tag uses commit c25a19afb81c because that's where the problem
> > > > was re-exposed, and how far back a stable backport is needed. But I've
> > > > taken a completely different, and hopefully better, approach in the
> > > > solution that isn't related to the code changes in c25a19afb81c.
> > > > 
> > > >    drivers/video/fbdev/hyperv_fb.c | 20 +++++++++++++-------
> > > >    1 file changed, 13 insertions(+), 7 deletions(-)
> > > 
> > > applied to fbdev tree.
> > > 
> > 
> > Thank you!
> > 
> > Related, I noticed the patch "fbdev: hyperv_fb: iounmap() the correct
> > memory when removing a device" is also in the fbdev for-next branch.
> > Wei Liu previously applied this patch to the hyperv-fixes tree (see [1])
> > and it's already in linux-next. Won't having it also in fbdev produce a
> > merge conflict?
> > [1] https://lore.kernel.org/linux-hyperv/Z6wHDw8BssJyQHiM@liuwe-devbox-debian-v2/
> 
> Thanks Michael!
> I now dropped that patch from the fbdev tree to avoid collisions.
> 
> Btw, I'm fine if we agree that all hyperv-fbdev fixes & patches go through
> hyperv or other trees. Just let me know.

I can pick up all hyperv-fbdev patches in the future. Thanks Helge.

Wei.

> 
> Helge

^ permalink raw reply


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