diff options
Diffstat (limited to 'drivers/video')
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); |