summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/backlight/88pm860x_bl.c1
-rw-r--r--drivers/video/backlight/Kconfig20
-rw-r--r--drivers/video/backlight/Makefile2
-rw-r--r--drivers/video/backlight/adp5520_bl.c1
-rw-r--r--drivers/video/backlight/adp8860_bl.c1
-rw-r--r--drivers/video/backlight/adp8870_bl.c1
-rw-r--r--drivers/video/backlight/apple_dwi_bl.c123
-rw-r--r--drivers/video/backlight/as3711_bl.c1
-rw-r--r--drivers/video/backlight/bd6107.c1
-rw-r--r--drivers/video/backlight/da903x_bl.c1
-rw-r--r--drivers/video/backlight/da9052_bl.c1
-rw-r--r--drivers/video/backlight/ep93xx_bl.c1
-rw-r--r--drivers/video/backlight/hp680_bl.c1
-rw-r--r--drivers/video/backlight/led_bl.c5
-rw-r--r--drivers/video/backlight/locomolcd.c1
-rw-r--r--drivers/video/backlight/lv5207lp.c1
-rw-r--r--drivers/video/backlight/max8925_bl.c1
-rw-r--r--drivers/video/backlight/pcf50633-backlight.c154
-rw-r--r--drivers/video/backlight/tps65217_bl.c1
-rw-r--r--drivers/video/backlight/vgg2432a4.c1
-rw-r--r--drivers/video/backlight/wm831x_bl.c1
-rw-r--r--drivers/video/console/Kconfig9
-rw-r--r--drivers/video/fbdev/aty/mach64_cursor.c7
-rw-r--r--drivers/video/fbdev/au1100fb.c4
-rw-r--r--drivers/video/fbdev/core/Kconfig10
-rw-r--r--drivers/video/fbdev/core/bitblit.c5
-rw-r--r--drivers/video/fbdev/core/cfbcopyarea.c428
-rw-r--r--drivers/video/fbdev/core/cfbfillrect.c362
-rw-r--r--drivers/video/fbdev/core/cfbimgblt.c357
-rw-r--r--drivers/video/fbdev/core/cfbmem.h43
-rw-r--r--drivers/video/fbdev/core/fb_copyarea.h405
-rw-r--r--drivers/video/fbdev/core/fb_draw.h274
-rw-r--r--drivers/video/fbdev/core/fb_fillrect.h280
-rw-r--r--drivers/video/fbdev/core/fb_imageblit.h495
-rw-r--r--drivers/video/fbdev/core/fbcon.c79
-rw-r--r--drivers/video/fbdev/core/fbcon.h38
-rw-r--r--drivers/video/fbdev/core/fbcon_ccw.c5
-rw-r--r--drivers/video/fbdev/core/fbcon_cw.c5
-rw-r--r--drivers/video/fbdev/core/fbcon_ud.c5
-rw-r--r--drivers/video/fbdev/core/fbsysfs.c69
-rw-r--r--drivers/video/fbdev/core/syscopyarea.c369
-rw-r--r--drivers/video/fbdev/core/sysfillrect.c324
-rw-r--r--drivers/video/fbdev/core/sysimgblt.c333
-rw-r--r--drivers/video/fbdev/core/sysmem.h39
-rw-r--r--drivers/video/fbdev/core/tileblit.c45
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dispc.c15
-rw-r--r--drivers/video/fbdev/pxafb.c23
-rw-r--r--drivers/video/fbdev/sh_mobile_lcdcfb.c29
-rw-r--r--drivers/video/fbdev/sm501fb.c7
-rw-r--r--drivers/video/fbdev/wmt_ge_rops.c30
52 files changed, 1795 insertions, 2621 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 44c9ef1435a2..5df981920a94 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -39,6 +39,7 @@ source "drivers/gpu/vga/Kconfig"
source "drivers/gpu/host1x/Kconfig"
source "drivers/gpu/ipu-v3/Kconfig"
+source "drivers/gpu/nova-core/Kconfig"
source "drivers/gpu/drm/Kconfig"
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index 720b5ada7fe8..0a1db2824076 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -11,7 +11,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/backlight.h>
#include <linux/mfd/88pm860x.h>
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 3614a5d29c71..d9374d208cee 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -70,7 +70,7 @@ config LCD_ILI9320
then say y to include a power driver for it.
config LCD_TDO24M
- tristate "Toppoly TDO24M and TDO35S LCD Panels support"
+ tristate "Toppoly TDO24M and TDO35S LCD Panels support"
depends on SPI_MASTER
help
If you have a Toppoly TDO24M/TDO35S series LCD panel, say y here to
@@ -290,6 +290,17 @@ config BACKLIGHT_APPLE
If you have an Intel-based Apple say Y to enable a driver for its
backlight.
+config BACKLIGHT_APPLE_DWI
+ tristate "Apple DWI 2-Wire Interface Backlight Driver"
+ depends on ARCH_APPLE || COMPILE_TEST
+ help
+ Say Y to enable the backlight driver for backlight controllers
+ attached via the Apple DWI 2-wire interface which is found in some
+ Apple iPhones, iPads and iPod touches.
+
+ To compile this driver as a module, choose M here: the module will
+ be called apple_dwi_bl.
+
config BACKLIGHT_QCOM_WLED
tristate "Qualcomm PMIC WLED Driver"
select REGMAP
@@ -359,13 +370,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..dfbb169bf6ea 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
+obj-$(CONFIG_BACKLIGHT_APPLE_DWI) += apple_dwi_bl.o
obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
@@ -49,7 +50,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/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c
index aa5c15e8db86..81c40d355aae 100644
--- a/drivers/video/backlight/adp5520_bl.c
+++ b/drivers/video/backlight/adp5520_bl.c
@@ -8,7 +8,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/mfd/adp5520.h>
#include <linux/slab.h>
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
index f51ada4795e8..d4bbd7a7406b 100644
--- a/drivers/video/backlight/adp8860_bl.c
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -11,7 +11,6 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/leds.h>
#include <linux/slab.h>
diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
index ad4bd4c8f441..e09e20492e7c 100644
--- a/drivers/video/backlight/adp8870_bl.c
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -11,7 +11,6 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
diff --git a/drivers/video/backlight/apple_dwi_bl.c b/drivers/video/backlight/apple_dwi_bl.c
new file mode 100644
index 000000000000..93bd744972d6
--- /dev/null
+++ b/drivers/video/backlight/apple_dwi_bl.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Driver for backlight controllers attached via Apple DWI 2-wire interface
+ *
+ * Copyright (c) 2024 Nick Chan <towinchenmi@gmail.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DWI_BL_CTL 0x0
+#define DWI_BL_CTL_SEND1 BIT(0)
+#define DWI_BL_CTL_SEND2 BIT(4)
+#define DWI_BL_CTL_SEND3 BIT(5)
+#define DWI_BL_CTL_LE_DATA BIT(6)
+/* Only used on Apple A9 and later */
+#define DWI_BL_CTL_SEND4 BIT(12)
+
+#define DWI_BL_CMD 0x4
+#define DWI_BL_CMD_TYPE GENMASK(31, 28)
+#define DWI_BL_CMD_TYPE_SET_BRIGHTNESS 0xa
+#define DWI_BL_CMD_DATA GENMASK(10, 0)
+
+#define DWI_BL_CTL_SEND (DWI_BL_CTL_SEND1 | \
+ DWI_BL_CTL_SEND2 | \
+ DWI_BL_CTL_SEND3 | \
+ DWI_BL_CTL_LE_DATA | \
+ DWI_BL_CTL_SEND4)
+
+#define DWI_BL_MAX_BRIGHTNESS 2047
+
+struct apple_dwi_bl {
+ void __iomem *base;
+};
+
+static int dwi_bl_update_status(struct backlight_device *bl)
+{
+ struct apple_dwi_bl *dwi_bl = bl_get_data(bl);
+
+ int brightness = backlight_get_brightness(bl);
+
+ u32 cmd = 0;
+
+ cmd |= FIELD_PREP(DWI_BL_CMD_DATA, brightness);
+ cmd |= FIELD_PREP(DWI_BL_CMD_TYPE, DWI_BL_CMD_TYPE_SET_BRIGHTNESS);
+
+ writel(cmd, dwi_bl->base + DWI_BL_CMD);
+ writel(DWI_BL_CTL_SEND, dwi_bl->base + DWI_BL_CTL);
+
+ return 0;
+}
+
+static int dwi_bl_get_brightness(struct backlight_device *bl)
+{
+ struct apple_dwi_bl *dwi_bl = bl_get_data(bl);
+
+ u32 cmd = readl(dwi_bl->base + DWI_BL_CMD);
+
+ return FIELD_GET(DWI_BL_CMD_DATA, cmd);
+}
+
+static const struct backlight_ops dwi_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = dwi_bl_get_brightness,
+ .update_status = dwi_bl_update_status
+};
+
+static int dwi_bl_probe(struct platform_device *dev)
+{
+ struct apple_dwi_bl *dwi_bl;
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct resource *res;
+
+ dwi_bl = devm_kzalloc(&dev->dev, sizeof(*dwi_bl), GFP_KERNEL);
+ if (!dwi_bl)
+ return -ENOMEM;
+
+ dwi_bl->base = devm_platform_get_and_ioremap_resource(dev, 0, &res);
+ if (IS_ERR(dwi_bl->base))
+ return PTR_ERR(dwi_bl->base);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = DWI_BL_MAX_BRIGHTNESS;
+ props.scale = BACKLIGHT_SCALE_LINEAR;
+
+ bl = devm_backlight_device_register(&dev->dev, dev->name, &dev->dev,
+ dwi_bl, &dwi_bl_ops, &props);
+ if (IS_ERR(bl))
+ return PTR_ERR(bl);
+
+ platform_set_drvdata(dev, dwi_bl);
+
+ bl->props.brightness = dwi_bl_get_brightness(bl);
+
+ return 0;
+}
+
+static const struct of_device_id dwi_bl_of_match[] = {
+ { .compatible = "apple,dwi-bl" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dwi_bl_of_match);
+
+static struct platform_driver dwi_bl_driver = {
+ .driver = {
+ .name = "apple-dwi-bl",
+ .of_match_table = dwi_bl_of_match
+ },
+ .probe = dwi_bl_probe,
+};
+
+module_platform_driver(dwi_bl_driver);
+
+MODULE_DESCRIPTION("Apple DWI Backlight Driver");
+MODULE_AUTHOR("Nick Chan <towinchenmi@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index e6f66bb35ef5..9f89eb19894e 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
-#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/mfd/as3711.h>
#include <linux/module.h>
diff --git a/drivers/video/backlight/bd6107.c b/drivers/video/backlight/bd6107.c
index 90764f83d2f1..74567af84e97 100644
--- a/drivers/video/backlight/bd6107.c
+++ b/drivers/video/backlight/bd6107.c
@@ -10,7 +10,6 @@
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/fb.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
index 71f21bbc7a9f..81ff42bec0ad 100644
--- a/drivers/video/backlight/da903x_bl.c
+++ b/drivers/video/backlight/da903x_bl.c
@@ -12,7 +12,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/mfd/da903x.h>
#include <linux/slab.h>
diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c
index 5e13ef96b717..f41523d78121 100644
--- a/drivers/video/backlight/da9052_bl.c
+++ b/drivers/video/backlight/da9052_bl.c
@@ -9,7 +9,6 @@
#include <linux/backlight.h>
#include <linux/delay.h>
-#include <linux/fb.h>
#include <linux/module.h>
#include <linux/platform_device.h>
diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c
index 2387009d452d..f59effc02352 100644
--- a/drivers/video/backlight/ep93xx_bl.c
+++ b/drivers/video/backlight/ep93xx_bl.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#define EP93XX_MAX_COUNT 255
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index fa9a983533b2..d8c2e4ada384 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -15,7 +15,6 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <cpu/dac.h>
diff --git a/drivers/video/backlight/led_bl.c b/drivers/video/backlight/led_bl.c
index ae34d1ecbfbe..d2db157b2c29 100644
--- a/drivers/video/backlight/led_bl.c
+++ b/drivers/video/backlight/led_bl.c
@@ -229,8 +229,11 @@ static void led_bl_remove(struct platform_device *pdev)
backlight_device_unregister(bl);
led_bl_power_off(priv);
- for (i = 0; i < priv->nb_leds; i++)
+ for (i = 0; i < priv->nb_leds; i++) {
+ mutex_lock(&priv->leds[i]->led_access);
led_sysfs_enable(priv->leds[i]);
+ mutex_unlock(&priv->leds[i]->led_access);
+ }
}
static const struct of_device_id led_bl_of_match[] = {
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index 346d3e29a843..1b493fb0516d 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -16,7 +16,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <asm/hardware/locomo.h>
diff --git a/drivers/video/backlight/lv5207lp.c b/drivers/video/backlight/lv5207lp.c
index 5f60989fa70f..a205f004eab2 100644
--- a/drivers/video/backlight/lv5207lp.c
+++ b/drivers/video/backlight/lv5207lp.c
@@ -9,7 +9,6 @@
#include <linux/backlight.h>
#include <linux/err.h>
-#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_data/lv5207lp.h>
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
index e607ec6fd4bf..4ac20a59e007 100644
--- a/drivers/video/backlight/max8925_bl.c
+++ b/drivers/video/backlight/max8925_bl.c
@@ -9,7 +9,6 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
-#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/backlight.h>
#include <linux/mfd/max8925.h>
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/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c
index d96d713fe7db..8aa860350644 100644
--- a/drivers/video/backlight/tps65217_bl.c
+++ b/drivers/video/backlight/tps65217_bl.c
@@ -11,7 +11,6 @@
#include <linux/kernel.h>
#include <linux/backlight.h>
#include <linux/err.h>
-#include <linux/fb.h>
#include <linux/mfd/tps65217.h>
#include <linux/module.h>
#include <linux/platform_device.h>
diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c
index bfc1913e8b55..3005eba6c82c 100644
--- a/drivers/video/backlight/vgg2432a4.c
+++ b/drivers/video/backlight/vgg2432a4.c
@@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/fb.h>
#include <linux/init.h>
#include <linux/lcd.h>
#include <linux/module.h>
diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c
index c5aaee205bdf..49027f04a1ec 100644
--- a/drivers/video/backlight/wm831x_bl.c
+++ b/drivers/video/backlight/wm831x_bl.c
@@ -9,7 +9,6 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/module.h>
-#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/slab.h>
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index bc31db6ef7d2..12f54480f57f 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -24,7 +24,7 @@ config VGA_CONSOLE
Say Y.
config MDA_CONSOLE
- depends on !M68K && !PARISC && ISA
+ depends on VGA_CONSOLE && ISA
tristate "MDA text console (dual-headed)"
help
Say Y here if you have an old MDA or monochrome Hercules graphics
@@ -47,12 +47,11 @@ config SGI_NEWPORT_CONSOLE
card of your Indy. Most people say Y here.
config DUMMY_CONSOLE
- bool
- default y
+ def_bool VT || VGA_CONSOLE || FRAMEBUFFER_CONSOLE
config DUMMY_CONSOLE_COLUMNS
int "Initial number of console screen columns"
- depends on DUMMY_CONSOLE && !ARCH_FOOTBRIDGE
+ depends on DUMMY_CONSOLE && !(ARCH_FOOTBRIDGE && VGA_CONSOLE)
default 160 if PARISC
default 80
help
@@ -62,7 +61,7 @@ config DUMMY_CONSOLE_COLUMNS
config DUMMY_CONSOLE_ROWS
int "Initial number of console screen rows"
- depends on DUMMY_CONSOLE && !ARCH_FOOTBRIDGE
+ depends on DUMMY_CONSOLE && !(ARCH_FOOTBRIDGE && VGA_CONSOLE)
default 64 if PARISC
default 30 if ARM
default 25
diff --git a/drivers/video/fbdev/aty/mach64_cursor.c b/drivers/video/fbdev/aty/mach64_cursor.c
index 971355c2cd7e..e826cb7dd55d 100644
--- a/drivers/video/fbdev/aty/mach64_cursor.c
+++ b/drivers/video/fbdev/aty/mach64_cursor.c
@@ -6,7 +6,6 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
-#include "../core/fb_draw.h"
#include <asm/io.h>
@@ -57,6 +56,12 @@
* definitation and CUR_VERT_POSN must be saturated to zero.
*/
+/* compose pixels based on mask */
+static inline unsigned long comp(unsigned long set, unsigned long unset, unsigned long mask)
+{
+ return ((set ^ unset) & mask) ^ unset;
+}
+
/*
* Hardware Cursor support.
*/
diff --git a/drivers/video/fbdev/au1100fb.c b/drivers/video/fbdev/au1100fb.c
index 840f22160763..6251a6b07b3a 100644
--- a/drivers/video/fbdev/au1100fb.c
+++ b/drivers/video/fbdev/au1100fb.c
@@ -137,13 +137,15 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
*/
int au1100fb_setmode(struct au1100fb_device *fbdev)
{
- struct fb_info *info = &fbdev->info;
+ struct fb_info *info;
u32 words;
int index;
if (!fbdev)
return -EINVAL;
+ info = &fbdev->info;
+
/* Update var-dependent FB info */
if (panel_is_active(fbdev->panel) || panel_is_color(fbdev->panel)) {
if (info->var.bits_per_pixel <= 8) {
diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig
index d554d8c543d4..4abe12db7594 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/bitblit.c b/drivers/video/fbdev/core/bitblit.c
index 3ff1b2a8659e..f9475c14f733 100644
--- a/drivers/video/fbdev/core/bitblit.c
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -59,12 +59,11 @@ static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
}
static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
+ int sx, int height, int width, int fg, int bg)
{
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
struct fb_fillrect region;
- region.color = attr_bgcol_ec(bgshift, vc, info);
+ region.color = bg;
region.dx = sx * vc->vc_font.width;
region.dy = sy * vc->vc_font.height;
region.width = width * vc->vc_font.width;
diff --git a/drivers/video/fbdev/core/cfbcopyarea.c b/drivers/video/fbdev/core/cfbcopyarea.c
index a271f57d9c6c..23fbf3a8df7c 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 cbaa4c9e2355..615de89256d5 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 7d1d2f1a627d..bcec4e32c0e7 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 000000000000..ce2f5f751ea1
--- /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 000000000000..53f1d5385c6e
--- /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 e0d829873930..8eb13f7b3972 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 000000000000..66042e534de7
--- /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 000000000000..3b2bb4946505
--- /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/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index e8b4e8c119b5..ac3c99ed92d1 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -160,7 +160,6 @@ static int info_idx = -1;
/* console rotation */
static int initial_rotation = -1;
-static int fbcon_has_sysfs;
static int margin_color;
static const struct consw fb_con;
@@ -1258,7 +1257,7 @@ static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
{
struct fb_info *info = fbcon_info_from_console(vc->vc_num);
struct fbcon_ops *ops = info->fbcon_par;
-
+ int fg, bg;
struct fbcon_display *p = &fb_display[vc->vc_num];
u_int y_break;
@@ -1279,16 +1278,18 @@ static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
fbcon_clear_margins(vc, 0);
}
+ fg = get_color(vc, info, vc->vc_video_erase_char, 1);
+ bg = get_color(vc, info, vc->vc_video_erase_char, 0);
/* Split blits that cross physical y_wrap boundary */
y_break = p->vrows - p->yscroll;
if (sy < y_break && sy + height - 1 >= y_break) {
u_int b = y_break - sy;
- ops->clear(vc, info, real_y(p, sy), sx, b, width);
+ ops->clear(vc, info, real_y(p, sy), sx, b, width, fg, bg);
ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
- width);
+ width, fg, bg);
} else
- ops->clear(vc, info, real_y(p, sy), sx, height, width);
+ ops->clear(vc, info, real_y(p, sy), sx, height, width, fg, bg);
}
static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
@@ -3157,7 +3158,7 @@ static const struct consw fb_con = {
.con_debug_leave = fbcon_debug_leave,
};
-static ssize_t store_rotate(struct device *device,
+static ssize_t rotate_store(struct device *device,
struct device_attribute *attr, const char *buf,
size_t count)
{
@@ -3179,7 +3180,7 @@ err:
return count;
}
-static ssize_t store_rotate_all(struct device *device,
+static ssize_t rotate_all_store(struct device *device,
struct device_attribute *attr,const char *buf,
size_t count)
{
@@ -3201,7 +3202,7 @@ err:
return count;
}
-static ssize_t show_rotate(struct device *device,
+static ssize_t rotate_show(struct device *device,
struct device_attribute *attr,char *buf)
{
struct fb_info *info;
@@ -3220,7 +3221,7 @@ err:
return sysfs_emit(buf, "%d\n", rotate);
}
-static ssize_t show_cursor_blink(struct device *device,
+static ssize_t cursor_blink_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *info;
@@ -3245,7 +3246,7 @@ err:
return sysfs_emit(buf, "%d\n", blink);
}
-static ssize_t store_cursor_blink(struct device *device,
+static ssize_t cursor_blink_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -3279,35 +3280,18 @@ err:
return count;
}
-static struct device_attribute device_attrs[] = {
- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
- __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
- store_cursor_blink),
-};
-
-static int fbcon_init_device(void)
-{
- int i, error = 0;
-
- fbcon_has_sysfs = 1;
-
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(fbcon_device, &device_attrs[i]);
+static DEVICE_ATTR_RW(cursor_blink);
+static DEVICE_ATTR_RW(rotate);
+static DEVICE_ATTR_WO(rotate_all);
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(fbcon_device, &device_attrs[i]);
+static struct attribute *fbcon_device_attrs[] = {
+ &dev_attr_cursor_blink.attr,
+ &dev_attr_rotate.attr,
+ &dev_attr_rotate_all.attr,
+ NULL
+};
- fbcon_has_sysfs = 0;
- }
-
- return 0;
-}
+ATTRIBUTE_GROUPS(fbcon_device);
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
static void fbcon_register_existing_fbs(struct work_struct *work)
@@ -3365,16 +3349,16 @@ void __init fb_console_init(void)
int i;
console_lock();
- fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
- "fbcon");
+ fbcon_device = device_create_with_groups(fb_class, NULL,
+ MKDEV(0, 0), NULL,
+ fbcon_device_groups, "fbcon");
if (IS_ERR(fbcon_device)) {
printk(KERN_WARNING "Unable to create device "
"for fbcon; errno = %ld\n",
PTR_ERR(fbcon_device));
fbcon_device = NULL;
- } else
- fbcon_init_device();
+ }
for (i = 0; i < MAX_NR_CONSOLES; i++)
con2fb_map[i] = -1;
@@ -3385,18 +3369,6 @@ void __init fb_console_init(void)
#ifdef MODULE
-static void __exit fbcon_deinit_device(void)
-{
- int i;
-
- if (fbcon_has_sysfs) {
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(fbcon_device, &device_attrs[i]);
-
- fbcon_has_sysfs = 0;
- }
-}
-
void __exit fb_console_exit(void)
{
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
@@ -3409,7 +3381,6 @@ void __exit fb_console_exit(void)
#endif
console_lock();
- fbcon_deinit_device();
device_destroy(fb_class, MKDEV(0, 0));
do_unregister_con_driver(&fb_con);
diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h
index df70ea5ec5b3..4d97e6d8a16a 100644
--- a/drivers/video/fbdev/core/fbcon.h
+++ b/drivers/video/fbdev/core/fbcon.h
@@ -55,7 +55,7 @@ struct fbcon_ops {
void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int dy, int dx, int height, int width);
void (*clear)(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width);
+ int sx, int height, int width, int fb, int bg);
void (*putcs)(struct vc_data *vc, struct fb_info *info,
const unsigned short *s, int count, int yy, int xx,
int fg, int bg);
@@ -116,42 +116,6 @@ static inline int mono_col(const struct fb_info *info)
return (~(0xfff << max_len)) & 0xff;
}
-static inline int attr_col_ec(int shift, struct vc_data *vc,
- struct fb_info *info, int is_fg)
-{
- int is_mono01;
- int col;
- int fg;
- int bg;
-
- if (!vc)
- return 0;
-
- if (vc->vc_can_do_color)
- return is_fg ? attr_fgcol(shift,vc->vc_video_erase_char)
- : attr_bgcol(shift,vc->vc_video_erase_char);
-
- if (!info)
- return 0;
-
- col = mono_col(info);
- is_mono01 = info->fix.visual == FB_VISUAL_MONO01;
-
- if (attr_reverse(vc->vc_video_erase_char)) {
- fg = is_mono01 ? col : 0;
- bg = is_mono01 ? 0 : col;
- }
- else {
- fg = is_mono01 ? 0 : col;
- bg = is_mono01 ? col : 0;
- }
-
- return is_fg ? fg : bg;
-}
-
-#define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0)
-#define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1)
-
/*
* Scroll Method
*/
diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c
index f9b794ff7d39..89ef4ba7e867 100644
--- a/drivers/video/fbdev/core/fbcon_ccw.c
+++ b/drivers/video/fbdev/core/fbcon_ccw.c
@@ -78,14 +78,13 @@ static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
}
static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
+ int sx, int height, int width, int fg, int bg)
{
struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
u32 vyres = GETVYRES(ops->p, info);
- region.color = attr_bgcol_ec(bgshift,vc,info);
+ region.color = bg;
region.dx = sy * vc->vc_font.height;
region.dy = vyres - ((sx + width) * vc->vc_font.width);
region.height = width * vc->vc_font.width;
diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c
index 903f6fc174e1..b9dac7940fb7 100644
--- a/drivers/video/fbdev/core/fbcon_cw.c
+++ b/drivers/video/fbdev/core/fbcon_cw.c
@@ -63,14 +63,13 @@ static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
}
static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
+ int sx, int height, int width, int fg, int bg)
{
struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
u32 vxres = GETVXRES(ops->p, info);
- region.color = attr_bgcol_ec(bgshift,vc,info);
+ region.color = bg;
region.dx = vxres - ((sy + height) * vc->vc_font.height);
region.dy = sx * vc->vc_font.width;
region.height = width * vc->vc_font.width;
diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c
index 594331936fd3..0af7913a2abd 100644
--- a/drivers/video/fbdev/core/fbcon_ud.c
+++ b/drivers/video/fbdev/core/fbcon_ud.c
@@ -64,15 +64,14 @@ static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy,
}
static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
+ int sx, int height, int width, int fg, int bg)
{
struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
u32 vyres = GETVYRES(ops->p, info);
u32 vxres = GETVXRES(ops->p, info);
- region.color = attr_bgcol_ec(bgshift,vc,info);
+ region.color = bg;
region.dy = vyres - ((sy + height) * vc->vc_font.height);
region.dx = vxres - ((sx + width) * vc->vc_font.width);
region.width = width * vc->vc_font.width;
diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c
index 1b3c9958ef5c..06d75c767579 100644
--- a/drivers/video/fbdev/core/fbsysfs.c
+++ b/drivers/video/fbdev/core/fbsysfs.c
@@ -416,55 +416,64 @@ static ssize_t show_bl_curve(struct device *device,
/* When cmap is added back in it should be a binary attribute
* not a text one. Consideration should also be given to converting
* fbdev to use configfs instead of sysfs */
-static struct device_attribute device_attrs[] = {
- __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
- __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
- __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
- __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
- __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
- __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
- __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
- __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(stride, S_IRUGO, show_stride, NULL),
- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
+static DEVICE_ATTR(bits_per_pixel, 0644, show_bpp, store_bpp);
+static DEVICE_ATTR(blank, 0644, show_blank, store_blank);
+static DEVICE_ATTR(console, 0644, show_console, store_console);
+static DEVICE_ATTR(cursor, 0644, show_cursor, store_cursor);
+static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
+static DEVICE_ATTR(modes, 0644, show_modes, store_modes);
+static DEVICE_ATTR(pan, 0644, show_pan, store_pan);
+static DEVICE_ATTR(virtual_size, 0644, show_virtual, store_virtual);
+static DEVICE_ATTR(name, 0444, show_name, NULL);
+static DEVICE_ATTR(stride, 0444, show_stride, NULL);
+static DEVICE_ATTR(rotate, 0644, show_rotate, store_rotate);
+static DEVICE_ATTR(state, 0644, show_fbstate, store_fbstate);
#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
- __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
+static DEVICE_ATTR(bl_curve, 0644, show_bl_curve, store_bl_curve);
#endif
+
+static struct attribute *fb_device_attrs[] = {
+ &dev_attr_bits_per_pixel.attr,
+ &dev_attr_blank.attr,
+ &dev_attr_console.attr,
+ &dev_attr_cursor.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_modes.attr,
+ &dev_attr_pan.attr,
+ &dev_attr_virtual_size.attr,
+ &dev_attr_name.attr,
+ &dev_attr_stride.attr,
+ &dev_attr_rotate.attr,
+ &dev_attr_state.attr,
+#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
+ &dev_attr_bl_curve.attr,
+#endif
+ NULL,
+};
+
+static const struct attribute_group fb_device_attr_group = {
+ .attrs = fb_device_attrs,
};
static int fb_init_device(struct fb_info *fb_info)
{
- int i, error = 0;
+ int ret;
dev_set_drvdata(fb_info->dev, fb_info);
fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(fb_info->dev, &device_attrs[i]);
-
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(fb_info->dev, &device_attrs[i]);
+ ret = device_add_group(fb_info->dev, &fb_device_attr_group);
+ if (ret)
fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
- }
return 0;
}
static void fb_cleanup_device(struct fb_info *fb_info)
{
- unsigned int i;
-
if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(fb_info->dev, &device_attrs[i]);
+ device_remove_group(fb_info->dev, &fb_device_attr_group);
fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
}
diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c
index 75e7001e8450..b634e2d21208 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 e49221a88ccc..372ca6a324c2 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 6949bbd51d92..c756cc658b7d 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 000000000000..033c31a96e78
--- /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];
+}
diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c
index eff7ec4da167..d342b90c42b7 100644
--- a/drivers/video/fbdev/core/tileblit.c
+++ b/drivers/video/fbdev/core/tileblit.c
@@ -32,16 +32,14 @@ static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy,
}
static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
+ int sx, int height, int width, int fg, int bg)
{
struct fb_tilerect rect;
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
rect.index = vc->vc_video_erase_char &
((vc->vc_hi_font_mask) ? 0x1ff : 0xff);
- rect.fg = attr_fgcol_ec(fgshift, vc, info);
- rect.bg = attr_bgcol_ec(bgshift, vc, info);
+ rect.fg = fg;
+ rect.bg = bg;
rect.sx = sx;
rect.sy = sy;
rect.width = width;
@@ -76,7 +74,42 @@ static void tile_putcs(struct vc_data *vc, struct fb_info *info,
static void tile_clear_margins(struct vc_data *vc, struct fb_info *info,
int color, int bottom_only)
{
- return;
+ unsigned int cw = vc->vc_font.width;
+ unsigned int ch = vc->vc_font.height;
+ unsigned int rw = info->var.xres - (vc->vc_cols*cw);
+ unsigned int bh = info->var.yres - (vc->vc_rows*ch);
+ unsigned int rs = info->var.xres - rw;
+ unsigned int bs = info->var.yres - bh;
+ unsigned int vwt = info->var.xres_virtual / cw;
+ unsigned int vht = info->var.yres_virtual / ch;
+ struct fb_tilerect rect;
+
+ rect.index = vc->vc_video_erase_char &
+ ((vc->vc_hi_font_mask) ? 0x1ff : 0xff);
+ rect.fg = color;
+ rect.bg = color;
+
+ if ((int) rw > 0 && !bottom_only) {
+ rect.sx = (info->var.xoffset + rs + cw - 1) / cw;
+ rect.sy = 0;
+ rect.width = (rw + cw - 1) / cw;
+ rect.height = vht;
+ if (rect.width + rect.sx > vwt)
+ rect.width = vwt - rect.sx;
+ if (rect.sx < vwt)
+ info->tileops->fb_tilefill(info, &rect);
+ }
+
+ if ((int) bh > 0) {
+ rect.sx = info->var.xoffset / cw;
+ rect.sy = (info->var.yoffset + bs) / ch;
+ rect.width = rs / cw;
+ rect.height = (bh + ch - 1) / ch;
+ if (rect.height + rect.sy > vht)
+ rect.height = vht - rect.sy;
+ if (rect.sy < vht)
+ info->tileops->fb_tilefill(info, &rect);
+ }
}
static void tile_cursor(struct vc_data *vc, struct fb_info *info, bool enable,
diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
index 5ac8201c3533..b71d15794ce8 100644
--- a/drivers/video/fbdev/fsl-diu-fb.c
+++ b/drivers/video/fbdev/fsl-diu-fb.c
@@ -1827,6 +1827,7 @@ static void fsl_diu_remove(struct platform_device *pdev)
int i;
data = dev_get_drvdata(&pdev->dev);
+ device_remove_file(&pdev->dev, &data->dev_attr);
disable_lcdc(&data->fsl_diu_info[0]);
free_irq(data->irq, data->diu_reg);
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c
index ccb96a5be07e..1dc70c96d813 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c
@@ -2659,13 +2659,8 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
row_inc = 0;
pix_inc = 0;
- if (plane == OMAP_DSS_WB) {
- frame_width = out_width;
- frame_height = out_height;
- } else {
- frame_width = in_width;
- frame_height = height;
- }
+ frame_width = in_width;
+ frame_height = height;
if (rotation_type == OMAP_DSS_ROT_TILER)
calc_tiler_rotation_offset(screen_width, frame_width,
@@ -2738,9 +2733,13 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool mem_to_mem)
{
int r;
- enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
+ enum omap_overlay_caps caps;
enum omap_channel channel;
+ if (plane == OMAP_DSS_WB)
+ return -EINVAL;
+
+ caps = dss_feat_get_overlay_caps(plane);
channel = dispc_ovl_get_channel_out(plane);
DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->"
diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c
index 4aa84853e31a..ee6da5084242 100644
--- a/drivers/video/fbdev/pxafb.c
+++ b/drivers/video/fbdev/pxafb.c
@@ -2233,32 +2233,27 @@ static int pxafb_probe(struct platform_device *dev)
{
struct pxafb_info *fbi;
struct pxafb_mach_info *inf, *pdata;
- int i, irq, ret;
+ int irq, ret;
dev_dbg(&dev->dev, "pxafb_probe\n");
ret = -ENOMEM;
pdata = dev_get_platdata(&dev->dev);
- inf = devm_kmalloc(&dev->dev, sizeof(*inf), GFP_KERNEL);
- if (!inf)
- goto failed;
-
if (pdata) {
- *inf = *pdata;
- inf->modes =
- devm_kmalloc_array(&dev->dev, pdata->num_modes,
- sizeof(inf->modes[0]), GFP_KERNEL);
+ inf = devm_kmemdup(&dev->dev, pdata, sizeof(*pdata), GFP_KERNEL);
+ if (!inf)
+ goto failed;
+
+ inf->modes = devm_kmemdup_array(&dev->dev, pdata->modes, pdata->num_modes,
+ sizeof(*pdata->modes), GFP_KERNEL);
if (!inf->modes)
goto failed;
- for (i = 0; i < inf->num_modes; i++)
- inf->modes[i] = pdata->modes[i];
} else {
inf = of_pxafb_of_mach_info(&dev->dev);
+ if (IS_ERR_OR_NULL(inf))
+ goto failed;
}
- if (IS_ERR_OR_NULL(inf))
- goto failed;
-
ret = pxafb_parse_options(&dev->dev, g_options, inf);
if (ret < 0)
goto failed;
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 4715dcb59811..dd950e4ab5ce 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -1338,16 +1338,19 @@ overlay_rop3_store(struct device *dev, struct device_attribute *attr,
return count;
}
-static const struct device_attribute overlay_sysfs_attrs[] = {
- __ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
- overlay_alpha_show, overlay_alpha_store),
- __ATTR(ovl_mode, S_IRUGO|S_IWUSR,
- overlay_mode_show, overlay_mode_store),
- __ATTR(ovl_position, S_IRUGO|S_IWUSR,
- overlay_position_show, overlay_position_store),
- __ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
- overlay_rop3_show, overlay_rop3_store),
+static DEVICE_ATTR_RW(overlay_alpha);
+static DEVICE_ATTR_RW(overlay_mode);
+static DEVICE_ATTR_RW(overlay_position);
+static DEVICE_ATTR_RW(overlay_rop3);
+
+static struct attribute *overlay_sysfs_attrs[] = {
+ &dev_attr_overlay_alpha.attr,
+ &dev_attr_overlay_mode.attr,
+ &dev_attr_overlay_position.attr,
+ &dev_attr_overlay_rop3.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(overlay_sysfs);
static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = {
.id = "SH Mobile LCDC",
@@ -1516,7 +1519,6 @@ sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
{
struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
struct fb_info *info = ovl->info;
- unsigned int i;
int ret;
if (info == NULL)
@@ -1530,12 +1532,6 @@ sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
dev_name(lcdc->dev), ovl->index, info->var.xres,
info->var.yres, info->var.bits_per_pixel);
- for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
- ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
- if (ret < 0)
- return ret;
- }
-
return 0;
}
@@ -2641,6 +2637,7 @@ err1:
static struct platform_driver sh_mobile_lcdc_driver = {
.driver = {
.name = "sh_mobile_lcdc_fb",
+ .dev_groups = overlay_sysfs_groups,
.pm = &sh_mobile_lcdc_dev_pm_ops,
},
.probe = sh_mobile_lcdc_probe,
diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c
index 7734377b2d87..ed6f4f43e2d5 100644
--- a/drivers/video/fbdev/sm501fb.c
+++ b/drivers/video/fbdev/sm501fb.c
@@ -327,6 +327,13 @@ static int sm501fb_check_var(struct fb_var_screeninfo *var,
if (var->xres_virtual > 4096 || var->yres_virtual > 2048)
return -EINVAL;
+ /* geometry sanity checks */
+ if (var->xres + var->xoffset > var->xres_virtual)
+ return -EINVAL;
+
+ if (var->yres + var->yoffset > var->yres_virtual)
+ return -EINVAL;
+
/* can cope with 8,16 or 32bpp */
if (var->bits_per_pixel <= 8)
diff --git a/drivers/video/fbdev/wmt_ge_rops.c b/drivers/video/fbdev/wmt_ge_rops.c
index 69106299ab47..92fbb3f3a0d3 100644
--- a/drivers/video/fbdev/wmt_ge_rops.c
+++ b/drivers/video/fbdev/wmt_ge_rops.c
@@ -12,7 +12,6 @@
#include <linux/io.h>
#include <linux/platform_device.h>
-#include "core/fb_draw.h"
#include "wmt_ge_rops.h"
#define GE_COMMAND_OFF 0x00
@@ -41,6 +40,33 @@
static void __iomem *regbase;
+/* from the spec it seems more like depth than bits per pixel */
+static inline unsigned long pixel_to_pat(u32 depth, u32 pixel, struct fb_info *p)
+{
+ switch (depth) {
+ case 1:
+ return ~0ul*pixel;
+ case 2:
+ return ~0ul/3*pixel;
+ case 4:
+ return ~0ul/15*pixel;
+ case 8:
+ return ~0ul/255*pixel;
+ case 12:
+ case 15:
+ case 16:
+ return ~0ul/0xffff*pixel;
+ case 18:
+ case 24:
+ return 0x1000001ul*pixel;
+ case 32:
+ return pixel;
+ default:
+ fb_warn_once(p, "%s: unsupported pixelformat %d\n", __func__, depth);
+ return 0;
+ }
+}
+
void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
unsigned long fg, pat;
@@ -54,7 +80,7 @@ void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
else
fg = rect->color;
- pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+ pat = pixel_to_pat(p->var.bits_per_pixel, fg, p);
if (p->fbops->fb_sync)
p->fbops->fb_sync(p);