diff options
Diffstat (limited to 'drivers/video')
203 files changed, 5271 insertions, 8343 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 44c9ef1435a2..a7144d275f54 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -37,8 +37,10 @@ source "drivers/char/agp/Kconfig" source "drivers/gpu/vga/Kconfig" +source "drivers/gpu/Kconfig" source "drivers/gpu/host1x/Kconfig" source "drivers/gpu/ipu-v3/Kconfig" +source "drivers/gpu/nova-core/Kconfig" source "drivers/gpu/drm/Kconfig" @@ -60,6 +62,25 @@ config HDMI endif # HAS_IOMEM +config FIRMWARE_EDID + bool "Enable firmware EDID" + depends on EFI_GENERIC_STUB || X86 + help + This enables access to the EDID transferred from the firmware. + On EFI systems, the EDID comes from the same device as the + primary GOP. On x86 with BIOS, it comes from the VESA BIOS. + DRM display drivers will be able to export the information + to userspace. + + Also enable this if DDC/I2C transfers do not work for your driver + and if you are using nvidiafb, i810fb or savagefb. + + In general, choosing Y for this option is safe. If you + experience extremely long delays while booting before you get + something on your display, try setting this to N. Matrox cards in + combination with certain motherboards and monitors are known to + suffer from this problem. + if VT source "drivers/video/console/Kconfig" endif @@ -69,5 +90,6 @@ if FB_CORE || SGI_NEWPORT_CONSOLE endif +source "drivers/gpu/trace/Kconfig" endmenu 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..a7a3fbaf7c29 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 @@ -156,6 +156,14 @@ config BACKLIGHT_ATMEL_LCDC If in doubt, it's safe to enable this option; it doesn't kick in unless the board's description says it's wired that way. +config BACKLIGHT_AW99706 + tristate "Backlight Driver for Awinic AW99706" + depends on I2C + select REGMAP_I2C + help + If you have a LCD backlight connected to the WLED output of AW99706 + WLED output, say Y here to enable this driver. + config BACKLIGHT_EP93XX tristate "Cirrus EP93xx Backlight Driver" depends on FB_EP93XX @@ -185,6 +193,7 @@ config BACKLIGHT_KTD253 config BACKLIGHT_KTD2801 tristate "Backlight Driver for Kinetic KTD2801" + depends on GPIOLIB || COMPILE_TEST select LEDS_EXPRESSWIRE help Say Y to enable the backlight driver for the Kinetic KTD2801 1-wire @@ -249,6 +258,17 @@ config BACKLIGHT_PWM If you have a LCD backlight adjustable by PWM, say Y to enable this driver. +config BACKLIGHT_CGBC + tristate "Congatec Board Controller (CGBC) backlight support" + depends on MFD_CGBC && X86 + help + Say Y here to enable support for LCD backlight control on Congatec + x86-based boards via the CGBC (Congatec Board Controller). + + This driver provides backlight brightness control through the Linux + backlight subsystem. It communicates with the board controller to + adjust LCD backlight using PWM signals. + config BACKLIGHT_DA903X tristate "Backlight Driver for DA9030/DA9034 using WLED" depends on PMIC_DA903X @@ -290,6 +310,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 +390,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..794820a98ed4 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -23,8 +23,11 @@ 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_AW99706) += aw99706.o obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o +obj-$(CONFIG_BACKLIGHT_CGBC) += cgbc_bl.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o @@ -49,7 +52,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_bl.c b/drivers/video/backlight/apple_bl.c index aaa824437a2a..423513d68b5b 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -24,6 +24,7 @@ #include <linux/pci.h> #include <linux/acpi.h> #include <linux/atomic.h> +#include <linux/platform_device.h> #include <acpi/video.h> static struct backlight_device *apple_backlight_device; @@ -134,7 +135,7 @@ static const struct hw_data nvidia_chipset_data = { .set_brightness = nvidia_chipset_set_brightness, }; -static int apple_bl_add(struct acpi_device *dev) +static int apple_bl_probe(struct platform_device *pdev) { struct backlight_properties props; struct pci_dev *host; @@ -193,7 +194,7 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static void apple_bl_remove(struct acpi_device *dev) +static void apple_bl_remove(struct platform_device *pdev) { backlight_device_unregister(apple_backlight_device); @@ -206,12 +207,12 @@ static const struct acpi_device_id apple_bl_ids[] = { {"", 0}, }; -static struct acpi_driver apple_bl_driver = { - .name = "Apple backlight", - .ids = apple_bl_ids, - .ops = { - .add = apple_bl_add, - .remove = apple_bl_remove, +static struct platform_driver apple_bl_driver = { + .probe = apple_bl_probe, + .remove = apple_bl_remove, + .driver = { + .name = "Apple backlight", + .acpi_match_table = apple_bl_ids, }, }; @@ -224,12 +225,12 @@ static int __init apple_bl_init(void) if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; - return acpi_bus_register_driver(&apple_bl_driver); + return platform_driver_register(&apple_bl_driver); } static void __exit apple_bl_exit(void) { - acpi_bus_unregister_driver(&apple_bl_driver); + platform_driver_unregister(&apple_bl_driver); } module_init(apple_bl_init); diff --git a/drivers/video/backlight/apple_dwi_bl.c b/drivers/video/backlight/apple_dwi_bl.c new file mode 100644 index 000000000000..ed8bf13d3f51 --- /dev/null +++ b/drivers/video/backlight/apple_dwi_bl.c @@ -0,0 +1,124 @@ +// 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/mod_devicetable.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..753160bbc3e7 100644 --- a/drivers/video/backlight/as3711_bl.c +++ b/drivers/video/backlight/as3711_bl.c @@ -10,10 +10,10 @@ #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> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/drivers/video/backlight/aw99706.c b/drivers/video/backlight/aw99706.c new file mode 100644 index 000000000000..938f352aaab7 --- /dev/null +++ b/drivers/video/backlight/aw99706.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * aw99706 - Backlight driver for the AWINIC AW99706 + * + * Copyright (C) 2025 Junjie Cao <caojunjie650@gmail.com> + * Copyright (C) 2025 Pengyu Luo <mitltlatltl@gmail.com> + * + * Based on vendor driver: + * Copyright (c) 2023 AWINIC Technology CO., LTD + */ + +#include <linux/backlight.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#define AW99706_MAX_BRT_LVL 4095 +#define AW99706_REG_MAX 0x1F +#define AW99706_ID 0x07 + +/* registers list */ +#define AW99706_CFG0_REG 0x00 +#define AW99706_DIM_MODE_MASK GENMASK(1, 0) + +#define AW99706_CFG1_REG 0x01 +#define AW99706_SW_FREQ_MASK GENMASK(3, 0) +#define AW99706_SW_ILMT_MASK GENMASK(5, 4) + +#define AW99706_CFG2_REG 0x02 +#define AW99706_ILED_MAX_MASK GENMASK(6, 0) +#define AW99706_UVLOSEL_MASK BIT(7) + +#define AW99706_CFG3_REG 0x03 +#define AW99706_CFG4_REG 0x04 +#define AW99706_BRT_MSB_MASK GENMASK(3, 0) + +#define AW99706_CFG5_REG 0x05 +#define AW99706_BRT_LSB_MASK GENMASK(7, 0) + +#define AW99706_CFG6_REG 0x06 +#define AW99706_RAMP_CTL_MASK GENMASK(7, 6) + +#define AW99706_CFG7_REG 0x07 +#define AW99706_CFG8_REG 0x08 +#define AW99706_CFG9_REG 0x09 +#define AW99706_CFGA_REG 0x0A +#define AW99706_CFGB_REG 0x0B +#define AW99706_CFGC_REG 0x0C +#define AW99706_CFGD_REG 0x0D +#define AW99706_FLAG_REG 0x10 +#define AW99706_BACKLIGHT_EN_MASK BIT(7) + +#define AW99706_CHIPID_REG 0x11 +#define AW99706_LED_OPEN_FLAG_REG 0x12 +#define AW99706_LED_SHORT_FLAG_REG 0x13 +#define AW99706_MTPLDOSEL_REG 0x1E +#define AW99706_MTPRUN_REG 0x1F + +#define RESV 0 + +/* Boost switching frequency table, in Hz */ +static const u32 aw99706_sw_freq_tbl[] = { + RESV, RESV, RESV, RESV, 300000, 400000, 500000, 600000, + 660000, 750000, 850000, 1000000, 1200000, 1330000, 1500000, 1700000 +}; + +/* Switching current limitation table, in uA */ +static const u32 aw99706_sw_ilmt_tbl[] = { + 1500000, 2000000, 2500000, 3000000 +}; + +/* ULVO threshold table, in uV */ +static const u32 aw99706_ulvo_thres_tbl[] = { + 2200000, 5000000 +}; + +struct aw99706_dt_prop { + const char * const name; + int (*lookup)(const struct aw99706_dt_prop *prop, u32 dt_val, u8 *val); + const u32 * const lookup_tbl; + u8 tbl_size; + u8 reg; + u8 mask; + u32 def_val; +}; + +static int aw99706_dt_property_lookup(const struct aw99706_dt_prop *prop, + u32 dt_val, u8 *val) +{ + int i; + + if (!prop->lookup_tbl) { + *val = dt_val; + return 0; + } + + for (i = 0; i < prop->tbl_size; i++) + if (prop->lookup_tbl[i] == dt_val) + break; + + *val = i; + + return i == prop->tbl_size ? -1 : 0; +} + +#define MIN_ILED_MAX 5000 +#define MAX_ILED_MAX 50000 +#define STEP_ILED_MAX 500 + +static int +aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop *prop, + u32 dt_val, u8 *val) +{ + if (dt_val > MAX_ILED_MAX || dt_val < MIN_ILED_MAX) + return -1; + + *val = (dt_val - MIN_ILED_MAX) / STEP_ILED_MAX; + + return (dt_val - MIN_ILED_MAX) % STEP_ILED_MAX; +} + +static const struct aw99706_dt_prop aw99706_dt_props[] = { + { + "awinic,dim-mode", aw99706_dt_property_lookup, + NULL, 0, + AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1, + }, + { + "awinic,sw-freq", aw99706_dt_property_lookup, + aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl), + AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000, + }, + { + "awinic,sw-ilmt", aw99706_dt_property_lookup, + aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl), + AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000, + }, + { + "awinic,iled-max", aw99706_dt_property_iled_max_convert, + NULL, 0, + AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000, + + }, + { + "awinic,uvlo-thres", aw99706_dt_property_lookup, + aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl), + AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000, + }, + { + "awinic,ramp-ctl", aw99706_dt_property_lookup, + NULL, 0, + AW99706_CFG6_REG, AW99706_RAMP_CTL_MASK, 2, + } +}; + +struct reg_init_data { + u8 reg; + u8 mask; + u8 val; +}; + +struct aw99706_device { + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + struct backlight_device *bl_dev; + struct gpio_desc *hwen_gpio; + struct reg_init_data init_tbl[ARRAY_SIZE(aw99706_dt_props)]; + bool bl_enable; +}; + +enum reg_access { + REG_NONE_ACCESS = 0, + REG_RD_ACCESS = 1, + REG_WR_ACCESS = 2, +}; + +static const u8 aw99706_regs[AW99706_REG_MAX + 1] = { + [AW99706_CFG0_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG1_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG2_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG3_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG4_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG5_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG6_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG7_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG8_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFG9_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFGA_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFGB_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFGC_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_CFGD_REG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW99706_FLAG_REG] = REG_RD_ACCESS, + [AW99706_CHIPID_REG] = REG_RD_ACCESS, + [AW99706_LED_OPEN_FLAG_REG] = REG_RD_ACCESS, + [AW99706_LED_SHORT_FLAG_REG] = REG_RD_ACCESS, + + /* + * Write bit is dropped here, writing BIT(0) to MTPLDOSEL will unlock + * Multi-time Programmable (MTP). + */ + [AW99706_MTPLDOSEL_REG] = REG_RD_ACCESS, + [AW99706_MTPRUN_REG] = REG_NONE_ACCESS, +}; + +static bool aw99706_readable_reg(struct device *dev, unsigned int reg) +{ + return aw99706_regs[reg] & REG_RD_ACCESS; +} + +static bool aw99706_writeable_reg(struct device *dev, unsigned int reg) +{ + return aw99706_regs[reg] & REG_WR_ACCESS; +} + +static inline int aw99706_i2c_read(struct aw99706_device *aw, u8 reg, + unsigned int *val) +{ + return regmap_read(aw->regmap, reg, val); +} + +static inline int aw99706_i2c_write(struct aw99706_device *aw, u8 reg, u8 val) +{ + return regmap_write(aw->regmap, reg, val); +} + +static inline int aw99706_i2c_update_bits(struct aw99706_device *aw, u8 reg, + u8 mask, u8 val) +{ + return regmap_update_bits(aw->regmap, reg, mask, val); +} + +static void aw99706_dt_parse(struct aw99706_device *aw, + struct backlight_properties *bl_props) +{ + const struct aw99706_dt_prop *prop; + u32 dt_val; + int ret, i; + u8 val; + + for (i = 0; i < ARRAY_SIZE(aw99706_dt_props); i++) { + prop = &aw99706_dt_props[i]; + ret = device_property_read_u32(aw->dev, prop->name, &dt_val); + if (ret < 0) + dt_val = prop->def_val; + + if (prop->lookup(prop, dt_val, &val)) { + dev_warn(aw->dev, "invalid value %d for property %s, using default value %d\n", + dt_val, prop->name, prop->def_val); + + prop->lookup(prop, prop->def_val, &val); + } + + aw->init_tbl[i].reg = prop->reg; + aw->init_tbl[i].mask = prop->mask; + aw->init_tbl[i].val = val << __ffs(prop->mask); + } + + bl_props->brightness = AW99706_MAX_BRT_LVL >> 1; + bl_props->max_brightness = AW99706_MAX_BRT_LVL; + device_property_read_u32(aw->dev, "default-brightness", + &bl_props->brightness); + device_property_read_u32(aw->dev, "max-brightness", + &bl_props->max_brightness); + + if (bl_props->max_brightness > AW99706_MAX_BRT_LVL) + bl_props->max_brightness = AW99706_MAX_BRT_LVL; + + if (bl_props->brightness > bl_props->max_brightness) + bl_props->brightness = bl_props->max_brightness; +} + +static int aw99706_hw_init(struct aw99706_device *aw) +{ + int ret, i; + + gpiod_set_value_cansleep(aw->hwen_gpio, 1); + + for (i = 0; i < ARRAY_SIZE(aw->init_tbl); i++) { + ret = aw99706_i2c_update_bits(aw, aw->init_tbl[i].reg, + aw->init_tbl[i].mask, + aw->init_tbl[i].val); + if (ret < 0) { + dev_err(aw->dev, "Failed to write init data %d\n", ret); + return ret; + } + } + + return 0; +} + +static int aw99706_bl_enable(struct aw99706_device *aw, bool en) +{ + int ret; + u8 val; + + val = FIELD_PREP(AW99706_BACKLIGHT_EN_MASK, en); + ret = aw99706_i2c_update_bits(aw, AW99706_CFGD_REG, + AW99706_BACKLIGHT_EN_MASK, val); + if (ret) + dev_err(aw->dev, "Failed to enable backlight!\n"); + + return ret; +} + +static int aw99706_update_brightness(struct aw99706_device *aw, u32 brt_lvl) +{ + bool bl_enable_now = !!brt_lvl; + int ret; + + ret = aw99706_i2c_write(aw, AW99706_CFG4_REG, + (brt_lvl >> 8) & AW99706_BRT_MSB_MASK); + if (ret < 0) + return ret; + + ret = aw99706_i2c_write(aw, AW99706_CFG5_REG, + brt_lvl & AW99706_BRT_LSB_MASK); + if (ret < 0) + return ret; + + if (aw->bl_enable != bl_enable_now) { + ret = aw99706_bl_enable(aw, bl_enable_now); + if (!ret) + aw->bl_enable = bl_enable_now; + } + + return ret; +} + +static int aw99706_bl_update_status(struct backlight_device *bl) +{ + struct aw99706_device *aw = bl_get_data(bl); + + return aw99706_update_brightness(aw, bl->props.brightness); +} + +static const struct backlight_ops aw99706_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = aw99706_bl_update_status, +}; + +static const struct regmap_config aw99706_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AW99706_REG_MAX, + .writeable_reg = aw99706_writeable_reg, + .readable_reg = aw99706_readable_reg, +}; + +static int aw99706_chip_id_read(struct aw99706_device *aw) +{ + int ret; + unsigned int val; + + ret = aw99706_i2c_read(aw, AW99706_CHIPID_REG, &val); + if (ret < 0) + return ret; + + return val; +} + +static int aw99706_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct aw99706_device *aw; + struct backlight_device *bl_dev; + struct backlight_properties props = {}; + int ret = 0; + + aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL); + if (!aw) + return -ENOMEM; + + aw->client = client; + aw->dev = dev; + i2c_set_clientdata(client, aw); + + aw->regmap = devm_regmap_init_i2c(client, &aw99706_regmap_config); + if (IS_ERR(aw->regmap)) + return dev_err_probe(dev, PTR_ERR(aw->regmap), + "Failed to init regmap\n"); + + ret = aw99706_chip_id_read(aw); + if (ret != AW99706_ID) + return dev_err_probe(dev, -ENODEV, + "Unknown chip id 0x%02x\n", ret); + + aw99706_dt_parse(aw, &props); + + aw->hwen_gpio = devm_gpiod_get(aw->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(aw->hwen_gpio)) + return dev_err_probe(dev, PTR_ERR(aw->hwen_gpio), + "Failed to get enable gpio\n"); + + ret = aw99706_hw_init(aw); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to initialize the chip\n"); + + props.type = BACKLIGHT_RAW; + props.scale = BACKLIGHT_SCALE_LINEAR; + + bl_dev = devm_backlight_device_register(dev, "aw99706-backlight", dev, + aw, &aw99706_bl_ops, &props); + if (IS_ERR(bl_dev)) + return dev_err_probe(dev, PTR_ERR(bl_dev), + "Failed to register backlight!\n"); + + aw->bl_dev = bl_dev; + + return 0; +} + +static void aw99706_remove(struct i2c_client *client) +{ + struct aw99706_device *aw = i2c_get_clientdata(client); + + aw99706_update_brightness(aw, 0); + + msleep(50); + + gpiod_set_value_cansleep(aw->hwen_gpio, 0); +} + +static int aw99706_suspend(struct device *dev) +{ + struct aw99706_device *aw = dev_get_drvdata(dev); + + return aw99706_update_brightness(aw, 0); +} + +static int aw99706_resume(struct device *dev) +{ + struct aw99706_device *aw = dev_get_drvdata(dev); + + return aw99706_hw_init(aw); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume); + +static const struct i2c_device_id aw99706_ids[] = { + { "aw99706" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw99706_ids); + +static const struct of_device_id aw99706_match_table[] = { + { .compatible = "awinic,aw99706", }, + { } +}; +MODULE_DEVICE_TABLE(of, aw99706_match_table); + +static struct i2c_driver aw99706_i2c_driver = { + .probe = aw99706_probe, + .remove = aw99706_remove, + .id_table = aw99706_ids, + .driver = { + .name = "aw99706", + .of_match_table = aw99706_match_table, + .pm = pm_ptr(&aw99706_pm_ops), + }, +}; + +module_i2c_driver(aw99706_i2c_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BackLight driver for aw99706"); diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index f699e5827ccb..a22d0bbb6e63 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -15,8 +15,8 @@ #include <linux/notifier.h> #include <linux/ctype.h> #include <linux/err.h> -#include <linux/fb.h> #include <linux/slab.h> +#include <linux/of.h> #ifdef CONFIG_PMAC_BACKLIGHT #include <asm/backlight.h> @@ -57,10 +57,10 @@ * a hot-key to adjust backlight, the driver must notify the backlight * core that brightness has changed using backlight_force_update(). * - * The backlight driver core receives notifications from fbdev and - * if the event is FB_EVENT_BLANK and if the value of blank, from the - * FBIOBLANK ioctrl, results in a change in the backlight state the - * update_status() operation is called. + * Display drives can control the backlight device's status using + * backlight_notify_blank() and backlight_notify_blank_all(). If this + * results in a change in the backlight state the functions call the + * update_status() operation. */ static struct list_head backlight_dev_list; @@ -78,85 +78,40 @@ static const char *const backlight_scale_types[] = { [BACKLIGHT_SCALE_NON_LINEAR] = "non-linear", }; -#if defined(CONFIG_FB_CORE) || (defined(CONFIG_FB_CORE_MODULE) && \ - defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) -/* - * fb_notifier_callback - * - * This callback gets called when something important happens inside a - * framebuffer driver. The backlight core only cares about FB_BLANK_UNBLANK - * which is reported to the driver using backlight_update_status() - * as a state change. - * - * There may be several fbdev's connected to the backlight device, - * in which case they are kept track of. A state change is only reported - * if there is a change in backlight for the specified fbdev. - */ -static int fb_notifier_callback(struct notifier_block *self, - unsigned long event, void *data) +void backlight_notify_blank(struct backlight_device *bd, struct device *display_dev, + bool fb_on, bool prev_fb_on) { - struct backlight_device *bd; - struct fb_event *evdata = data; - struct fb_info *info = evdata->info; - struct backlight_device *fb_bd = fb_bl_device(info); - int node = info->node; - int fb_blank = 0; - - /* If we aren't interested in this event, skip it immediately ... */ - if (event != FB_EVENT_BLANK) - return 0; - - bd = container_of(self, struct backlight_device, fb_notif); - mutex_lock(&bd->ops_lock); + guard(mutex)(&bd->ops_lock); if (!bd->ops) - goto out; - if (bd->ops->controls_device && !bd->ops->controls_device(bd, info->device)) - goto out; - if (fb_bd && fb_bd != bd) - goto out; - - fb_blank = *(int *)evdata->data; - if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) { - bd->fb_bl_on[node] = true; + return; + if (bd->ops->controls_device && !bd->ops->controls_device(bd, display_dev)) + return; + + if (fb_on && (!prev_fb_on || !bd->use_count)) { if (!bd->use_count++) { bd->props.state &= ~BL_CORE_FBBLANK; backlight_update_status(bd); } - } else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) { - bd->fb_bl_on[node] = false; + } else if (!fb_on && prev_fb_on && bd->use_count) { if (!(--bd->use_count)) { bd->props.state |= BL_CORE_FBBLANK; backlight_update_status(bd); } } -out: - mutex_unlock(&bd->ops_lock); - return 0; } +EXPORT_SYMBOL(backlight_notify_blank); -static int backlight_register_fb(struct backlight_device *bd) +void backlight_notify_blank_all(struct device *display_dev, bool fb_on, bool prev_fb_on) { - memset(&bd->fb_notif, 0, sizeof(bd->fb_notif)); - bd->fb_notif.notifier_call = fb_notifier_callback; + struct backlight_device *bd; - return fb_register_client(&bd->fb_notif); -} + guard(mutex)(&backlight_dev_list_mutex); -static void backlight_unregister_fb(struct backlight_device *bd) -{ - fb_unregister_client(&bd->fb_notif); -} -#else -static inline int backlight_register_fb(struct backlight_device *bd) -{ - return 0; + list_for_each_entry(bd, &backlight_dev_list, entry) + backlight_notify_blank(bd, display_dev, fb_on, prev_fb_on); } - -static inline void backlight_unregister_fb(struct backlight_device *bd) -{ -} -#endif /* CONFIG_FB_CORE */ +EXPORT_SYMBOL(backlight_notify_blank_all); static void backlight_generate_event(struct backlight_device *bd, enum backlight_update_reason reason) @@ -416,7 +371,7 @@ struct backlight_device *backlight_device_register(const char *name, pr_debug("backlight_device_register: name=%s\n", name); - new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); + new_bd = kzalloc_obj(struct backlight_device); if (!new_bd) return ERR_PTR(-ENOMEM); @@ -447,12 +402,6 @@ struct backlight_device *backlight_device_register(const char *name, return ERR_PTR(rc); } - rc = backlight_register_fb(new_bd); - if (rc) { - device_unregister(&new_bd->dev); - return ERR_PTR(rc); - } - new_bd->ops = ops; #ifdef CONFIG_PMAC_BACKLIGHT @@ -539,7 +488,6 @@ void backlight_device_unregister(struct backlight_device *bd) bd->ops = NULL; mutex_unlock(&bd->ops_lock); - backlight_unregister_fb(bd); device_unregister(&bd->dev); } EXPORT_SYMBOL(backlight_device_unregister); 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/cgbc_bl.c b/drivers/video/backlight/cgbc_bl.c new file mode 100644 index 000000000000..ab27e14338a8 --- /dev/null +++ b/drivers/video/backlight/cgbc_bl.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Congatec Board Controller (CGBC) Backlight Driver + * + * This driver provides backlight control for LCD displays connected to + * Congatec boards via the CGBC (Congatec Board Controller). It integrates + * with the Linux backlight subsystem and communicates with hardware through + * the cgbc-core module. + * + * Copyright (C) 2025 Novatron Oy + * + * Author: Petri Karhula <petri.karhula@novatron.fi> + */ + +#include <linux/backlight.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/mfd/cgbc.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define BLT_PWM_DUTY_MASK GENMASK(6, 0) + +/* CGBC command for PWM brightness control*/ +#define CGBC_CMD_BLT0_PWM 0x75 + +#define CGBC_BL_MAX_BRIGHTNESS 100 + +/** + * CGBC backlight driver data + * @dev: Pointer to the platform device + * @cgbc: Pointer to the parent CGBC device data + * @current_brightness: Current brightness level (0-100) + */ +struct cgbc_bl_data { + struct device *dev; + struct cgbc_device_data *cgbc; + unsigned int current_brightness; +}; + +static int cgbc_bl_read_brightness(struct cgbc_bl_data *bl_data) +{ + u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM }; + u8 reply_buf[3]; + int ret; + + ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), + reply_buf, sizeof(reply_buf), NULL); + if (ret < 0) + return ret; + + /* + * Get only PWM duty factor percentage, + * ignore polarity inversion bit (bit 7) + */ + bl_data->current_brightness = FIELD_GET(BLT_PWM_DUTY_MASK, reply_buf[0]); + + return 0; +} + +static int cgbc_bl_update_status(struct backlight_device *bl) +{ + struct cgbc_bl_data *bl_data = bl_get_data(bl); + u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM }; + u8 reply_buf[3]; + u8 brightness; + int ret; + + brightness = backlight_get_brightness(bl); + + if (brightness != bl_data->current_brightness) { + /* Read the current values */ + ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), reply_buf, + sizeof(reply_buf), NULL); + if (ret < 0) { + dev_err(bl_data->dev, "Failed to read PWM settings: %d\n", ret); + return ret; + } + + /* + * Prepare command buffer for writing new settings. Only 2nd byte is changed + * to set new brightness (PWM duty cycle %). Other values (polarity, frequency) + * are preserved from the read values. + */ + cmd_buf[1] = (reply_buf[0] & ~BLT_PWM_DUTY_MASK) | + FIELD_PREP(BLT_PWM_DUTY_MASK, brightness); + cmd_buf[2] = reply_buf[1]; + cmd_buf[3] = reply_buf[2]; + + ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), reply_buf, + sizeof(reply_buf), NULL); + if (ret < 0) { + dev_err(bl_data->dev, "Failed to set brightness: %d\n", ret); + return ret; + } + + bl_data->current_brightness = reply_buf[0] & BLT_PWM_DUTY_MASK; + + /* Verify the setting was applied correctly */ + if (bl_data->current_brightness != brightness) { + dev_err(bl_data->dev, + "Brightness setting verification failed (got %u, expected %u)\n", + bl_data->current_brightness, (unsigned int)brightness); + return -EIO; + } + } + + return 0; +} + +static int cgbc_bl_get_brightness(struct backlight_device *bl) +{ + struct cgbc_bl_data *bl_data = bl_get_data(bl); + int ret; + + ret = cgbc_bl_read_brightness(bl_data); + if (ret < 0) { + dev_err(bl_data->dev, "Failed to read brightness: %d\n", ret); + return ret; + } + + return bl_data->current_brightness; +} + +static const struct backlight_ops cgbc_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = cgbc_bl_update_status, + .get_brightness = cgbc_bl_get_brightness, +}; + +static int cgbc_bl_probe(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); + struct backlight_properties props = { }; + struct backlight_device *bl_dev; + struct cgbc_bl_data *bl_data; + int ret; + + bl_data = devm_kzalloc(&pdev->dev, sizeof(*bl_data), GFP_KERNEL); + if (!bl_data) + return -ENOMEM; + + bl_data->dev = &pdev->dev; + bl_data->cgbc = cgbc; + + ret = cgbc_bl_read_brightness(bl_data); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to read initial brightness\n"); + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = CGBC_BL_MAX_BRIGHTNESS; + props.brightness = bl_data->current_brightness; + props.scale = BACKLIGHT_SCALE_LINEAR; + + bl_dev = devm_backlight_device_register(&pdev->dev, "cgbc-backlight", + &pdev->dev, bl_data, + &cgbc_bl_ops, &props); + if (IS_ERR(bl_dev)) + return dev_err_probe(&pdev->dev, PTR_ERR(bl_dev), + "Failed to register backlight device\n"); + + platform_set_drvdata(pdev, bl_data); + + return 0; +} + +static struct platform_driver cgbc_bl_driver = { + .driver = { + .name = "cgbc-backlight", + }, + .probe = cgbc_bl_probe, +}; + +module_platform_driver(cgbc_bl_driver); + +MODULE_AUTHOR("Petri Karhula <petri.karhula@novatron.fi>"); +MODULE_DESCRIPTION("Congatec Board Controller (CGBC) Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cgbc-backlight"); 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..2493138febfa 100644 --- a/drivers/video/backlight/da9052_bl.c +++ b/drivers/video/backlight/da9052_bl.c @@ -9,7 +9,7 @@ #include <linux/backlight.h> #include <linux/delay.h> -#include <linux/fb.h> +#include <linux/mod_devicetable.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/jornada720_bl.c b/drivers/video/backlight/jornada720_bl.c index e28d2c071798..bbb65fdaddc7 100644 --- a/drivers/video/backlight/jornada720_bl.c +++ b/drivers/video/backlight/jornada720_bl.c @@ -7,6 +7,7 @@ #include <linux/backlight.h> #include <linux/device.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/video/backlight/ktd2801-backlight.c b/drivers/video/backlight/ktd2801-backlight.c index 0489b0615ceb..17eac1b3bce4 100644 --- a/drivers/video/backlight/ktd2801-backlight.c +++ b/drivers/video/backlight/ktd2801-backlight.c @@ -6,6 +6,7 @@ #include <linux/backlight.h> #include <linux/gpio/consumer.h> #include <linux/leds-expresswire.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/property.h> diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 3267acf8dc5b..e918f57608ef 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -15,86 +15,59 @@ #include <linux/notifier.h> #include <linux/ctype.h> #include <linux/err.h> -#include <linux/fb.h> #include <linux/slab.h> -#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ - defined(CONFIG_LCD_CLASS_DEVICE_MODULE)) -static int to_lcd_power(int fb_blank) -{ - switch (fb_blank) { - case FB_BLANK_UNBLANK: - return LCD_POWER_ON; - /* deprecated; TODO: should become 'off' */ - case FB_BLANK_NORMAL: - return LCD_POWER_REDUCED; - case FB_BLANK_VSYNC_SUSPEND: - return LCD_POWER_REDUCED_VSYNC_SUSPEND; - /* 'off' */ - case FB_BLANK_HSYNC_SUSPEND: - case FB_BLANK_POWERDOWN: - default: - return LCD_POWER_OFF; - } -} +static DEFINE_MUTEX(lcd_dev_list_mutex); +static LIST_HEAD(lcd_dev_list); -/* This callback gets called when something important happens inside a - * framebuffer driver. We're looking if that important event is blanking, - * and if it is, we're switching lcd power as well ... - */ -static int fb_notifier_callback(struct notifier_block *self, - unsigned long event, void *data) +static void lcd_notify_blank(struct lcd_device *ld, struct device *display_dev, + int power) { - struct lcd_device *ld = container_of(self, struct lcd_device, fb_notif); - struct fb_event *evdata = data; - struct fb_info *info = evdata->info; - struct lcd_device *fb_lcd = fb_lcd_device(info); - guard(mutex)(&ld->ops_lock); - if (!ld->ops) - return 0; - if (ld->ops->controls_device && !ld->ops->controls_device(ld, info->device)) - return 0; - if (fb_lcd && fb_lcd != ld) - return 0; + if (!ld->ops || !ld->ops->set_power) + return; + if (ld->ops->controls_device && !ld->ops->controls_device(ld, display_dev)) + return; - if (event == FB_EVENT_BLANK) { - int power = to_lcd_power(*(int *)evdata->data); + ld->ops->set_power(ld, power); +} - if (ld->ops->set_power) - ld->ops->set_power(ld, power); - } else { - const struct fb_videomode *videomode = evdata->data; +void lcd_notify_blank_all(struct device *display_dev, int power) +{ + struct lcd_device *ld; - if (ld->ops->set_mode) - ld->ops->set_mode(ld, videomode->xres, videomode->yres); - } + guard(mutex)(&lcd_dev_list_mutex); - return 0; + list_for_each_entry(ld, &lcd_dev_list, entry) + lcd_notify_blank(ld, display_dev, power); } +EXPORT_SYMBOL(lcd_notify_blank_all); -static int lcd_register_fb(struct lcd_device *ld) +static void lcd_notify_mode_change(struct lcd_device *ld, struct device *display_dev, + unsigned int width, unsigned int height) { - memset(&ld->fb_notif, 0, sizeof(ld->fb_notif)); - ld->fb_notif.notifier_call = fb_notifier_callback; - return fb_register_client(&ld->fb_notif); -} + guard(mutex)(&ld->ops_lock); -static void lcd_unregister_fb(struct lcd_device *ld) -{ - fb_unregister_client(&ld->fb_notif); -} -#else -static int lcd_register_fb(struct lcd_device *ld) -{ - return 0; + if (!ld->ops || !ld->ops->set_mode) + return; + if (ld->ops->controls_device && !ld->ops->controls_device(ld, display_dev)) + return; + + ld->ops->set_mode(ld, width, height); } -static inline void lcd_unregister_fb(struct lcd_device *ld) +void lcd_notify_mode_change_all(struct device *display_dev, + unsigned int width, unsigned int height) { + struct lcd_device *ld; + + guard(mutex)(&lcd_dev_list_mutex); + + list_for_each_entry(ld, &lcd_dev_list, entry) + lcd_notify_mode_change(ld, display_dev, width, height); } -#endif /* CONFIG_FB */ +EXPORT_SYMBOL(lcd_notify_mode_change_all); static ssize_t lcd_power_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -224,7 +197,7 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, pr_debug("lcd_device_register: name=%s\n", name); - new_ld = kzalloc(sizeof(struct lcd_device), GFP_KERNEL); + new_ld = kzalloc_obj(struct lcd_device); if (!new_ld) return ERR_PTR(-ENOMEM); @@ -245,11 +218,8 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, return ERR_PTR(rc); } - rc = lcd_register_fb(new_ld); - if (rc) { - device_unregister(&new_ld->dev); - return ERR_PTR(rc); - } + guard(mutex)(&lcd_dev_list_mutex); + list_add(&new_ld->entry, &lcd_dev_list); return new_ld; } @@ -266,10 +236,12 @@ void lcd_device_unregister(struct lcd_device *ld) if (!ld) return; + guard(mutex)(&lcd_dev_list_mutex); + list_del(&ld->entry); + mutex_lock(&ld->ops_lock); ld->ops = NULL; mutex_unlock(&ld->ops_lock); - lcd_unregister_fb(ld); device_unregister(&ld->dev); } diff --git a/drivers/video/backlight/led_bl.c b/drivers/video/backlight/led_bl.c index ae34d1ecbfbe..f7ab9b360731 100644 --- a/drivers/video/backlight/led_bl.c +++ b/drivers/video/backlight/led_bl.c @@ -9,6 +9,7 @@ #include <linux/backlight.h> #include <linux/leds.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> struct led_bl_data { @@ -89,7 +90,7 @@ static int led_bl_get_leds(struct device *dev, return -EINVAL; } - leds = devm_kzalloc(dev, sizeof(struct led_classdev *) * nb_leds, + leds = devm_kcalloc(dev, nb_leds, sizeof(struct led_classdev *), GFP_KERNEL); if (!leds) return -ENOMEM; @@ -137,7 +138,7 @@ static int led_bl_parse_levels(struct device *dev, unsigned int db; u32 *levels = NULL; - levels = devm_kzalloc(dev, sizeof(u32) * num_levels, + levels = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL); if (!levels) return -ENOMEM; @@ -210,6 +211,19 @@ static int led_bl_probe(struct platform_device *pdev) } for (i = 0; i < priv->nb_leds; i++) { + struct device_link *link; + + link = device_link_add(&pdev->dev, priv->leds[i]->dev->parent, + DL_FLAG_AUTOREMOVE_CONSUMER); + if (!link) { + dev_err(&pdev->dev, "Failed to add devlink (consumer %s, supplier %s)\n", + dev_name(&pdev->dev), dev_name(priv->leds[i]->dev->parent)); + backlight_device_unregister(priv->bl_dev); + return -EINVAL; + } + } + + for (i = 0; i < priv->nb_leds; i++) { mutex_lock(&priv->leds[i]->led_access); led_sysfs_disable(priv->leds[i]); mutex_unlock(&priv->leds[i]->led_access); @@ -229,8 +243,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/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 7075bfab59c4..d191560ce285 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -22,7 +22,7 @@ #define LP855X_DEVICE_CTRL 0x01 #define LP855X_EEPROM_START 0xA0 #define LP855X_EEPROM_END 0xA7 -#define LP8556_EPROM_START 0xA0 +#define LP8556_EPROM_START 0x98 #define LP8556_EPROM_END 0xAF /* LP8555/7 Registers */ 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/mp3309c.c b/drivers/video/backlight/mp3309c.c index 372058e26129..9337110ce6e5 100644 --- a/drivers/video/backlight/mp3309c.c +++ b/drivers/video/backlight/mp3309c.c @@ -222,7 +222,6 @@ static int mp3309c_parse_fwnode(struct mp3309c_chip *chip, if (IS_ERR(chip->pwmd)) return dev_err_probe(dev, PTR_ERR(chip->pwmd), "error getting pwm data\n"); pdata->dimming_mode = DIMMING_PWM; - pwm_apply_args(chip->pwmd); } /* @@ -353,12 +352,13 @@ static int mp3309c_probe(struct i2c_client *client) chip->pdata = pdata; /* Backlight properties */ - memset(&props, 0, sizeof(struct backlight_properties)); - props.brightness = pdata->default_brightness; - props.max_brightness = pdata->max_brightness; - props.scale = BACKLIGHT_SCALE_LINEAR; - props.type = BACKLIGHT_RAW; - props.power = BACKLIGHT_POWER_ON; + props = (typeof(props)){ + .brightness = pdata->default_brightness, + .max_brightness = pdata->max_brightness, + .scale = BACKLIGHT_SCALE_LINEAR, + .type = BACKLIGHT_RAW, + .power = BACKLIGHT_POWER_ON, + }; chip->bl = devm_backlight_device_register(dev, "mp3309c", dev, chip, &mp3309c_bl_ops, &props); if (IS_ERR(chip->bl)) 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/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 9afe701b2a1b..8054e4787725 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -1244,6 +1244,15 @@ static const struct wled_var_cfg wled4_ovp_cfg = { .size = ARRAY_SIZE(wled4_ovp_values), }; +static const u32 pmi8994_wled_ovp_values[] = { + 31000, 29500, 19400, 17800, +}; + +static const struct wled_var_cfg pmi8994_wled_ovp_cfg = { + .values = pmi8994_wled_ovp_values, + .size = ARRAY_SIZE(pmi8994_wled_ovp_values), +}; + static inline u32 wled5_ovp_values_fn(u32 idx) { /* @@ -1357,6 +1366,29 @@ static int wled_configure(struct wled *wled) }, }; + const struct wled_u32_opts pmi8994_wled_opts[] = { + { + .name = "qcom,current-boost-limit", + .val_ptr = &cfg->boost_i_limit, + .cfg = &wled4_boost_i_limit_cfg, + }, + { + .name = "qcom,current-limit-microamp", + .val_ptr = &cfg->string_i_limit, + .cfg = &wled4_string_i_limit_cfg, + }, + { + .name = "qcom,ovp-millivolt", + .val_ptr = &cfg->ovp, + .cfg = &pmi8994_wled_ovp_cfg, + }, + { + .name = "qcom,switching-freq", + .val_ptr = &cfg->switch_freq, + .cfg = &wled3_switch_freq_cfg, + }, + }; + const struct wled_u32_opts wled5_opts[] = { { .name = "qcom,current-boost-limit", @@ -1406,9 +1438,11 @@ static int wled_configure(struct wled *wled) wled->ctrl_addr = be32_to_cpu(*prop_addr); rc = of_property_read_string(dev->of_node, "label", &wled->name); - if (rc) + if (rc) { wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - + if (!wled->name) + return -ENOMEM; + } switch (wled->version) { case 3: u32_opts = wled3_opts; @@ -1421,8 +1455,14 @@ static int wled_configure(struct wled *wled) break; case 4: - u32_opts = wled4_opts; - size = ARRAY_SIZE(wled4_opts); + if (of_device_is_compatible(dev->of_node, "qcom,pmi8950-wled") || + of_device_is_compatible(dev->of_node, "qcom,pmi8994-wled")) { + u32_opts = pmi8994_wled_opts; + size = ARRAY_SIZE(pmi8994_wled_opts); + } else { + u32_opts = wled4_opts; + size = ARRAY_SIZE(wled4_opts); + } *cfg = wled4_config_defaults; wled->wled_set_brightness = wled4_set_brightness; wled->wled_sync_toggle = wled3_sync_toggle; diff --git a/drivers/video/backlight/rave-sp-backlight.c b/drivers/video/backlight/rave-sp-backlight.c index e708a060a6e4..bfe01b9b9174 100644 --- a/drivers/video/backlight/rave-sp-backlight.c +++ b/drivers/video/backlight/rave-sp-backlight.c @@ -9,8 +9,10 @@ #include <linux/backlight.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mfd/rave-sp.h> +#include <linux/of.h> #include <linux/platform_device.h> #define RAVE_SP_BACKLIGHT_LCD_EN BIT(7) diff --git a/drivers/video/backlight/rt4831-backlight.c b/drivers/video/backlight/rt4831-backlight.c index 7ead75929a43..26214519bfce 100644 --- a/drivers/video/backlight/rt4831-backlight.c +++ b/drivers/video/backlight/rt4831-backlight.c @@ -4,6 +4,7 @@ #include <linux/backlight.h> #include <linux/bitops.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/property.h> diff --git a/drivers/video/backlight/sky81452-backlight.c b/drivers/video/backlight/sky81452-backlight.c index 2749231f0385..b2679b24de14 100644 --- a/drivers/video/backlight/sky81452-backlight.c +++ b/drivers/video/backlight/sky81452-backlight.c @@ -202,6 +202,9 @@ static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode"); pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift"); pdata->gpiod_enable = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(pdata->gpiod_enable)) + return dev_err_cast_probe(dev, pdata->gpiod_enable, + "failed to get gpio\n"); ret = of_property_count_u32_elems(np, "led-sources"); if (ret < 0) { 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/console/dummycon.c b/drivers/video/console/dummycon.c index 139049368fdc..7d02470f19b9 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -85,6 +85,15 @@ static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, /* Redraw, so that we get putc(s) for output done while blanked */ return true; } + +static bool dummycon_switch(struct vc_data *vc) +{ + /* + * Redraw, so that we get putc(s) for output done while switched + * away. Informs deferred consoles to take over the display. + */ + return true; +} #else static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, unsigned int x) { } @@ -95,6 +104,10 @@ static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, { return false; } +static bool dummycon_switch(struct vc_data *vc) +{ + return false; +} #endif static const char *dummycon_startup(void) @@ -124,11 +137,6 @@ static bool dummycon_scroll(struct vc_data *vc, unsigned int top, return false; } -static bool dummycon_switch(struct vc_data *vc) -{ - return false; -} - /* * The console `switch' structure for the dummy console * diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 242415366074..e88ff3a93b77 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -33,9 +33,9 @@ #define NEWPORT_LEN 0x10000 -#define FONT_DATA ((unsigned char *)font_vga_8x16.data) +#define FONT_DATA font_vga_8x16.data -static unsigned char *font_data[MAX_NR_CONSOLES]; +static font_data_t *font_data[MAX_NR_CONSOLES]; static struct newport_regs *npregs; static unsigned long newport_addr; @@ -95,7 +95,7 @@ static inline void newport_init_cmap(void) static const struct linux_logo *newport_show_logo(void) { -#ifdef CONFIG_LOGO_SGI_CLUT224 +#ifdef CONFIG_LOGO_LINUX_CLUT224 const struct linux_logo *logo = fb_find_logo(8); const unsigned char *clut; const unsigned char *data; @@ -127,7 +127,7 @@ static const struct linux_logo *newport_show_logo(void) return logo; #else return NULL; -#endif /* CONFIG_LOGO_SGI_CLUT224 */ +#endif /* CONFIG_LOGO_LINUX_CLUT224 */ } static inline void newport_clear_screen(int xstart, int ystart, int xend, @@ -370,9 +370,9 @@ static void newport_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, static void newport_putc(struct vc_data *vc, u16 charattr, unsigned int ypos, unsigned int xpos) { - unsigned char *p; + const unsigned char *p; - p = &font_data[vc->vc_num][(charattr & 0xff) << 4]; + p = &font_data_buf(font_data[vc->vc_num])[(charattr & 0xff) << 4]; charattr = (charattr >> 8) & 0xff; xpos <<= 3; ypos <<= 4; @@ -400,7 +400,7 @@ static void newport_putcs(struct vc_data *vc, const u16 *s, unsigned int count, unsigned int ypos, unsigned int xpos) { - unsigned char *p; + const unsigned char *p; unsigned int i; u16 charattr; @@ -424,7 +424,7 @@ static void newport_putcs(struct vc_data *vc, const u16 *s, NPORT_DMODE0_L32); for (i = 0; i < count; i++, xpos += 8) { - p = &font_data[vc->vc_num][(scr_readw(s++) & 0xff) << 4]; + p = &font_data_buf(font_data[vc->vc_num])[(scr_readw(s++) & 0xff) << 4]; newport_wait(npregs); @@ -501,52 +501,32 @@ static int newport_set_font(int unit, const struct console_font *op, { int w = op->width; int h = op->height; - int size = h * op->charcount; int i; - unsigned char *new_data, *data = op->data, *p; + font_data_t *new_data; /* ladis: when I grow up, there will be a day... and more sizes will * be supported ;-) */ - if ((w != 8) || (h != 16) || (vpitch != 32) - || (op->charcount != 256 && op->charcount != 512)) + if (w != 8 || h != 16 || (op->charcount != 256 && op->charcount != 512)) return -EINVAL; - if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, - GFP_USER))) return -ENOMEM; - - new_data += FONT_EXTRA_WORDS * sizeof(int); - FNTSIZE(new_data) = size; - FNTCHARCNT(new_data) = op->charcount; - REFCOUNT(new_data) = 0; /* usage counter */ - FNTSUM(new_data) = 0; - - p = new_data; - for (i = 0; i < op->charcount; i++) { - memcpy(p, data, h); - data += 32; - p += h; - } + new_data = font_data_import(op, vpitch, NULL); + if (IS_ERR(new_data)) + return PTR_ERR(new_data); /* check if font is already used by other console */ for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (font_data[i] != FONT_DATA - && FNTSIZE(font_data[i]) == size - && !memcmp(font_data[i], new_data, size)) { - kfree(new_data - FONT_EXTRA_WORDS * sizeof(int)); + if (font_data_is_equal(font_data[i], new_data)) { + font_data_put(new_data); /* current font is the same as the new one */ if (i == unit) return 0; new_data = font_data[i]; + font_data_get(new_data); break; } } - /* old font is user font */ - if (font_data[unit] != FONT_DATA) { - if (--REFCOUNT(font_data[unit]) == 0) - kfree(font_data[unit] - - FONT_EXTRA_WORDS * sizeof(int)); - } - REFCOUNT(new_data)++; + + font_data_put(font_data[unit]); font_data[unit] = new_data; return 0; @@ -554,12 +534,9 @@ static int newport_set_font(int unit, const struct console_font *op, static int newport_set_def_font(int unit, struct console_font *op) { - if (font_data[unit] != FONT_DATA) { - if (--REFCOUNT(font_data[unit]) == 0) - kfree(font_data[unit] - - FONT_EXTRA_WORDS * sizeof(int)); - font_data[unit] = FONT_DATA; - } + font_data_put(font_data[unit]); + font_data[unit] = FONT_DATA; + font_data_get(font_data[unit]); return 0; } diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index f1f3ee8e5e8a..6f58e6a9e76a 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -186,7 +186,7 @@ static int sticon_set_font(struct vc_data *vc, const struct console_font *op, new_font->underline_height = 0; new_font->underline_pos = 0; - cooked_font = kzalloc(sizeof(*cooked_font), GFP_KERNEL); + cooked_font = kzalloc_obj(*cooked_font); if (!cooked_font) { kfree(new_font); return -ENOMEM; diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 55c6686f091e..1c73d560f196 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -126,9 +126,9 @@ config FB_ACORN config FB_CLPS711X tristate "CLPS711X LCD support" depends on FB && (ARCH_CLPS711X || COMPILE_TEST) + depends on LCD_CLASS_DEVICE select FB_IOMEM_HELPERS select FB_MODE_HELPERS - select LCD_CLASS_DEVICE select VIDEOMODE_HELPERS help Say Y to enable the Framebuffer driver for the Cirrus Logic @@ -150,7 +150,7 @@ config FB_IMX tristate "Freescale i.MX1/21/25/27 LCD support" depends on FB && HAVE_CLK && HAS_IOMEM depends on ARCH_MXC || COMPILE_TEST - select LCD_CLASS_DEVICE + depends on LCD_CLASS_DEVICE select FB_IOMEM_HELPERS select FB_MODE_HELPERS select VIDEOMODE_HELPERS @@ -660,7 +660,7 @@ config FB_ATMEL config FB_NVIDIA tristate "nVidia Framebuffer Support" - depends on FB && PCI + depends on FB && PCI && HAS_IOPORT select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -668,10 +668,12 @@ config FB_NVIDIA select BITREVERSE select VGASTATE help - This driver supports graphics boards with the nVidia chips, TNT - and newer. For very old chipsets, such as the RIVA128, then use - the rivafb. - Say Y if you have such a graphics board. + This driver supports graphics boards with the nVidia chips, from TNT + through early GeForce generations (NV4–NV2x: Twintor, Twintor2, Celsius, + Kelvin). + Later architectures (Rankine and newer) are not reliably supported. + For very old chipsets, such as the RIVA128, use rivafb. + If unsure, say N. To compile this driver as a module, choose M here: the module will be called nvidiafb. @@ -816,11 +818,11 @@ config FB_I810_I2C config FB_MATROX tristate "Matrox acceleration" depends on FB && PCI + depends on FB_TILEBLITTING select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select FB_TILEBLITTING select FB_MACMODES if PPC_PMAC help Say Y here if you have a Matrox Millennium, Matrox Millennium II, @@ -948,9 +950,6 @@ config FB_RADEON a framebuffer device. There are both PCI and AGP versions. You don't need to choose this to run the Radeon in plain VGA mode. - There is a product page at - https://products.amd.com/en-us/GraphicCardResult.aspx - config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" depends on FB_RADEON @@ -1053,13 +1052,14 @@ config FB_ATY_BACKLIGHT config FB_S3 tristate "S3 Trio/Virge support" depends on FB && PCI && HAS_IOPORT + depends on FB_TILEBLITTING select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select FB_TILEBLITTING select FB_SVGALIB select VGASTATE + select FB_CFB_REV_PIXELS_IN_BYTE select FONT_8x16 if FRAMEBUFFER_CONSOLE help Driver for graphics boards with S3 Trio / S3 Virge chip. @@ -1258,11 +1258,11 @@ config FB_VOODOO1 config FB_VT8623 tristate "VIA VT8623 support" depends on FB && PCI && HAS_IOPORT + depends on FB_TILEBLITTING select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select FB_TILEBLITTING select FB_SVGALIB select VGASTATE select FONT_8x16 if FRAMEBUFFER_CONSOLE @@ -1296,11 +1296,11 @@ config FB_TRIDENT config FB_ARK tristate "ARK 2000PV support" depends on FB && PCI && HAS_IOPORT + depends on FB_TILEBLITTING select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_IOMEM_FOPS - select FB_TILEBLITTING select FB_SVGALIB select VGASTATE select FONT_8x16 if FRAMEBUFFER_CONSOLE @@ -1347,7 +1347,8 @@ endchoice config FB_AU1100 bool "Au1100 LCD Driver" - depends on (FB = y) && MIPS_ALCHEMY + depends on FB = y + depends on MIPS_ALCHEMY || COMPILE_TEST select FB_IOMEM_HELPERS help This is the framebuffer driver for the AMD Au1100 SOC. It can drive @@ -1772,14 +1773,6 @@ config FB_BROADSHEET and could also have been called by other names when coupled with a bridge adapter. -config FB_HYPERV - tristate "Microsoft Hyper-V Synthetic Video support" - depends on FB && HYPERV - select DMA_CMA if HAVE_DMA_CONTIGUOUS && CMA - select FB_IOMEM_HELPERS_DEFERRED - help - This framebuffer driver supports Microsoft Hyper-V Synthetic Video. - config FB_SIMPLE tristate "Simple framebuffer support" depends on FB diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index b3d12f977c06..36a18d958ba0 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -111,7 +111,6 @@ obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ -obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o obj-$(CONFIG_FB_OPENCORES) += ocfb.o obj-$(CONFIG_FB_SM712) += sm712fb.o diff --git a/drivers/video/fbdev/arkfb.c b/drivers/video/fbdev/arkfb.c index 082501feceb9..866c1165704e 100644 --- a/drivers/video/fbdev/arkfb.c +++ b/drivers/video/fbdev/arkfb.c @@ -431,9 +431,10 @@ static struct dac_ops ics5342_ops = { static struct dac_info * ics5342_init(dac_read_regs_t drr, dac_write_regs_t dwr, void *data) { - struct dac_info *info = kzalloc(sizeof(struct ics5342_info), GFP_KERNEL); + struct ics5342_info *ics_info = kzalloc_obj(struct ics5342_info); + struct dac_info *info = &ics_info->dac; - if (! info) + if (!ics_info) return NULL; info->dacops = &ics5342_ops; diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 210fd3ac18a4..9fc5af09f86c 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -2324,8 +2324,6 @@ static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk) * Initialisation */ -static struct fb_info *fb_list = NULL; - #if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) static int atyfb_get_timings_from_lcd(struct atyfb_par *par, struct fb_var_screeninfo *var) @@ -2614,8 +2612,12 @@ static int aty_init(struct fb_info *info) pr_cont("\n"); } #endif - if (par->pll_ops->init_pll) - par->pll_ops->init_pll(info, &par->pll); + if (par->pll_ops->init_pll) { + ret = par->pll_ops->init_pll(info, &par->pll); + if (ret) + return ret; + } + if (par->pll_ops->resume_pll) par->pll_ops->resume_pll(info, &par->pll); @@ -2754,8 +2756,6 @@ static int aty_init(struct fb_info *info) #endif } - fb_list = info; - PRINTKI("fb%d: %s frame buffer device on %s\n", info->node, info->fix.id, par->bus_type == ISA ? "ISA" : "PCI"); return 0; @@ -2972,7 +2972,7 @@ static int atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info, /* nothing */ ; j = i + 4; - par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC); + par->mmap_map = kzalloc_objs(*par->mmap_map, j, GFP_ATOMIC); if (!par->mmap_map) { PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n"); return -ENOMEM; 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/aty/radeon_backlight.c b/drivers/video/fbdev/aty/radeon_backlight.c index 9e41d2a18649..32d7f32db46a 100644 --- a/drivers/video/fbdev/aty/radeon_backlight.c +++ b/drivers/video/fbdev/aty/radeon_backlight.c @@ -59,7 +59,7 @@ static int radeon_bl_update_status(struct backlight_device *bd) */ level = backlight_get_brightness(bd); - del_timer_sync(&rinfo->lvds_timer); + timer_delete_sync(&rinfo->lvds_timer); radeon_engine_idle(); lvds_gen_cntl = INREG(LVDS_GEN_CNTL); @@ -136,7 +136,7 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo) return; #endif - pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL); + pdata = kmalloc_obj(struct radeon_bl_privdata); if (!pdata) { printk("radeonfb: Memory allocation failed\n"); goto error; diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index d866608da8d1..cb006484831b 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -1082,7 +1082,7 @@ int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch) } break; case MT_LCD: - del_timer_sync(&rinfo->lvds_timer); + timer_delete_sync(&rinfo->lvds_timer); val = INREG(LVDS_GEN_CNTL); if (unblank) { u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON @@ -1443,7 +1443,7 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg */ static void radeon_lvds_timer_func(struct timer_list *t) { - struct radeonfb_info *rinfo = from_timer(rinfo, t, lvds_timer); + struct radeonfb_info *rinfo = timer_container_of(rinfo, t, lvds_timer); radeon_engine_idle(); @@ -1653,7 +1653,7 @@ static int radeonfb_set_par(struct fb_info *info) int depth = var_to_depth(mode); int use_rmx = 0; - newmode = kmalloc(sizeof(struct radeon_regs), GFP_KERNEL); + newmode = kmalloc_obj(struct radeon_regs); if (!newmode) return -ENOMEM; @@ -2227,7 +2227,7 @@ static const struct bin_attribute edid1_attr = { .mode = 0444, }, .size = EDID_LENGTH, - .read_new = radeon_show_edid1, + .read = radeon_show_edid1, }; static const struct bin_attribute edid2_attr = { @@ -2236,7 +2236,7 @@ static const struct bin_attribute edid2_attr = { .mode = 0444, }, .size = EDID_LENGTH, - .read_new = radeon_show_edid2, + .read = radeon_show_edid2, }; static int radeonfb_pci_register(struct pci_dev *pdev, @@ -2516,7 +2516,7 @@ static void radeonfb_pci_unregister(struct pci_dev *pdev) if (rinfo->mon2_EDID) sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr); - del_timer_sync(&rinfo->lvds_timer); + timer_delete_sync(&rinfo->lvds_timer); arch_phys_wc_del(rinfo->wc_cookie); radeonfb_bl_exit(rinfo); unregister_framebuffer(info); diff --git a/drivers/video/fbdev/aty/radeon_monitor.c b/drivers/video/fbdev/aty/radeon_monitor.c index df55e23b7a5a..621d13a1a1d9 100644 --- a/drivers/video/fbdev/aty/radeon_monitor.c +++ b/drivers/video/fbdev/aty/radeon_monitor.c @@ -654,7 +654,7 @@ static void radeon_fixup_panel_info(struct radeonfb_info *rinfo) { #ifdef CONFIG_PPC /* - * LCD Flat panels should use fixed dividers, we enfore that on + * LCD Flat panels should use fixed dividers, we enforce that on * PPC only for now... */ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c index 97a5972f5b1f..5ff4a964055a 100644 --- a/drivers/video/fbdev/aty/radeon_pm.c +++ b/drivers/video/fbdev/aty/radeon_pm.c @@ -2650,7 +2650,7 @@ static int radeonfb_pci_suspend_late(struct device *dev, pm_message_t mesg) /* Sleep */ rinfo->asleep = 1; rinfo->lock_blank = 1; - del_timer_sync(&rinfo->lvds_timer); + timer_delete_sync(&rinfo->lvds_timer); #ifdef CONFIG_PPC_PMAC /* On powermac, we have hooks to properly suspend/resume AGP now, diff --git a/drivers/video/fbdev/au1100fb.c b/drivers/video/fbdev/au1100fb.c index 840f22160763..c54cfcd832bb 100644 --- a/drivers/video/fbdev/au1100fb.c +++ b/drivers/video/fbdev/au1100fb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * BRIEF MODULE DESCRIPTION * Au1100 LCD Driver. @@ -20,28 +21,13 @@ * Based on: * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device * Created 28 Dec 1997 by Geert Uytterhoeven - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define pr_fmt(fmt) "au1100fb:" fmt "\n" + #include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -55,11 +41,353 @@ #include <linux/platform_device.h> #include <linux/slab.h> -#include <asm/mach-au1x00/au1000.h> +#if defined(__BIG_ENDIAN) +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 +#else +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 +#endif +#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 + +/********************************************************************/ + +/* LCD controller restrictions */ +#define AU1100_LCD_MAX_XRES 800 +#define AU1100_LCD_MAX_YRES 600 +#define AU1100_LCD_MAX_BPP 16 +#define AU1100_LCD_MAX_CLK 48000000 +#define AU1100_LCD_NBR_PALETTE_ENTRIES 256 + +/* Default number of visible screen buffer to allocate */ +#define AU1100FB_NBR_VIDEO_BUFFERS 4 + +/********************************************************************/ + +struct au1100fb_panel +{ + const char name[25]; /* Full name <vendor>_<model> */ + + u32 control_base; /* Mode-independent control values */ + u32 clkcontrol_base; /* Panel pixclock preferences */ + + u32 horztiming; + u32 verttiming; + + u32 xres; /* Maximum horizontal resolution */ + u32 yres; /* Maximum vertical resolution */ + u32 bpp; /* Maximum depth supported */ +}; + +struct au1100fb_regs +{ + u32 lcd_control; + u32 lcd_intstatus; + u32 lcd_intenable; + u32 lcd_horztiming; + u32 lcd_verttiming; + u32 lcd_clkcontrol; + u32 lcd_dmaaddr0; + u32 lcd_dmaaddr1; + u32 lcd_words; + u32 lcd_pwmdiv; + u32 lcd_pwmhi; + u32 reserved[(0x0400-0x002C)/4]; + u32 lcd_palettebase[256]; +}; + +struct au1100fb_device { + + struct fb_info info; /* FB driver info record */ + + struct au1100fb_panel *panel; /* Panel connected to this device */ + + struct au1100fb_regs* regs; /* Registers memory map */ + size_t regs_len; + unsigned int regs_phys; + +#ifdef CONFIG_PM + /* stores the register values during suspend */ + struct au1100fb_regs pm_regs; +#endif + + unsigned char* fb_mem; /* FrameBuffer memory map */ + size_t fb_len; + dma_addr_t fb_phys; + int panel_idx; + struct clk *lcdclk; + struct device *dev; +}; + +/********************************************************************/ + +#define LCD_CONTROL (AU1100_LCD_BASE + 0x0) + #define LCD_CONTROL_SBB_BIT 21 + #define LCD_CONTROL_SBB_MASK (0x3 << LCD_CONTROL_SBB_BIT) + #define LCD_CONTROL_SBB_1 (0 << LCD_CONTROL_SBB_BIT) + #define LCD_CONTROL_SBB_2 (1 << LCD_CONTROL_SBB_BIT) + #define LCD_CONTROL_SBB_3 (2 << LCD_CONTROL_SBB_BIT) + #define LCD_CONTROL_SBB_4 (3 << LCD_CONTROL_SBB_BIT) + #define LCD_CONTROL_SBPPF_BIT 18 + #define LCD_CONTROL_SBPPF_MASK (0x7 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_SBPPF_655 (0 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_SBPPF_565 (1 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_SBPPF_556 (2 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_SBPPF_1555 (3 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_SBPPF_5551 (4 << LCD_CONTROL_SBPPF_BIT) + #define LCD_CONTROL_WP (1<<17) + #define LCD_CONTROL_WD (1<<16) + #define LCD_CONTROL_C (1<<15) + #define LCD_CONTROL_SM_BIT 13 + #define LCD_CONTROL_SM_MASK (0x3 << LCD_CONTROL_SM_BIT) + #define LCD_CONTROL_SM_0 (0 << LCD_CONTROL_SM_BIT) + #define LCD_CONTROL_SM_90 (1 << LCD_CONTROL_SM_BIT) + #define LCD_CONTROL_SM_180 (2 << LCD_CONTROL_SM_BIT) + #define LCD_CONTROL_SM_270 (3 << LCD_CONTROL_SM_BIT) + #define LCD_CONTROL_DB (1<<12) + #define LCD_CONTROL_CCO (1<<11) + #define LCD_CONTROL_DP (1<<10) + #define LCD_CONTROL_PO_BIT 8 + #define LCD_CONTROL_PO_MASK (0x3 << LCD_CONTROL_PO_BIT) + #define LCD_CONTROL_PO_00 (0 << LCD_CONTROL_PO_BIT) + #define LCD_CONTROL_PO_01 (1 << LCD_CONTROL_PO_BIT) + #define LCD_CONTROL_PO_10 (2 << LCD_CONTROL_PO_BIT) + #define LCD_CONTROL_PO_11 (3 << LCD_CONTROL_PO_BIT) + #define LCD_CONTROL_MPI (1<<7) + #define LCD_CONTROL_PT (1<<6) + #define LCD_CONTROL_PC (1<<5) + #define LCD_CONTROL_BPP_BIT 1 + #define LCD_CONTROL_BPP_MASK (0x7 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_1 (0 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_2 (1 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_4 (2 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_8 (3 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_12 (4 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_BPP_16 (5 << LCD_CONTROL_BPP_BIT) + #define LCD_CONTROL_GO (1<<0) + +#define LCD_INTSTATUS (AU1100_LCD_BASE + 0x4) +#define LCD_INTENABLE (AU1100_LCD_BASE + 0x8) + #define LCD_INT_SD (1<<7) + #define LCD_INT_OF (1<<6) + #define LCD_INT_UF (1<<5) + #define LCD_INT_SA (1<<3) + #define LCD_INT_SS (1<<2) + #define LCD_INT_S1 (1<<1) + #define LCD_INT_S0 (1<<0) + +#define LCD_HORZTIMING (AU1100_LCD_BASE + 0xC) + #define LCD_HORZTIMING_HN2_BIT 24 + #define LCD_HORZTIMING_HN2_MASK (0xFF << LCD_HORZTIMING_HN2_BIT) + #define LCD_HORZTIMING_HN2_N(N) ((((N)-1) << LCD_HORZTIMING_HN2_BIT) & LCD_HORZTIMING_HN2_MASK) + #define LCD_HORZTIMING_HN1_BIT 16 + #define LCD_HORZTIMING_HN1_MASK (0xFF << LCD_HORZTIMING_HN1_BIT) + #define LCD_HORZTIMING_HN1_N(N) ((((N)-1) << LCD_HORZTIMING_HN1_BIT) & LCD_HORZTIMING_HN1_MASK) + #define LCD_HORZTIMING_HPW_BIT 10 + #define LCD_HORZTIMING_HPW_MASK (0x3F << LCD_HORZTIMING_HPW_BIT) + #define LCD_HORZTIMING_HPW_N(N) ((((N)-1) << LCD_HORZTIMING_HPW_BIT) & LCD_HORZTIMING_HPW_MASK) + #define LCD_HORZTIMING_PPL_BIT 0 + #define LCD_HORZTIMING_PPL_MASK (0x3FF << LCD_HORZTIMING_PPL_BIT) + #define LCD_HORZTIMING_PPL_N(N) ((((N)-1) << LCD_HORZTIMING_PPL_BIT) & LCD_HORZTIMING_PPL_MASK) + +#define LCD_VERTTIMING (AU1100_LCD_BASE + 0x10) + #define LCD_VERTTIMING_VN2_BIT 24 + #define LCD_VERTTIMING_VN2_MASK (0xFF << LCD_VERTTIMING_VN2_BIT) + #define LCD_VERTTIMING_VN2_N(N) ((((N)-1) << LCD_VERTTIMING_VN2_BIT) & LCD_VERTTIMING_VN2_MASK) + #define LCD_VERTTIMING_VN1_BIT 16 + #define LCD_VERTTIMING_VN1_MASK (0xFF << LCD_VERTTIMING_VN1_BIT) + #define LCD_VERTTIMING_VN1_N(N) ((((N)-1) << LCD_VERTTIMING_VN1_BIT) & LCD_VERTTIMING_VN1_MASK) + #define LCD_VERTTIMING_VPW_BIT 10 + #define LCD_VERTTIMING_VPW_MASK (0x3F << LCD_VERTTIMING_VPW_BIT) + #define LCD_VERTTIMING_VPW_N(N) ((((N)-1) << LCD_VERTTIMING_VPW_BIT) & LCD_VERTTIMING_VPW_MASK) + #define LCD_VERTTIMING_LPP_BIT 0 + #define LCD_VERTTIMING_LPP_MASK (0x3FF << LCD_VERTTIMING_LPP_BIT) + #define LCD_VERTTIMING_LPP_N(N) ((((N)-1) << LCD_VERTTIMING_LPP_BIT) & LCD_VERTTIMING_LPP_MASK) + +#define LCD_CLKCONTROL (AU1100_LCD_BASE + 0x14) + #define LCD_CLKCONTROL_IB (1<<18) + #define LCD_CLKCONTROL_IC (1<<17) + #define LCD_CLKCONTROL_IH (1<<16) + #define LCD_CLKCONTROL_IV (1<<15) + #define LCD_CLKCONTROL_BF_BIT 10 + #define LCD_CLKCONTROL_BF_MASK (0x1F << LCD_CLKCONTROL_BF_BIT) + #define LCD_CLKCONTROL_BF_N(N) ((((N)-1) << LCD_CLKCONTROL_BF_BIT) & LCD_CLKCONTROL_BF_MASK) + #define LCD_CLKCONTROL_PCD_BIT 0 + #define LCD_CLKCONTROL_PCD_MASK (0x3FF << LCD_CLKCONTROL_PCD_BIT) + #define LCD_CLKCONTROL_PCD_N(N) (((N) << LCD_CLKCONTROL_PCD_BIT) & LCD_CLKCONTROL_PCD_MASK) + +#define LCD_DMAADDR0 (AU1100_LCD_BASE + 0x18) +#define LCD_DMAADDR1 (AU1100_LCD_BASE + 0x1C) + #define LCD_DMA_SA_BIT 5 + #define LCD_DMA_SA_MASK (0x7FFFFFF << LCD_DMA_SA_BIT) + #define LCD_DMA_SA_N(N) ((N) & LCD_DMA_SA_MASK) + +#define LCD_WORDS (AU1100_LCD_BASE + 0x20) + #define LCD_WRD_WRDS_BIT 0 + #define LCD_WRD_WRDS_MASK (0xFFFFFFFF << LCD_WRD_WRDS_BIT) + #define LCD_WRD_WRDS_N(N) ((((N)-1) << LCD_WRD_WRDS_BIT) & LCD_WRD_WRDS_MASK) + +#define LCD_PWMDIV (AU1100_LCD_BASE + 0x24) + #define LCD_PWMDIV_EN (1<<12) + #define LCD_PWMDIV_PWMDIV_BIT 0 + #define LCD_PWMDIV_PWMDIV_MASK (0xFFF << LCD_PWMDIV_PWMDIV_BIT) + #define LCD_PWMDIV_PWMDIV_N(N) ((((N)-1) << LCD_PWMDIV_PWMDIV_BIT) & LCD_PWMDIV_PWMDIV_MASK) + +#define LCD_PWMHI (AU1100_LCD_BASE + 0x28) + #define LCD_PWMHI_PWMHI1_BIT 12 + #define LCD_PWMHI_PWMHI1_MASK (0xFFF << LCD_PWMHI_PWMHI1_BIT) + #define LCD_PWMHI_PWMHI1_N(N) (((N) << LCD_PWMHI_PWMHI1_BIT) & LCD_PWMHI_PWMHI1_MASK) + #define LCD_PWMHI_PWMHI0_BIT 0 + #define LCD_PWMHI_PWMHI0_MASK (0xFFF << LCD_PWMHI_PWMHI0_BIT) + #define LCD_PWMHI_PWMHI0_N(N) (((N) << LCD_PWMHI_PWMHI0_BIT) & LCD_PWMHI_PWMHI0_MASK) + +#define LCD_PALLETTEBASE (AU1100_LCD_BASE + 0x400) + #define LCD_PALLETTE_MONO_MI_BIT 0 + #define LCD_PALLETTE_MONO_MI_MASK (0xF << LCD_PALLETTE_MONO_MI_BIT) + #define LCD_PALLETTE_MONO_MI_N(N) (((N)<< LCD_PALLETTE_MONO_MI_BIT) & LCD_PALLETTE_MONO_MI_MASK) + + #define LCD_PALLETTE_COLOR_RI_BIT 8 + #define LCD_PALLETTE_COLOR_RI_MASK (0xF << LCD_PALLETTE_COLOR_RI_BIT) + #define LCD_PALLETTE_COLOR_RI_N(N) (((N)<< LCD_PALLETTE_COLOR_RI_BIT) & LCD_PALLETTE_COLOR_RI_MASK) + #define LCD_PALLETTE_COLOR_GI_BIT 4 + #define LCD_PALLETTE_COLOR_GI_MASK (0xF << LCD_PALLETTE_COLOR_GI_BIT) + #define LCD_PALLETTE_COLOR_GI_N(N) (((N)<< LCD_PALLETTE_COLOR_GI_BIT) & LCD_PALLETTE_COLOR_GI_MASK) + #define LCD_PALLETTE_COLOR_BI_BIT 0 + #define LCD_PALLETTE_COLOR_BI_MASK (0xF << LCD_PALLETTE_COLOR_BI_BIT) + #define LCD_PALLETTE_COLOR_BI_N(N) (((N)<< LCD_PALLETTE_COLOR_BI_BIT) & LCD_PALLETTE_COLOR_BI_MASK) + + #define LCD_PALLETTE_TFT_DC_BIT 0 + #define LCD_PALLETTE_TFT_DC_MASK (0xFFFF << LCD_PALLETTE_TFT_DC_BIT) + #define LCD_PALLETTE_TFT_DC_N(N) (((N)<< LCD_PALLETTE_TFT_DC_BIT) & LCD_PALLETTE_TFT_DC_MASK) + +/********************************************************************/ + +/* List of panels known to work with the AU1100 LCD controller. + * To add a new panel, enter the same specifications as the + * Generic_TFT one, and MAKE SURE that it doesn't conflicts + * with the controller restrictions. Restrictions are: + * + * STN color panels: max_bpp <= 12 + * STN mono panels: max_bpp <= 4 + * TFT panels: max_bpp <= 16 + * max_xres <= 800 + * max_yres <= 600 + */ +static struct au1100fb_panel known_lcd_panels[] = +{ + /* 800x600x16bpp CRT */ + [0] = { + .name = "CRT_800x600_16", + .xres = 800, + .yres = 600, + .bpp = 16, + .control_base = 0x0004886A | + LCD_CONTROL_DEFAULT_PO | LCD_CONTROL_DEFAULT_SBPPF | + LCD_CONTROL_BPP_16 | LCD_CONTROL_SBB_4, + .clkcontrol_base = 0x00020000, + .horztiming = 0x005aff1f, + .verttiming = 0x16000e57, + }, + /* just the standard LCD */ + [1] = { + .name = "WWPC LCD", + .xres = 240, + .yres = 320, + .bpp = 16, + .control_base = 0x0006806A, + .horztiming = 0x0A1010EF, + .verttiming = 0x0301013F, + .clkcontrol_base = 0x00018001, + }, + /* Sharp 320x240 TFT panel */ + [2] = { + .name = "Sharp_LQ038Q5DR01", + .xres = 320, + .yres = 240, + .bpp = 16, + .control_base = + ( LCD_CONTROL_SBPPF_565 + | LCD_CONTROL_C + | LCD_CONTROL_SM_0 + | LCD_CONTROL_DEFAULT_PO + | LCD_CONTROL_PT + | LCD_CONTROL_PC + | LCD_CONTROL_BPP_16 ), + .horztiming = + ( LCD_HORZTIMING_HN2_N(8) + | LCD_HORZTIMING_HN1_N(60) + | LCD_HORZTIMING_HPW_N(12) + | LCD_HORZTIMING_PPL_N(320) ), + .verttiming = + ( LCD_VERTTIMING_VN2_N(5) + | LCD_VERTTIMING_VN1_N(17) + | LCD_VERTTIMING_VPW_N(1) + | LCD_VERTTIMING_LPP_N(240) ), + .clkcontrol_base = LCD_CLKCONTROL_PCD_N(1), + }, + + /* Hitachi SP14Q005 and possibly others */ + [3] = { + .name = "Hitachi_SP14Qxxx", + .xres = 320, + .yres = 240, + .bpp = 4, + .control_base = + ( LCD_CONTROL_C + | LCD_CONTROL_BPP_4 ), + .horztiming = + ( LCD_HORZTIMING_HN2_N(1) + | LCD_HORZTIMING_HN1_N(1) + | LCD_HORZTIMING_HPW_N(1) + | LCD_HORZTIMING_PPL_N(320) ), + .verttiming = + ( LCD_VERTTIMING_VN2_N(1) + | LCD_VERTTIMING_VN1_N(1) + | LCD_VERTTIMING_VPW_N(1) + | LCD_VERTTIMING_LPP_N(240) ), + .clkcontrol_base = LCD_CLKCONTROL_PCD_N(4), + }, + + /* Generic 640x480 TFT panel */ + [4] = { + .name = "TFT_640x480_16", + .xres = 640, + .yres = 480, + .bpp = 16, + .control_base = 0x004806a | LCD_CONTROL_DEFAULT_PO, + .horztiming = 0x3434d67f, + .verttiming = 0x0e0e39df, + .clkcontrol_base = LCD_CLKCONTROL_PCD_N(1), + }, + + /* Pb1100 LCDB 640x480 PrimeView TFT panel */ + [5] = { + .name = "PrimeView_640x480_16", + .xres = 640, + .yres = 480, + .bpp = 16, + .control_base = 0x0004886a | LCD_CONTROL_DEFAULT_PO, + .horztiming = 0x0e4bfe7f, + .verttiming = 0x210805df, + .clkcontrol_base = 0x00038001, + }, +}; + +/********************************************************************/ -#define DEBUG 0 +/* Inline helpers */ -#include "au1100fb.h" +#define panel_is_dual(panel) (panel->control_base & LCD_CONTROL_DP) +#define panel_is_active(panel)(panel->control_base & LCD_CONTROL_PT) +#define panel_is_color(panel) (panel->control_base & LCD_CONTROL_PC) +#define panel_swap_rgb(panel) (panel->control_base & LCD_CONTROL_CCO) + +#if defined(CONFIG_COMPILE_TEST) && (!defined(CONFIG_MIPS) || defined(CONFIG_64BIT)) +/* + * KSEG1ADDR() is defined in arch/mips/include/asm/addrspace.h + * for 32 bit configurations. Provide a stub for compile testing + * on other platforms. + */ +#define KSEG1ADDR(x) (x) +#endif #define DRIVER_NAME "au1100fb" #define DRIVER_DESC "LCD controller driver for AU1100 processors" @@ -84,21 +412,6 @@ struct fb_bitfield rgb_bitfields[][4] = { { 8, 4, 0 }, { 4, 4, 0 }, { 0, 4, 0 }, { 0, 0, 0 } }, }; -static struct fb_fix_screeninfo au1100fb_fix = { - .id = "AU1100 FB", - .xpanstep = 1, - .ypanstep = 1, - .type = FB_TYPE_PACKED_PIXELS, - .accel = FB_ACCEL_NONE, -}; - -static struct fb_var_screeninfo au1100fb_var = { - .activate = FB_ACTIVATE_NOW, - .height = -1, - .width = -1, - .vmode = FB_VMODE_NONINTERLACED, -}; - /* fb_blank * Blank the screen. Depending on the mode, the screen will be * activated with the backlight color, or desactivated @@ -107,7 +420,7 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi) { struct au1100fb_device *fbdev = to_au1100fb_device(fbi); - print_dbg("fb_blank %d %p", blank_mode, fbi); + pr_devel("fb_blank %d %p", blank_mode, fbi); switch (blank_mode) { @@ -135,15 +448,17 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi) * Set hardware with var settings. This will enable the controller with a specific * mode, normally validated with the fb_check_var method */ -int au1100fb_setmode(struct au1100fb_device *fbdev) +static 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) { @@ -232,7 +547,8 @@ int au1100fb_setmode(struct au1100fb_device *fbdev) /* fb_setcolreg * Set color in LCD palette. */ -int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi) +static int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *fbi) { struct au1100fb_device *fbdev; u32 *palette; @@ -291,14 +607,14 @@ int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned /* fb_pan_display * Pan display in x and/or y as specified */ -int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) +static int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) { struct au1100fb_device *fbdev; int dy; fbdev = to_au1100fb_device(fbi); - print_dbg("fb_pan_display %p %p", var, fbi); + pr_devel("fb_pan_display %p %p", var, fbi); if (!var || !fbdev) { return -EINVAL; @@ -309,13 +625,13 @@ int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) return -EINVAL; } - print_dbg("fb_pan_display 2 %p %p", var, fbi); + pr_devel("fb_pan_display 2 %p %p", var, fbi); dy = var->yoffset - fbi->var.yoffset; if (dy) { u32 dmaaddr; - print_dbg("Panning screen of %d lines", dy); + pr_devel("Panning screen of %d lines", dy); dmaaddr = fbdev->regs->lcd_dmaaddr0; dmaaddr += (fbi->fix.line_length * dy); @@ -329,7 +645,7 @@ int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr); } } - print_dbg("fb_pan_display 3 %p %p", var, fbi); + pr_devel("fb_pan_display 3 %p %p", var, fbi); return 0; } @@ -338,13 +654,16 @@ int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) * Map video memory in user space. We don't use the generic fb_mmap method mainly * to allow the use of the TLB streaming flag (CCA=6) */ -int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +static int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) { struct au1100fb_device *fbdev = to_au1100fb_device(fbi); vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); +#ifndef CONFIG_S390 + /* On s390 pgprot_val() is a function and thus not a lvalue */ pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6 +#endif return dma_mmap_coherent(fbdev->dev, vma, fbdev->fb_mem, fbdev->fb_phys, fbdev->fb_len); @@ -369,7 +688,7 @@ static int au1100fb_setup(struct au1100fb_device *fbdev) int num_panels = ARRAY_SIZE(known_lcd_panels); if (num_panels <= 0) { - print_err("No LCD panels supported by driver!"); + pr_err("No LCD panels supported by driver!"); return -ENODEV; } @@ -392,16 +711,16 @@ static int au1100fb_setup(struct au1100fb_device *fbdev) } } if (i >= num_panels) { - print_warn("Panel '%s' not supported!", this_opt); + pr_warn("Panel '%s' not supported!", this_opt); return -ENODEV; } } /* Unsupported option */ else - print_warn("Unsupported option \"%s\"", this_opt); + pr_warn("Unsupported option \"%s\"", this_opt); } - print_info("Panel=%s", fbdev->panel->name); + pr_info("Panel=%s", fbdev->panel->name); return 0; } @@ -426,26 +745,33 @@ static int au1100fb_drv_probe(struct platform_device *dev) /* Allocate region for our registers and map them */ regs_res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!regs_res) { - print_err("fail to retrieve registers resource"); + pr_err("fail to retrieve registers resource"); return -EFAULT; } - au1100fb_fix.mmio_start = regs_res->start; - au1100fb_fix.mmio_len = resource_size(regs_res); + fbdev->info.fix = (struct fb_fix_screeninfo) { + .mmio_start = regs_res->start, + .mmio_len = resource_size(regs_res), + .id = "AU1100 FB", + .xpanstep = 1, + .ypanstep = 1, + .type = FB_TYPE_PACKED_PIXELS, + .accel = FB_ACCEL_NONE, + }; if (!devm_request_mem_region(&dev->dev, - au1100fb_fix.mmio_start, - au1100fb_fix.mmio_len, + fbdev->info.fix.mmio_start, + fbdev->info.fix.mmio_len, DRIVER_NAME)) { - print_err("fail to lock memory region at 0x%08lx", - au1100fb_fix.mmio_start); + pr_err("fail to lock memory region at 0x%08lx", + fbdev->info.fix.mmio_start); return -EBUSY; } - fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(au1100fb_fix.mmio_start); + fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(fbdev->info.fix.mmio_start); - print_dbg("Register memory map at %p", fbdev->regs); - print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len); + pr_devel("Register memory map at %p", fbdev->regs); + pr_devel("phys=0x%08x, size=%zu", fbdev->regs_phys, fbdev->regs_len); c = clk_get(NULL, "lcd_intclk"); if (!IS_ERR(c)) { @@ -462,27 +788,32 @@ static int au1100fb_drv_probe(struct platform_device *dev) PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { - print_err("fail to allocate framebuffer (size: %dK))", + pr_err("fail to allocate framebuffer (size: %zuK))", fbdev->fb_len / 1024); return -ENOMEM; } - au1100fb_fix.smem_start = fbdev->fb_phys; - au1100fb_fix.smem_len = fbdev->fb_len; + fbdev->info.fix.smem_start = fbdev->fb_phys; + fbdev->info.fix.smem_len = fbdev->fb_len; - print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); - print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); + pr_devel("Framebuffer memory map at %p", fbdev->fb_mem); + pr_devel("phys=0x%pad, size=%zuK", &fbdev->fb_phys, fbdev->fb_len / 1024); /* load the panel info into the var struct */ - au1100fb_var.bits_per_pixel = fbdev->panel->bpp; - au1100fb_var.xres = fbdev->panel->xres; - au1100fb_var.xres_virtual = au1100fb_var.xres; - au1100fb_var.yres = fbdev->panel->yres; - au1100fb_var.yres_virtual = au1100fb_var.yres; + fbdev->info.var = (struct fb_var_screeninfo) { + .activate = FB_ACTIVATE_NOW, + .height = -1, + .width = -1, + .vmode = FB_VMODE_NONINTERLACED, + .bits_per_pixel = fbdev->panel->bpp, + .xres = fbdev->panel->xres, + .xres_virtual = fbdev->panel->xres, + .yres = fbdev->panel->yres, + .yres_virtual = fbdev->panel->yres, + }; fbdev->info.screen_base = fbdev->fb_mem; fbdev->info.fbops = &au1100fb_ops; - fbdev->info.fix = au1100fb_fix; fbdev->info.pseudo_palette = devm_kcalloc(&dev->dev, 16, sizeof(u32), GFP_KERNEL); @@ -490,19 +821,17 @@ static int au1100fb_drv_probe(struct platform_device *dev) return -ENOMEM; if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { - print_err("Fail to allocate colormap (%d entries)", + pr_err("Fail to allocate colormap (%d entries)", AU1100_LCD_NBR_PALETTE_ENTRIES); return -EFAULT; } - fbdev->info.var = au1100fb_var; - /* Set h/w registers */ au1100fb_setmode(fbdev); /* Register new framebuffer */ if (register_framebuffer(&fbdev->info) < 0) { - print_err("cannot register new framebuffer"); + pr_err("cannot register new framebuffer"); goto failed; } @@ -520,7 +849,7 @@ failed: return -ENODEV; } -void au1100fb_drv_remove(struct platform_device *dev) +static void au1100fb_drv_remove(struct platform_device *dev) { struct au1100fb_device *fbdev = NULL; @@ -543,9 +872,7 @@ void au1100fb_drv_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static struct au1100fb_regs fbregs; - -int au1100fb_drv_suspend(struct platform_device *dev, pm_message_t state) +static int au1100fb_drv_suspend(struct platform_device *dev, pm_message_t state) { struct au1100fb_device *fbdev = platform_get_drvdata(dev); @@ -557,21 +884,24 @@ int au1100fb_drv_suspend(struct platform_device *dev, pm_message_t state) clk_disable(fbdev->lcdclk); - memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs)); + memcpy(&fbdev->pm_regs, fbdev->regs, sizeof(struct au1100fb_regs)); return 0; } -int au1100fb_drv_resume(struct platform_device *dev) +static int au1100fb_drv_resume(struct platform_device *dev) { struct au1100fb_device *fbdev = platform_get_drvdata(dev); + int ret; if (!fbdev) return 0; - memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs)); + memcpy(fbdev->regs, &fbdev->pm_regs, sizeof(struct au1100fb_regs)); - clk_enable(fbdev->lcdclk); + ret = clk_enable(fbdev->lcdclk); + if (ret) + return ret; /* Unblank the LCD */ au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info); diff --git a/drivers/video/fbdev/au1100fb.h b/drivers/video/fbdev/au1100fb.h deleted file mode 100644 index 79f4048726f1..000000000000 --- a/drivers/video/fbdev/au1100fb.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * Hardware definitions for the Au1100 LCD controller - * - * Copyright 2002 MontaVista Software - * Copyright 2002 Alchemy Semiconductor - * Author: Alchemy Semiconductor, MontaVista Software - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _AU1100LCD_H -#define _AU1100LCD_H - -#include <asm/mach-au1x00/au1000.h> - -#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) -#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) -#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) - -#if DEBUG -#define print_dbg(f, arg...) printk(__FILE__ ": " f "\n", ## arg) -#else -#define print_dbg(f, arg...) do {} while (0) -#endif - -#if defined(__BIG_ENDIAN) -#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 -#else -#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 -#endif -#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 - -/********************************************************************/ - -/* LCD controller restrictions */ -#define AU1100_LCD_MAX_XRES 800 -#define AU1100_LCD_MAX_YRES 600 -#define AU1100_LCD_MAX_BPP 16 -#define AU1100_LCD_MAX_CLK 48000000 -#define AU1100_LCD_NBR_PALETTE_ENTRIES 256 - -/* Default number of visible screen buffer to allocate */ -#define AU1100FB_NBR_VIDEO_BUFFERS 4 - -/********************************************************************/ - -struct au1100fb_panel -{ - const char name[25]; /* Full name <vendor>_<model> */ - - u32 control_base; /* Mode-independent control values */ - u32 clkcontrol_base; /* Panel pixclock preferences */ - - u32 horztiming; - u32 verttiming; - - u32 xres; /* Maximum horizontal resolution */ - u32 yres; /* Maximum vertical resolution */ - u32 bpp; /* Maximum depth supported */ -}; - -struct au1100fb_regs -{ - u32 lcd_control; - u32 lcd_intstatus; - u32 lcd_intenable; - u32 lcd_horztiming; - u32 lcd_verttiming; - u32 lcd_clkcontrol; - u32 lcd_dmaaddr0; - u32 lcd_dmaaddr1; - u32 lcd_words; - u32 lcd_pwmdiv; - u32 lcd_pwmhi; - u32 reserved[(0x0400-0x002C)/4]; - u32 lcd_palettebase[256]; -}; - -struct au1100fb_device { - - struct fb_info info; /* FB driver info record */ - - struct au1100fb_panel *panel; /* Panel connected to this device */ - - struct au1100fb_regs* regs; /* Registers memory map */ - size_t regs_len; - unsigned int regs_phys; - - unsigned char* fb_mem; /* FrameBuffer memory map */ - size_t fb_len; - dma_addr_t fb_phys; - int panel_idx; - struct clk *lcdclk; - struct device *dev; -}; - -/********************************************************************/ - -#define LCD_CONTROL (AU1100_LCD_BASE + 0x0) - #define LCD_CONTROL_SBB_BIT 21 - #define LCD_CONTROL_SBB_MASK (0x3 << LCD_CONTROL_SBB_BIT) - #define LCD_CONTROL_SBB_1 (0 << LCD_CONTROL_SBB_BIT) - #define LCD_CONTROL_SBB_2 (1 << LCD_CONTROL_SBB_BIT) - #define LCD_CONTROL_SBB_3 (2 << LCD_CONTROL_SBB_BIT) - #define LCD_CONTROL_SBB_4 (3 << LCD_CONTROL_SBB_BIT) - #define LCD_CONTROL_SBPPF_BIT 18 - #define LCD_CONTROL_SBPPF_MASK (0x7 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_SBPPF_655 (0 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_SBPPF_565 (1 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_SBPPF_556 (2 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_SBPPF_1555 (3 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_SBPPF_5551 (4 << LCD_CONTROL_SBPPF_BIT) - #define LCD_CONTROL_WP (1<<17) - #define LCD_CONTROL_WD (1<<16) - #define LCD_CONTROL_C (1<<15) - #define LCD_CONTROL_SM_BIT 13 - #define LCD_CONTROL_SM_MASK (0x3 << LCD_CONTROL_SM_BIT) - #define LCD_CONTROL_SM_0 (0 << LCD_CONTROL_SM_BIT) - #define LCD_CONTROL_SM_90 (1 << LCD_CONTROL_SM_BIT) - #define LCD_CONTROL_SM_180 (2 << LCD_CONTROL_SM_BIT) - #define LCD_CONTROL_SM_270 (3 << LCD_CONTROL_SM_BIT) - #define LCD_CONTROL_DB (1<<12) - #define LCD_CONTROL_CCO (1<<11) - #define LCD_CONTROL_DP (1<<10) - #define LCD_CONTROL_PO_BIT 8 - #define LCD_CONTROL_PO_MASK (0x3 << LCD_CONTROL_PO_BIT) - #define LCD_CONTROL_PO_00 (0 << LCD_CONTROL_PO_BIT) - #define LCD_CONTROL_PO_01 (1 << LCD_CONTROL_PO_BIT) - #define LCD_CONTROL_PO_10 (2 << LCD_CONTROL_PO_BIT) - #define LCD_CONTROL_PO_11 (3 << LCD_CONTROL_PO_BIT) - #define LCD_CONTROL_MPI (1<<7) - #define LCD_CONTROL_PT (1<<6) - #define LCD_CONTROL_PC (1<<5) - #define LCD_CONTROL_BPP_BIT 1 - #define LCD_CONTROL_BPP_MASK (0x7 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_1 (0 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_2 (1 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_4 (2 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_8 (3 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_12 (4 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_BPP_16 (5 << LCD_CONTROL_BPP_BIT) - #define LCD_CONTROL_GO (1<<0) - -#define LCD_INTSTATUS (AU1100_LCD_BASE + 0x4) -#define LCD_INTENABLE (AU1100_LCD_BASE + 0x8) - #define LCD_INT_SD (1<<7) - #define LCD_INT_OF (1<<6) - #define LCD_INT_UF (1<<5) - #define LCD_INT_SA (1<<3) - #define LCD_INT_SS (1<<2) - #define LCD_INT_S1 (1<<1) - #define LCD_INT_S0 (1<<0) - -#define LCD_HORZTIMING (AU1100_LCD_BASE + 0xC) - #define LCD_HORZTIMING_HN2_BIT 24 - #define LCD_HORZTIMING_HN2_MASK (0xFF << LCD_HORZTIMING_HN2_BIT) - #define LCD_HORZTIMING_HN2_N(N) ((((N)-1) << LCD_HORZTIMING_HN2_BIT) & LCD_HORZTIMING_HN2_MASK) - #define LCD_HORZTIMING_HN1_BIT 16 - #define LCD_HORZTIMING_HN1_MASK (0xFF << LCD_HORZTIMING_HN1_BIT) - #define LCD_HORZTIMING_HN1_N(N) ((((N)-1) << LCD_HORZTIMING_HN1_BIT) & LCD_HORZTIMING_HN1_MASK) - #define LCD_HORZTIMING_HPW_BIT 10 - #define LCD_HORZTIMING_HPW_MASK (0x3F << LCD_HORZTIMING_HPW_BIT) - #define LCD_HORZTIMING_HPW_N(N) ((((N)-1) << LCD_HORZTIMING_HPW_BIT) & LCD_HORZTIMING_HPW_MASK) - #define LCD_HORZTIMING_PPL_BIT 0 - #define LCD_HORZTIMING_PPL_MASK (0x3FF << LCD_HORZTIMING_PPL_BIT) - #define LCD_HORZTIMING_PPL_N(N) ((((N)-1) << LCD_HORZTIMING_PPL_BIT) & LCD_HORZTIMING_PPL_MASK) - -#define LCD_VERTTIMING (AU1100_LCD_BASE + 0x10) - #define LCD_VERTTIMING_VN2_BIT 24 - #define LCD_VERTTIMING_VN2_MASK (0xFF << LCD_VERTTIMING_VN2_BIT) - #define LCD_VERTTIMING_VN2_N(N) ((((N)-1) << LCD_VERTTIMING_VN2_BIT) & LCD_VERTTIMING_VN2_MASK) - #define LCD_VERTTIMING_VN1_BIT 16 - #define LCD_VERTTIMING_VN1_MASK (0xFF << LCD_VERTTIMING_VN1_BIT) - #define LCD_VERTTIMING_VN1_N(N) ((((N)-1) << LCD_VERTTIMING_VN1_BIT) & LCD_VERTTIMING_VN1_MASK) - #define LCD_VERTTIMING_VPW_BIT 10 - #define LCD_VERTTIMING_VPW_MASK (0x3F << LCD_VERTTIMING_VPW_BIT) - #define LCD_VERTTIMING_VPW_N(N) ((((N)-1) << LCD_VERTTIMING_VPW_BIT) & LCD_VERTTIMING_VPW_MASK) - #define LCD_VERTTIMING_LPP_BIT 0 - #define LCD_VERTTIMING_LPP_MASK (0x3FF << LCD_VERTTIMING_LPP_BIT) - #define LCD_VERTTIMING_LPP_N(N) ((((N)-1) << LCD_VERTTIMING_LPP_BIT) & LCD_VERTTIMING_LPP_MASK) - -#define LCD_CLKCONTROL (AU1100_LCD_BASE + 0x14) - #define LCD_CLKCONTROL_IB (1<<18) - #define LCD_CLKCONTROL_IC (1<<17) - #define LCD_CLKCONTROL_IH (1<<16) - #define LCD_CLKCONTROL_IV (1<<15) - #define LCD_CLKCONTROL_BF_BIT 10 - #define LCD_CLKCONTROL_BF_MASK (0x1F << LCD_CLKCONTROL_BF_BIT) - #define LCD_CLKCONTROL_BF_N(N) ((((N)-1) << LCD_CLKCONTROL_BF_BIT) & LCD_CLKCONTROL_BF_MASK) - #define LCD_CLKCONTROL_PCD_BIT 0 - #define LCD_CLKCONTROL_PCD_MASK (0x3FF << LCD_CLKCONTROL_PCD_BIT) - #define LCD_CLKCONTROL_PCD_N(N) (((N) << LCD_CLKCONTROL_PCD_BIT) & LCD_CLKCONTROL_PCD_MASK) - -#define LCD_DMAADDR0 (AU1100_LCD_BASE + 0x18) -#define LCD_DMAADDR1 (AU1100_LCD_BASE + 0x1C) - #define LCD_DMA_SA_BIT 5 - #define LCD_DMA_SA_MASK (0x7FFFFFF << LCD_DMA_SA_BIT) - #define LCD_DMA_SA_N(N) ((N) & LCD_DMA_SA_MASK) - -#define LCD_WORDS (AU1100_LCD_BASE + 0x20) - #define LCD_WRD_WRDS_BIT 0 - #define LCD_WRD_WRDS_MASK (0xFFFFFFFF << LCD_WRD_WRDS_BIT) - #define LCD_WRD_WRDS_N(N) ((((N)-1) << LCD_WRD_WRDS_BIT) & LCD_WRD_WRDS_MASK) - -#define LCD_PWMDIV (AU1100_LCD_BASE + 0x24) - #define LCD_PWMDIV_EN (1<<12) - #define LCD_PWMDIV_PWMDIV_BIT 0 - #define LCD_PWMDIV_PWMDIV_MASK (0xFFF << LCD_PWMDIV_PWMDIV_BIT) - #define LCD_PWMDIV_PWMDIV_N(N) ((((N)-1) << LCD_PWMDIV_PWMDIV_BIT) & LCD_PWMDIV_PWMDIV_MASK) - -#define LCD_PWMHI (AU1100_LCD_BASE + 0x28) - #define LCD_PWMHI_PWMHI1_BIT 12 - #define LCD_PWMHI_PWMHI1_MASK (0xFFF << LCD_PWMHI_PWMHI1_BIT) - #define LCD_PWMHI_PWMHI1_N(N) (((N) << LCD_PWMHI_PWMHI1_BIT) & LCD_PWMHI_PWMHI1_MASK) - #define LCD_PWMHI_PWMHI0_BIT 0 - #define LCD_PWMHI_PWMHI0_MASK (0xFFF << LCD_PWMHI_PWMHI0_BIT) - #define LCD_PWMHI_PWMHI0_N(N) (((N) << LCD_PWMHI_PWMHI0_BIT) & LCD_PWMHI_PWMHI0_MASK) - -#define LCD_PALLETTEBASE (AU1100_LCD_BASE + 0x400) - #define LCD_PALLETTE_MONO_MI_BIT 0 - #define LCD_PALLETTE_MONO_MI_MASK (0xF << LCD_PALLETTE_MONO_MI_BIT) - #define LCD_PALLETTE_MONO_MI_N(N) (((N)<< LCD_PALLETTE_MONO_MI_BIT) & LCD_PALLETTE_MONO_MI_MASK) - - #define LCD_PALLETTE_COLOR_RI_BIT 8 - #define LCD_PALLETTE_COLOR_RI_MASK (0xF << LCD_PALLETTE_COLOR_RI_BIT) - #define LCD_PALLETTE_COLOR_RI_N(N) (((N)<< LCD_PALLETTE_COLOR_RI_BIT) & LCD_PALLETTE_COLOR_RI_MASK) - #define LCD_PALLETTE_COLOR_GI_BIT 4 - #define LCD_PALLETTE_COLOR_GI_MASK (0xF << LCD_PALLETTE_COLOR_GI_BIT) - #define LCD_PALLETTE_COLOR_GI_N(N) (((N)<< LCD_PALLETTE_COLOR_GI_BIT) & LCD_PALLETTE_COLOR_GI_MASK) - #define LCD_PALLETTE_COLOR_BI_BIT 0 - #define LCD_PALLETTE_COLOR_BI_MASK (0xF << LCD_PALLETTE_COLOR_BI_BIT) - #define LCD_PALLETTE_COLOR_BI_N(N) (((N)<< LCD_PALLETTE_COLOR_BI_BIT) & LCD_PALLETTE_COLOR_BI_MASK) - - #define LCD_PALLETTE_TFT_DC_BIT 0 - #define LCD_PALLETTE_TFT_DC_MASK (0xFFFF << LCD_PALLETTE_TFT_DC_BIT) - #define LCD_PALLETTE_TFT_DC_N(N) (((N)<< LCD_PALLETTE_TFT_DC_BIT) & LCD_PALLETTE_TFT_DC_MASK) - -/********************************************************************/ - -/* List of panels known to work with the AU1100 LCD controller. - * To add a new panel, enter the same specifications as the - * Generic_TFT one, and MAKE SURE that it doesn't conflicts - * with the controller restrictions. Restrictions are: - * - * STN color panels: max_bpp <= 12 - * STN mono panels: max_bpp <= 4 - * TFT panels: max_bpp <= 16 - * max_xres <= 800 - * max_yres <= 600 - */ -static struct au1100fb_panel known_lcd_panels[] = -{ - /* 800x600x16bpp CRT */ - [0] = { - .name = "CRT_800x600_16", - .xres = 800, - .yres = 600, - .bpp = 16, - .control_base = 0x0004886A | - LCD_CONTROL_DEFAULT_PO | LCD_CONTROL_DEFAULT_SBPPF | - LCD_CONTROL_BPP_16 | LCD_CONTROL_SBB_4, - .clkcontrol_base = 0x00020000, - .horztiming = 0x005aff1f, - .verttiming = 0x16000e57, - }, - /* just the standard LCD */ - [1] = { - .name = "WWPC LCD", - .xres = 240, - .yres = 320, - .bpp = 16, - .control_base = 0x0006806A, - .horztiming = 0x0A1010EF, - .verttiming = 0x0301013F, - .clkcontrol_base = 0x00018001, - }, - /* Sharp 320x240 TFT panel */ - [2] = { - .name = "Sharp_LQ038Q5DR01", - .xres = 320, - .yres = 240, - .bpp = 16, - .control_base = - ( LCD_CONTROL_SBPPF_565 - | LCD_CONTROL_C - | LCD_CONTROL_SM_0 - | LCD_CONTROL_DEFAULT_PO - | LCD_CONTROL_PT - | LCD_CONTROL_PC - | LCD_CONTROL_BPP_16 ), - .horztiming = - ( LCD_HORZTIMING_HN2_N(8) - | LCD_HORZTIMING_HN1_N(60) - | LCD_HORZTIMING_HPW_N(12) - | LCD_HORZTIMING_PPL_N(320) ), - .verttiming = - ( LCD_VERTTIMING_VN2_N(5) - | LCD_VERTTIMING_VN1_N(17) - | LCD_VERTTIMING_VPW_N(1) - | LCD_VERTTIMING_LPP_N(240) ), - .clkcontrol_base = LCD_CLKCONTROL_PCD_N(1), - }, - - /* Hitachi SP14Q005 and possibly others */ - [3] = { - .name = "Hitachi_SP14Qxxx", - .xres = 320, - .yres = 240, - .bpp = 4, - .control_base = - ( LCD_CONTROL_C - | LCD_CONTROL_BPP_4 ), - .horztiming = - ( LCD_HORZTIMING_HN2_N(1) - | LCD_HORZTIMING_HN1_N(1) - | LCD_HORZTIMING_HPW_N(1) - | LCD_HORZTIMING_PPL_N(320) ), - .verttiming = - ( LCD_VERTTIMING_VN2_N(1) - | LCD_VERTTIMING_VN1_N(1) - | LCD_VERTTIMING_VPW_N(1) - | LCD_VERTTIMING_LPP_N(240) ), - .clkcontrol_base = LCD_CLKCONTROL_PCD_N(4), - }, - - /* Generic 640x480 TFT panel */ - [4] = { - .name = "TFT_640x480_16", - .xres = 640, - .yres = 480, - .bpp = 16, - .control_base = 0x004806a | LCD_CONTROL_DEFAULT_PO, - .horztiming = 0x3434d67f, - .verttiming = 0x0e0e39df, - .clkcontrol_base = LCD_CLKCONTROL_PCD_N(1), - }, - - /* Pb1100 LCDB 640x480 PrimeView TFT panel */ - [5] = { - .name = "PrimeView_640x480_16", - .xres = 640, - .yres = 480, - .bpp = 16, - .control_base = 0x0004886a | LCD_CONTROL_DEFAULT_PO, - .horztiming = 0x0e4bfe7f, - .verttiming = 0x210805df, - .clkcontrol_base = 0x00038001, - }, -}; - -/********************************************************************/ - -/* Inline helpers */ - -#define panel_is_dual(panel) (panel->control_base & LCD_CONTROL_DP) -#define panel_is_active(panel)(panel->control_base & LCD_CONTROL_PT) -#define panel_is_color(panel) (panel->control_base & LCD_CONTROL_PC) -#define panel_swap_rgb(panel) (panel->control_base & LCD_CONTROL_CCO) - -#endif /* _AU1100LCD_H */ diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index ed770222660b..685e629e7e16 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -1724,8 +1724,10 @@ static int au1200fb_drv_probe(struct platform_device *dev) /* Now hook interrupt too */ irq = platform_get_irq(dev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto failed; + } ret = request_irq(irq, au1200fb_handle_irq, IRQF_SHARED, "lcd", (void *)dev); diff --git a/drivers/video/fbdev/c2p_iplan2.c b/drivers/video/fbdev/c2p_iplan2.c index cfd2361f24b1..ee4b315d3f40 100644 --- a/drivers/video/fbdev/c2p_iplan2.c +++ b/drivers/video/fbdev/c2p_iplan2.c @@ -8,6 +8,7 @@ * for more details. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/string.h> diff --git a/drivers/video/fbdev/c2p_planar.c b/drivers/video/fbdev/c2p_planar.c index 819c82a98ac0..236aad5137ef 100644 --- a/drivers/video/fbdev/c2p_planar.c +++ b/drivers/video/fbdev/c2p_planar.c @@ -8,6 +8,7 @@ * for more details. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/string.h> diff --git a/drivers/video/fbdev/carminefb.c b/drivers/video/fbdev/carminefb.c index e56065cdba97..bd4bff6a2484 100644 --- a/drivers/video/fbdev/carminefb.c +++ b/drivers/video/fbdev/carminefb.c @@ -620,7 +620,7 @@ static int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent) return ret; ret = -ENOMEM; - hw = kzalloc(sizeof *hw, GFP_KERNEL); + hw = kzalloc_obj(*hw); if (!hw) goto err_enable_pci; @@ -649,13 +649,13 @@ static int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent) * is required for that largest resolution to avoid remaps at run * time */ - if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM) - carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM; + if (carminefb_fix.smem_len > CARMINE_TOTAL_DISPLAY_MEM) + carminefb_fix.smem_len = CARMINE_TOTAL_DISPLAY_MEM; - else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) { + else if (carminefb_fix.smem_len < CARMINE_TOTAL_DISPLAY_MEM) { printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " "are required.", carminefb_fix.smem_len, - CARMINE_TOTAL_DIPLAY_MEM); + CARMINE_TOTAL_DISPLAY_MEM); goto err_unmap_vregs; } diff --git a/drivers/video/fbdev/carminefb.h b/drivers/video/fbdev/carminefb.h index 297688eba469..c9825481d96b 100644 --- a/drivers/video/fbdev/carminefb.h +++ b/drivers/video/fbdev/carminefb.h @@ -7,7 +7,7 @@ #define MAX_DISPLAY 2 #define CARMINE_DISPLAY_MEM (800 * 600 * 4) -#define CARMINE_TOTAL_DIPLAY_MEM (CARMINE_DISPLAY_MEM * MAX_DISPLAY) +#define CARMINE_TOTAL_DISPLAY_MEM (CARMINE_DISPLAY_MEM * MAX_DISPLAY) #define CARMINE_USE_DISPLAY0 (1 << 0) #define CARMINE_USE_DISPLAY1 (1 << 1) diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index 5e61a349a4ab..7a7db7100499 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c @@ -216,12 +216,9 @@ static int clps711x_fb_probe(struct platform_device *pdev) cfb = info->par; platform_set_drvdata(pdev, info); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto out_fb_release; - cfb->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!cfb->base) { - ret = -ENOMEM; + cfb->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(cfb->base)) { + ret = PTR_ERR(cfb->base); goto out_fb_release; } diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c index 308967b5096a..f7faa95fefd3 100644 --- a/drivers/video/fbdev/cobalt_lcdfb.c +++ b/drivers/video/fbdev/cobalt_lcdfb.c @@ -295,19 +295,13 @@ static int cobalt_lcdfb_probe(struct platform_device *dev) if (!info) return -ENOMEM; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) { + info->screen_base = devm_platform_get_and_ioremap_resource(dev, 0, &res); + if (IS_ERR(info->screen_base)) { framebuffer_release(info); - return -EBUSY; + return PTR_ERR(info->screen_base); } info->screen_size = resource_size(res); - info->screen_base = devm_ioremap(&dev->dev, res->start, - info->screen_size); - if (!info->screen_base) { - framebuffer_release(info); - return -ENOMEM; - } info->fbops = &cobalt_lcd_fbops; info->fix = cobalt_lcdfb_fix; diff --git a/drivers/video/fbdev/controlfb.c b/drivers/video/fbdev/controlfb.c index 5c5284e8ae0e..4bb821d7f284 100644 --- a/drivers/video/fbdev/controlfb.c +++ b/drivers/video/fbdev/controlfb.c @@ -944,7 +944,7 @@ static int __init control_of_init(struct device_node *dp) printk(KERN_ERR "can't get 2 addresses for control\n"); return -ENXIO; } - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc_obj(*p); if (!p) return -ENOMEM; control_fb = p; /* save it for cleanups */ diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig index d554d8c543d4..8d1993e0b591 100644 --- a/drivers/video/fbdev/core/Kconfig +++ b/drivers/video/fbdev/core/Kconfig @@ -10,28 +10,13 @@ config FB_CORE config FB_NOTIFY bool -config FIRMWARE_EDID - bool "Enable firmware EDID" - depends on FB - help - This enables access to the EDID transferred from the firmware. - On the i386, this is from the Video BIOS. Enable this if DDC/I2C - transfers do not work for your driver and if you are using - nvidiafb, i810fb or savagefb. - - In general, choosing Y for this option is safe. If you - experience extremely long delays while booting before you get - something on your display, try setting this to N. Matrox cards in - combination with certain motherboards and monitors are known to - suffer from this problem. - config FB_DEVICE bool "Provide legacy /dev/fb* device" depends on FB_CORE default FB help Say Y here if you want the legacy /dev/fb* device file and - interfaces within sysfs anc procfs. It is only required if you + interfaces within sysfs and procfs. It is only required if you have userspace programs that depend on fbdev for graphics output. This does not affect the framebuffer console. If unsure, say N. @@ -69,7 +54,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 +82,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 @@ -187,7 +180,7 @@ config FB_BACKLIGHT depends on FB config FB_MODE_HELPERS - bool "Enable Video Mode Handling Helpers" + bool depends on FB help This enables functions for handling video modes using the diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index 3ff1b2a8659e..65681dcc5930 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -22,8 +22,7 @@ /* * Accelerated handlers. */ -static void update_attr(u8 *dst, u8 *src, int attribute, - struct vc_data *vc) +static void update_attr(u8 *dst, const u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; int width = DIV_ROUND_UP(vc->vc_font.width, 8); @@ -59,12 +58,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; @@ -80,12 +78,16 @@ static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info, struct fb_image *image, u8 *buf, u8 *dst) { u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + unsigned int charcnt = vc->vc_font.charcount; u32 idx = vc->vc_font.width >> 3; - u8 *src; + const u8 *src; while (cnt--) { - src = vc->vc_font.data + (scr_readw(s++)& - charmask)*cellsize; + u16 ch = scr_readw(s++) & charmask; + + if (ch >= charcnt) + ch = 0; + src = vc->vc_font.data + (unsigned int)ch * cellsize; if (attr) { update_attr(buf, src, attr, vc); @@ -113,14 +115,18 @@ static inline void bit_putcs_unaligned(struct vc_data *vc, u8 *dst) { u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + unsigned int charcnt = vc->vc_font.charcount; u32 shift_low = 0, mod = vc->vc_font.width % 8; u32 shift_high = 8; u32 idx = vc->vc_font.width >> 3; - u8 *src; + const u8 *src; while (cnt--) { - src = vc->vc_font.data + (scr_readw(s++)& - charmask)*cellsize; + u16 ch = scr_readw(s++) & charmask; + + if (ch >= charcnt) + ch = 0; + src = vc->vc_font.data + (unsigned int)ch * cellsize; if (attr) { update_attr(buf, src, attr, vc); @@ -161,6 +167,11 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info, image.height = vc->vc_font.height; image.depth = 1; + if (image.dy >= info->var.yres) + return; + + image.height = min(image.height, info->var.yres - image.dy); + if (attribute) { buf = kmalloc(cellsize, GFP_ATOMIC); if (!buf) @@ -174,6 +185,18 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info, cnt = count; image.width = vc->vc_font.width * cnt; + + if (image.dx >= info->var.xres) + break; + + if (image.dx + image.width > info->var.xres) { + image.width = info->var.xres - image.dx; + cnt = image.width / vc->vc_font.width; + if (cnt == 0) + break; + image.width = cnt * vc->vc_font.width; + } + pitch = DIV_ROUND_UP(image.width, 8) + scan_align; pitch &= ~scan_align; size = pitch * image.height + buf_align; @@ -237,13 +260,13 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; int w = DIV_ROUND_UP(vc->vc_font.width, 8), c; - int y = real_y(ops->p, vc->state.y); + int y = real_y(par->p, vc->state.y); int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1; - char *src; + const u8 *src; cursor.set = 0; @@ -254,10 +277,10 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, attribute = get_attribute(info, c); src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); - if (ops->cursor_state.image.data != src || - ops->cursor_reset) { - ops->cursor_state.image.data = src; - cursor.set |= FB_CUR_SETIMAGE; + if (par->cursor_state.image.data != (const char *)src || + par->cursor_reset) { + par->cursor_state.image.data = src; + cursor.set |= FB_CUR_SETIMAGE; } if (attribute) { @@ -266,101 +289,72 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC); if (!dst) return; - kfree(ops->cursor_data); - ops->cursor_data = dst; + kfree(par->cursor_data); + par->cursor_data = dst; update_attr(dst, src, attribute, vc); src = dst; } - if (ops->cursor_state.image.fg_color != fg || - ops->cursor_state.image.bg_color != bg || - ops->cursor_reset) { - ops->cursor_state.image.fg_color = fg; - ops->cursor_state.image.bg_color = bg; + if (par->cursor_state.image.fg_color != fg || + par->cursor_state.image.bg_color != bg || + par->cursor_reset) { + par->cursor_state.image.fg_color = fg; + par->cursor_state.image.bg_color = bg; cursor.set |= FB_CUR_SETCMAP; } - if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || - (ops->cursor_state.image.dy != (vc->vc_font.height * y)) || - ops->cursor_reset) { - ops->cursor_state.image.dx = vc->vc_font.width * vc->state.x; - ops->cursor_state.image.dy = vc->vc_font.height * y; + if ((par->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || + (par->cursor_state.image.dy != (vc->vc_font.height * y)) || + par->cursor_reset) { + par->cursor_state.image.dx = vc->vc_font.width * vc->state.x; + par->cursor_state.image.dy = vc->vc_font.height * y; cursor.set |= FB_CUR_SETPOS; } - if (ops->cursor_state.image.height != vc->vc_font.height || - ops->cursor_state.image.width != vc->vc_font.width || - ops->cursor_reset) { - ops->cursor_state.image.height = vc->vc_font.height; - ops->cursor_state.image.width = vc->vc_font.width; + if (par->cursor_state.image.height != vc->vc_font.height || + par->cursor_state.image.width != vc->vc_font.width || + par->cursor_reset) { + par->cursor_state.image.height = vc->vc_font.height; + par->cursor_state.image.width = vc->vc_font.width; cursor.set |= FB_CUR_SETSIZE; } - if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || - ops->cursor_reset) { - ops->cursor_state.hot.x = cursor.hot.y = 0; + if (par->cursor_state.hot.x || par->cursor_state.hot.y || + par->cursor_reset) { + par->cursor_state.hot.x = cursor.hot.y = 0; cursor.set |= FB_CUR_SETHOT; } if (cursor.set & FB_CUR_SETSIZE || - vc->vc_cursor_type != ops->p->cursor_shape || - ops->cursor_state.mask == NULL || - ops->cursor_reset) { - char *mask = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC); - int cur_height, size, i = 0; - u8 msk = 0xff; + vc->vc_cursor_type != par->p->cursor_shape || + par->cursor_state.mask == NULL || + par->cursor_reset) { + unsigned char *mask = kmalloc_array(vc->vc_font.height, w, GFP_ATOMIC); if (!mask) return; + fbcon_fill_cursor_mask(par, vc, mask); - kfree(ops->cursor_state.mask); - ops->cursor_state.mask = mask; + kfree(par->cursor_state.mask); + par->cursor_state.mask = (const char *)mask; - ops->p->cursor_shape = vc->vc_cursor_type; + par->p->cursor_shape = vc->vc_cursor_type; cursor.set |= FB_CUR_SETSHAPE; - - switch (CUR_SIZE(ops->p->cursor_shape)) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; - } - size = (vc->vc_font.height - cur_height) * w; - while (size--) - mask[i++] = ~msk; - size = cur_height * w; - while (size--) - mask[i++] = msk; } - ops->cursor_state.enable = enable && !use_sw; + par->cursor_state.enable = enable && !use_sw; cursor.image.data = src; - cursor.image.fg_color = ops->cursor_state.image.fg_color; - cursor.image.bg_color = ops->cursor_state.image.bg_color; - cursor.image.dx = ops->cursor_state.image.dx; - cursor.image.dy = ops->cursor_state.image.dy; - cursor.image.height = ops->cursor_state.image.height; - cursor.image.width = ops->cursor_state.image.width; - cursor.hot.x = ops->cursor_state.hot.x; - cursor.hot.y = ops->cursor_state.hot.y; - cursor.mask = ops->cursor_state.mask; - cursor.enable = ops->cursor_state.enable; + cursor.image.fg_color = par->cursor_state.image.fg_color; + cursor.image.bg_color = par->cursor_state.image.bg_color; + cursor.image.dx = par->cursor_state.image.dx; + cursor.image.dy = par->cursor_state.image.dy; + cursor.image.height = par->cursor_state.image.height; + cursor.image.width = par->cursor_state.image.width; + cursor.hot.x = par->cursor_state.hot.x; + cursor.hot.y = par->cursor_state.hot.y; + cursor.mask = par->cursor_state.mask; + cursor.enable = par->cursor_state.enable; cursor.image.depth = 1; cursor.rop = ROP_XOR; @@ -370,31 +364,31 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, if (err) soft_cursor(info, &cursor); - ops->cursor_reset = 0; + par->cursor_reset = 0; } static int bit_update_start(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int err; - err = fb_pan_display(info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + err = fb_pan_display(info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode; return err; } -void fbcon_set_bitops(struct fbcon_ops *ops) +static const struct fbcon_bitops bit_fbcon_bitops = { + .bmove = bit_bmove, + .clear = bit_clear, + .putcs = bit_putcs, + .clear_margins = bit_clear_margins, + .cursor = bit_cursor, + .update_start = bit_update_start, +}; + +void fbcon_set_bitops_ur(struct fbcon_par *par) { - ops->bmove = bit_bmove; - ops->clear = bit_clear; - ops->putcs = bit_putcs; - ops->clear_margins = bit_clear_margins; - ops->cursor = bit_cursor; - ops->update_start = bit_update_start; - ops->rotate_font = NULL; - - if (ops->rotate) - fbcon_set_rotate(ops); + par->bitops = &bit_fbcon_bitops; } diff --git a/drivers/video/fbdev/core/cfbcopyarea.c b/drivers/video/fbdev/core/cfbcopyarea.c index a271f57d9c6c..ce2e6807be60 100644 --- a/drivers/video/fbdev/core/cfbcopyarea.c +++ b/drivers/video/fbdev/core/cfbcopyarea.c @@ -1,440 +1,36 @@ +// 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/export.h> #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 +#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE +#define FB_REV_PIXELS_IN_BYTE #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; -#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..bd2fbbda10c6 100644 --- a/drivers/video/fbdev/core/cfbfillrect.c +++ b/drivers/video/fbdev/core/cfbfillrect.c @@ -1,374 +1,36 @@ +// 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/export.h> #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..e116cd1d8a39 100644 --- a/drivers/video/fbdev/core/cfbimgblt.c +++ b/drivers/video/fbdev/core/cfbimgblt.c @@ -1,369 +1,36 @@ +// 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/export.h> #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_backlight.c b/drivers/video/fbdev/core/fb_backlight.c index 6fdaa9f81be9..dbed9696f4c5 100644 --- a/drivers/video/fbdev/core/fb_backlight.c +++ b/drivers/video/fbdev/core/fb_backlight.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include <linux/backlight.h> #include <linux/export.h> #include <linux/fb.h> #include <linux/mutex.h> @@ -36,4 +37,15 @@ struct backlight_device *fb_bl_device(struct fb_info *info) return info->bl_dev; } EXPORT_SYMBOL(fb_bl_device); + +void fb_bl_notify_blank(struct fb_info *info, int old_blank) +{ + bool on = info->blank == FB_BLANK_UNBLANK; + bool prev_on = old_blank == FB_BLANK_UNBLANK; + + if (info->bl_dev) + backlight_notify_blank(info->bl_dev, info->device, on, prev_on); + else + backlight_notify_blank_all(info->device, on, prev_on); +} #endif diff --git a/drivers/video/fbdev/core/fb_cmdline.c b/drivers/video/fbdev/core/fb_cmdline.c index 4d1634c492ec..594b60424d1c 100644 --- a/drivers/video/fbdev/core/fb_cmdline.c +++ b/drivers/video/fbdev/core/fb_cmdline.c @@ -40,7 +40,7 @@ int fb_get_options(const char *name, char **option) bool enabled; if (name) - is_of = strncmp(name, "offb", 4); + is_of = !strncmp(name, "offb", 4); enabled = __video_get_options(name, &options, is_of); 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_ddc.c b/drivers/video/fbdev/core/fb_ddc.c index e25143219862..824796361367 100644 --- a/drivers/video/fbdev/core/fb_ddc.c +++ b/drivers/video/fbdev/core/fb_ddc.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/device.h> +#include <linux/export.h> #include <linux/module.h> #include <linux/fb.h> #include <linux/i2c-algo-bit.h> diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 65363df8e81b..fd00b86e1ae6 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/export.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/vmalloc.h> @@ -23,6 +24,104 @@ #include <linux/rmap.h> #include <linux/pagemap.h> +struct address_space; + +/* + * struct fb_deferred_io_state + */ + +struct fb_deferred_io_state { + struct kref ref; + + int open_count; /* number of opened files; protected by fb_info lock */ + struct address_space *mapping; /* page cache object for fb device */ + + struct mutex lock; /* mutex that protects the pageref list */ + /* fields protected by lock */ + struct fb_info *info; + struct list_head pagereflist; /* list of pagerefs for touched pages */ + unsigned long npagerefs; + struct fb_deferred_io_pageref *pagerefs; +}; + +static struct fb_deferred_io_state *fb_deferred_io_state_alloc(unsigned long len) +{ + struct fb_deferred_io_state *fbdefio_state; + struct fb_deferred_io_pageref *pagerefs; + unsigned long npagerefs; + + fbdefio_state = kzalloc_obj(*fbdefio_state); + if (!fbdefio_state) + return NULL; + + npagerefs = DIV_ROUND_UP(len, PAGE_SIZE); + + /* alloc a page ref for each page of the display memory */ + pagerefs = kvzalloc_objs(*pagerefs, npagerefs); + if (!pagerefs) + goto err_kfree; + fbdefio_state->npagerefs = npagerefs; + fbdefio_state->pagerefs = pagerefs; + + kref_init(&fbdefio_state->ref); + mutex_init(&fbdefio_state->lock); + + INIT_LIST_HEAD(&fbdefio_state->pagereflist); + + return fbdefio_state; + +err_kfree: + kfree(fbdefio_state); + return NULL; +} + +static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state) +{ + WARN_ON(!list_empty(&fbdefio_state->pagereflist)); + mutex_destroy(&fbdefio_state->lock); + kvfree(fbdefio_state->pagerefs); + + kfree(fbdefio_state); +} + +static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state) +{ + kref_get(&fbdefio_state->ref); +} + +static void __fb_deferred_io_state_release(struct kref *ref) +{ + struct fb_deferred_io_state *fbdefio_state = + container_of(ref, struct fb_deferred_io_state, ref); + + fb_deferred_io_state_release(fbdefio_state); +} + +static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state) +{ + kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release); +} + +/* + * struct vm_operations_struct + */ + +static void fb_deferred_io_vm_open(struct vm_area_struct *vma) +{ + struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; + + WARN_ON_ONCE(!try_module_get(THIS_MODULE)); + fb_deferred_io_state_get(fbdefio_state); +} + +static void fb_deferred_io_vm_close(struct vm_area_struct *vma) +{ + struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; + + fb_deferred_io_state_put(fbdefio_state); + module_put(THIS_MODULE); +} + static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long offs) { struct fb_deferred_io *fbdefio = info->fbdefio; @@ -43,18 +142,19 @@ static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long return page; } -static struct fb_deferred_io_pageref *fb_deferred_io_pageref_lookup(struct fb_info *info, - unsigned long offset, - struct page *page) +static struct fb_deferred_io_pageref * +fb_deferred_io_pageref_lookup(struct fb_deferred_io_state *fbdefio_state, unsigned long offset, + struct page *page) { + struct fb_info *info = fbdefio_state->info; unsigned long pgoff = offset >> PAGE_SHIFT; struct fb_deferred_io_pageref *pageref; - if (fb_WARN_ON_ONCE(info, pgoff >= info->npagerefs)) + if (fb_WARN_ON_ONCE(info, pgoff >= fbdefio_state->npagerefs)) return NULL; /* incorrect allocation size */ /* 1:1 mapping between pageref and page offset */ - pageref = &info->pagerefs[pgoff]; + pageref = &fbdefio_state->pagerefs[pgoff]; if (pageref->page) goto out; @@ -69,23 +169,16 @@ out: return pageref; } -static void fb_deferred_io_pageref_clear(struct fb_deferred_io_pageref *pageref) -{ - struct page *page = pageref->page; - - if (page) - page->mapping = NULL; -} - static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info, unsigned long offset, struct page *page) { struct fb_deferred_io *fbdefio = info->fbdefio; - struct list_head *pos = &fbdefio->pagereflist; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + struct list_head *pos = &fbdefio_state->pagereflist; struct fb_deferred_io_pageref *pageref, *cur; - pageref = fb_deferred_io_pageref_lookup(info, offset, page); + pageref = fb_deferred_io_pageref_lookup(fbdefio_state, offset, page); if (!pageref) return NULL; @@ -106,7 +199,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info * pages. If possible, drivers should try to work with * unsorted page lists instead. */ - list_for_each_entry(cur, &fbdefio->pagereflist, list) { + list_for_each_entry(cur, &fbdefio_state->pagereflist, list) { if (cur->offset > pageref->offset) break; } @@ -128,28 +221,46 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref, /* this is to find and return the vmalloc-ed fb pages */ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) { + struct fb_info *info; unsigned long offset; struct page *page; - struct fb_info *info = vmf->vma->vm_private_data; + vm_fault_t ret; + struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; + + mutex_lock(&fbdefio_state->lock); + + info = fbdefio_state->info; + if (!info) { + ret = VM_FAULT_SIGBUS; /* our device is gone */ + goto err_mutex_unlock; + } offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= info->fix.smem_len) - return VM_FAULT_SIGBUS; + if (offset >= info->fix.smem_len) { + ret = VM_FAULT_SIGBUS; + goto err_mutex_unlock; + } page = fb_deferred_io_get_page(info, offset); - if (!page) - return VM_FAULT_SIGBUS; + if (!page) { + ret = VM_FAULT_SIGBUS; + goto err_mutex_unlock; + } - if (vmf->vma->vm_file) - page->mapping = vmf->vma->vm_file->f_mapping; - else - printk(KERN_ERR "no mapping available\n"); + if (!vmf->vma->vm_file) + fb_err(info, "no mapping available\n"); - BUG_ON(!page->mapping); - page->index = vmf->pgoff; /* for folio_mkclean() */ + fb_WARN_ON_ONCE(info, !fbdefio_state->mapping); + + mutex_unlock(&fbdefio_state->lock); vmf->page = page; + return 0; + +err_mutex_unlock: + mutex_unlock(&fbdefio_state->lock); + return ret; } int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync) @@ -176,15 +287,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); * Adds a page to the dirty list. Call this from struct * vm_operations_struct.page_mkwrite. */ -static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset, - struct page *page) +static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state, + unsigned long offset, struct page *page) { - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_info *info; + struct fb_deferred_io *fbdefio; struct fb_deferred_io_pageref *pageref; vm_fault_t ret; /* protect against the workqueue changing the page list */ - mutex_lock(&fbdefio->lock); + mutex_lock(&fbdefio_state->lock); + + info = fbdefio_state->info; + if (!info) { + ret = VM_FAULT_SIGBUS; /* our device is gone */ + goto err_mutex_unlock; + } + + fbdefio = info->fbdefio; pageref = fb_deferred_io_pageref_get(info, offset, page); if (WARN_ON_ONCE(!pageref)) { @@ -194,58 +314,46 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long /* * We want the page to remain locked from ->page_mkwrite until - * the PTE is marked dirty to avoid folio_mkclean() being called - * before the PTE is updated, which would leave the page ignored - * by defio. + * the PTE is marked dirty to avoid mapping_wrprotect_range() + * being called before the PTE is updated, which would leave + * the page ignored by defio. * Do this by locking the page here and informing the caller * about it with VM_FAULT_LOCKED. */ lock_page(pageref->page); - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); /* come back after delay to process the deferred IO */ schedule_delayed_work(&info->deferred_work, fbdefio->delay); return VM_FAULT_LOCKED; err_mutex_unlock: - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); return ret; } -/* - * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O - * @fb_info: The fbdev info structure - * @vmf: The VM fault - * - * This is a callback we get when userspace first tries to - * write to the page. We schedule a workqueue. That workqueue - * will eventually mkclean the touched pages and execute the - * deferred framebuffer IO. Then if userspace touches a page - * again, we repeat the same scheme. - * - * Returns: - * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise. - */ -static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf) +static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state, + struct vm_fault *vmf) { unsigned long offset = vmf->pgoff << PAGE_SHIFT; struct page *page = vmf->page; file_update_time(vmf->vma->vm_file); - return fb_deferred_io_track_page(info, offset, page); + return fb_deferred_io_track_page(fbdefio_state, offset, page); } -/* vm_ops->page_mkwrite handler */ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) { - struct fb_info *info = vmf->vma->vm_private_data; + struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; - return fb_deferred_io_page_mkwrite(info, vmf); + return fb_deferred_io_page_mkwrite(fbdefio_state, vmf); } static const struct vm_operations_struct fb_deferred_io_vm_ops = { + .open = fb_deferred_io_vm_open, + .close = fb_deferred_io_vm_close, .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; @@ -258,11 +366,17 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) { vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + if (!try_module_get(THIS_MODULE)) + return -EINVAL; + vma->vm_ops = &fb_deferred_io_vm_ops; vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); if (!(info->flags & FBINFO_VIRTFB)) vm_flags_set(vma, VM_IO); - vma->vm_private_data = info; + vma->vm_private_data = info->fbdefio_state; + + fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */ + return 0; } EXPORT_SYMBOL_GPL(fb_deferred_io_mmap); @@ -273,61 +387,52 @@ static void fb_deferred_io_work(struct work_struct *work) struct fb_info *info = container_of(work, struct fb_info, deferred_work.work); struct fb_deferred_io_pageref *pageref, *next; struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; - /* here we mkclean the pages, then do all deferred IO */ - mutex_lock(&fbdefio->lock); - list_for_each_entry(pageref, &fbdefio->pagereflist, list) { - struct folio *folio = page_folio(pageref->page); + /* here we wrprotect the page's mappings, then do all deferred IO. */ + mutex_lock(&fbdefio_state->lock); +#ifdef CONFIG_MMU + list_for_each_entry(pageref, &fbdefio_state->pagereflist, list) { + struct page *page = pageref->page; + pgoff_t pgoff = pageref->offset >> PAGE_SHIFT; - folio_lock(folio); - folio_mkclean(folio); - folio_unlock(folio); + mapping_wrprotect_range(fbdefio_state->mapping, pgoff, + page_to_pfn(page), 1); } +#endif /* driver's callback with pagereflist */ - fbdefio->deferred_io(info, &fbdefio->pagereflist); + fbdefio->deferred_io(info, &fbdefio_state->pagereflist); /* clear the list */ - list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list) + list_for_each_entry_safe(pageref, next, &fbdefio_state->pagereflist, list) fb_deferred_io_pageref_put(pageref, info); - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); } int fb_deferred_io_init(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; - struct fb_deferred_io_pageref *pagerefs; - unsigned long npagerefs; - int ret; + struct fb_deferred_io_state *fbdefio_state; BUG_ON(!fbdefio); if (WARN_ON(!info->fix.smem_len)) return -EINVAL; - mutex_init(&fbdefio->lock); + fbdefio_state = fb_deferred_io_state_alloc(info->fix.smem_len); + if (!fbdefio_state) + return -ENOMEM; + fbdefio_state->info = info; + INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work); - INIT_LIST_HEAD(&fbdefio->pagereflist); if (fbdefio->delay == 0) /* set a default of 1 s */ fbdefio->delay = HZ; - npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE); - - /* alloc a page ref for each page of the display memory */ - pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL); - if (!pagerefs) { - ret = -ENOMEM; - goto err; - } - info->npagerefs = npagerefs; - info->pagerefs = pagerefs; + info->fbdefio_state = fbdefio_state; return 0; - -err: - mutex_destroy(&fbdefio->lock); - return ret; } EXPORT_SYMBOL_GPL(fb_deferred_io_init); @@ -335,40 +440,40 @@ void fb_deferred_io_open(struct fb_info *info, struct inode *inode, struct file *file) { - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + fbdefio_state->mapping = file->f_mapping; file->f_mapping->a_ops = &fb_deferred_io_aops; - fbdefio->open_count++; + fbdefio_state->open_count++; } EXPORT_SYMBOL_GPL(fb_deferred_io_open); static void fb_deferred_io_lastclose(struct fb_info *info) { - unsigned long i; - flush_delayed_work(&info->deferred_work); - - /* clear out the mapping that we setup */ - for (i = 0; i < info->npagerefs; ++i) - fb_deferred_io_pageref_clear(&info->pagerefs[i]); } void fb_deferred_io_release(struct fb_info *info) { - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; - if (!--fbdefio->open_count) + if (!--fbdefio_state->open_count) fb_deferred_io_lastclose(info); } EXPORT_SYMBOL_GPL(fb_deferred_io_release); void fb_deferred_io_cleanup(struct fb_info *info) { - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; fb_deferred_io_lastclose(info); - kvfree(info->pagerefs); - mutex_destroy(&fbdefio->lock); + info->fbdefio_state = NULL; + + mutex_lock(&fbdefio_state->lock); + fbdefio_state->info = NULL; + mutex_unlock(&fbdefio_state->lock); + + fb_deferred_io_state_put(fbdefio_state); } EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); 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..f366670a53af --- /dev/null +++ b/drivers/video/fbdev/core/fb_fillrect.h @@ -0,0 +1,279 @@ +/* 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: + return color; + } +#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/fb_info.c b/drivers/video/fbdev/core/fb_info.c index 4847ebe50d7d..52f9bd2c5417 100644 --- a/drivers/video/fbdev/core/fb_info.c +++ b/drivers/video/fbdev/core/fb_info.c @@ -42,6 +42,7 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) info->device = dev; info->fbcon_rotate_hint = -1; + info->blank = FB_BLANK_UNBLANK; #if IS_ENABLED(CONFIG_FB_BACKLIGHT) mutex_init(&info->bl_curve_mutex); diff --git a/drivers/video/fbdev/core/fb_io_fops.c b/drivers/video/fbdev/core/fb_io_fops.c index 3408ff1b2b7a..6ab60fcd0050 100644 --- a/drivers/video/fbdev/core/fb_io_fops.c +++ b/drivers/video/fbdev/core/fb_io_fops.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/export.h> #include <linux/fb.h> #include <linux/module.h> #include <linux/uaccess.h> diff --git a/drivers/video/fbdev/core/fb_sys_fops.c b/drivers/video/fbdev/core/fb_sys_fops.c index a9aa6519a5b3..be96b3b3942e 100644 --- a/drivers/video/fbdev/core/fb_sys_fops.c +++ b/drivers/video/fbdev/core/fb_sys_fops.c @@ -9,6 +9,8 @@ * for more details. * */ + +#include <linux/export.h> #include <linux/fb.h> #include <linux/module.h> #include <linux/uaccess.h> diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index ff09e57f3c38..9cc3e87da14b 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -11,6 +11,7 @@ * more details. */ +#include <linux/export.h> #include <linux/string.h> #include <linux/module.h> #include <linux/fb.h> diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index e8b4e8c119b5..b0e3e765360d 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -56,6 +56,7 @@ * more details. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> @@ -65,6 +66,7 @@ #include <linux/string.h> #include <linux/kd.h> #include <linux/panic.h> +#include <linux/pci.h> #include <linux/printk.h> #include <linux/slab.h> #include <linux/fb.h> @@ -77,9 +79,11 @@ #include <linux/interrupt.h> #include <linux/crc32.h> /* For counting font checksums */ #include <linux/uaccess.h> +#include <linux/vga_switcheroo.h> #include <asm/irq.h> #include "fbcon.h" +#include "fbcon_rotate.h" #include "fb_internal.h" /* @@ -117,9 +121,14 @@ static signed char con2fb_map_boot[MAX_NR_CONSOLES]; static struct fb_info *fbcon_info_from_console(int console) { + signed char fb; WARN_CONSOLE_UNLOCKED(); - return fbcon_registered_fb[con2fb_map[console]]; + fb = con2fb_map[console]; + if (fb < 0 || fb >= ARRAY_SIZE(fbcon_registered_fb)) + return NULL; + + return fbcon_registered_fb[fb]; } static int logo_lines; @@ -129,9 +138,9 @@ static int logo_shown = FBCON_LOGO_CANSHOW; /* console mappings */ static unsigned int first_fb_vc; static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1; -static int fbcon_is_default = 1; +static bool fbcon_is_default = true; static int primary_device = -1; -static int fbcon_has_console_bind; +static bool fbcon_has_console_bind; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY static int map_override; @@ -160,14 +169,13 @@ 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; #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) -static int fbcon_cursor_noblink; +static bool fbcon_cursor_blink = true; #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) @@ -193,27 +201,27 @@ static struct device *fbcon_device; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION static inline void fbcon_set_rotation(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; if (!(info->flags & FBINFO_MISC_TILEBLITTING) && - ops->p->con_rotate < 4) - ops->rotate = ops->p->con_rotate; + par->p->con_rotate < 4) + par->rotate = par->p->con_rotate; else - ops->rotate = 0; + par->rotate = 0; } static void fbcon_rotate(struct fb_info *info, u32 rotate) { - struct fbcon_ops *ops= info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fb_info *fb_info; - if (!ops || ops->currcon == -1) + if (!par || par->currcon == -1) return; - fb_info = fbcon_info_from_console(ops->currcon); + fb_info = fbcon_info_from_console(par->currcon); if (info == fb_info) { - struct fbcon_display *p = &fb_display[ops->currcon]; + struct fbcon_display *p = &fb_display[par->currcon]; if (rotate < 4) p->con_rotate = rotate; @@ -226,12 +234,12 @@ static void fbcon_rotate(struct fb_info *info, u32 rotate) static void fbcon_rotate_all(struct fb_info *info, u32 rotate) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct vc_data *vc; struct fbcon_display *p; int i; - if (!ops || ops->currcon < 0 || rotate > 3) + if (!par || par->currcon < 0 || rotate > 3) return; for (i = first_fb_vc; i <= last_fb_vc; i++) { @@ -249,9 +257,9 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate) #else static inline void fbcon_set_rotation(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - ops->rotate = FB_ROTATE_UR; + par->rotate = FB_ROTATE_UR; } static void fbcon_rotate(struct fb_info *info, u32 rotate) @@ -265,35 +273,48 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate) } #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */ +static void fbcon_set_bitops(struct fbcon_par *par) +{ + switch (par->rotate) { + default: + fallthrough; + case FB_ROTATE_UR: + fbcon_set_bitops_ur(par); + break; + case FB_ROTATE_CW: + fbcon_set_bitops_cw(par); + break; + case FB_ROTATE_UD: + fbcon_set_bitops_ud(par); + break; + case FB_ROTATE_CCW: + fbcon_set_bitops_ccw(par); + break; + } +} + static int fbcon_get_rotate(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - return (ops) ? ops->rotate : 0; + return (par) ? par->rotate : 0; } static bool fbcon_skip_panic(struct fb_info *info) { -/* panic_cpu is not exported, and can't be used if built as module. Use - * oops_in_progress instead, but non-fatal oops won't be printed. - */ -#if defined(MODULE) - return (info->skip_panic && unlikely(oops_in_progress)); -#else - return (info->skip_panic && unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID)); -#endif + return (info->skip_panic && unlikely(panic_in_progress())); } -static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) +static inline bool fbcon_is_active(struct vc_data *vc, struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - return (info->state != FBINFO_STATE_RUNNING || - vc->vc_mode != KD_TEXT || ops->graphics || fbcon_skip_panic(info)); + return info->state == FBINFO_STATE_RUNNING && + vc->vc_mode == KD_TEXT && !par->graphics && !fbcon_skip_panic(info); } static int get_color(struct vc_data *vc, struct fb_info *info, - u16 c, int is_fg) + u16 c, bool is_fg) { int depth = fb_get_color_depth(&info->var, &info->fix); int color = 0; @@ -359,9 +380,19 @@ static int get_color(struct vc_data *vc, struct fb_info *info, return color; } +static int get_fg_color(struct vc_data *vc, struct fb_info *info, u16 c) +{ + return get_color(vc, info, c, true); +} + +static int get_bg_color(struct vc_data *vc, struct fb_info *info, u16 c) +{ + return get_color(vc, info, c, false); +} + static void fb_flashcursor(struct work_struct *work) { - struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work); + struct fbcon_par *par = container_of(work, struct fbcon_par, cursor_work.work); struct fb_info *info; struct vc_data *vc = NULL; int c; @@ -376,10 +407,10 @@ static void fb_flashcursor(struct work_struct *work) return; /* protected by console_lock */ - info = ops->info; + info = par->info; - if (ops->currcon != -1) - vc = vc_cons[ops->currcon].d; + if (par->currcon != -1) + vc = vc_cons[par->currcon].d; if (!vc || !con_is_visible(vc) || fbcon_info_from_console(vc->vc_num) != info || @@ -389,29 +420,70 @@ static void fb_flashcursor(struct work_struct *work) } c = scr_readw((u16 *) vc->vc_pos); - enable = ops->cursor_flash && !ops->cursor_state.enable; - ops->cursor(vc, info, enable, get_color(vc, info, c, 1), - get_color(vc, info, c, 0)); + enable = par->cursor_flash && !par->cursor_state.enable; + par->bitops->cursor(vc, info, enable, + get_fg_color(vc, info, c), + get_bg_color(vc, info, c)); console_unlock(); - queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, - ops->cur_blink_jiffies); + queue_delayed_work(system_power_efficient_wq, &par->cursor_work, + par->cur_blink_jiffies); } static void fbcon_add_cursor_work(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - if (!fbcon_cursor_noblink) - queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, - ops->cur_blink_jiffies); + if (fbcon_cursor_blink) + queue_delayed_work(system_power_efficient_wq, &par->cursor_work, + par->cur_blink_jiffies); } static void fbcon_del_cursor_work(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; + + cancel_delayed_work_sync(&par->cursor_work); +} + +void fbcon_fill_cursor_mask(struct fbcon_par *par, struct vc_data *vc, unsigned char *mask) +{ + static const unsigned int pattern = 0xffffffff; + unsigned int pitch = vc_font_pitch(&vc->vc_font); + unsigned int cur_height, size; + + switch (CUR_SIZE(vc->vc_cursor_type)) { + case CUR_NONE: + cur_height = 0; + break; + case CUR_UNDERLINE: + if (vc->vc_font.height < 10) + cur_height = 1; + else + cur_height = 2; + break; + case CUR_LOWER_THIRD: + cur_height = vc->vc_font.height / 3; + break; + case CUR_LOWER_HALF: + cur_height = vc->vc_font.height / 2; + break; + case CUR_TWO_THIRDS: + cur_height = (vc->vc_font.height * 2) / 3; + break; + case CUR_BLOCK: + default: + cur_height = vc->vc_font.height; + break; + } + + size = (vc->vc_font.height - cur_height) * pitch; + while (size--) + *mask++ = (unsigned char)~pattern; - cancel_delayed_work_sync(&ops->cursor_work); + size = cur_height * pitch; + while (size--) + *mask++ = (unsigned char)pattern; } #ifndef MODULE @@ -459,7 +531,7 @@ static int __init fb_console_setup(char *this_opt) last_fb_vc = simple_strtoul(options, &options, 10) - 1; if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES) last_fb_vc = MAX_NR_CONSOLES - 1; - fbcon_is_default = 0; + fbcon_is_default = false; continue; } @@ -554,7 +626,7 @@ static int do_fbcon_takeover(int show_logo) con2fb_map[i] = -1; info_idx = -1; } else { - fbcon_has_console_bind = 1; + fbcon_has_console_bind = true; } return err; @@ -571,7 +643,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, int cols, int rows, int new_cols, int new_rows) { /* Need to make room for the logo */ - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int cnt, erase = vc->vc_video_erase_char, step; unsigned short *save = NULL, *r, *q; int logo_height; @@ -587,7 +659,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, */ if (fb_get_color_depth(&info->var, &info->fix) == 1) erase &= ~0x400; - logo_height = fb_prepare_logo(info, ops->rotate); + logo_height = fb_prepare_logo(info, par->rotate); logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height); q = (unsigned short *) (vc->vc_origin + vc->vc_size_row * rows); @@ -659,15 +731,15 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, #ifdef CONFIG_FB_TILEBLITTING static void set_blitting_type(struct vc_data *vc, struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - ops->p = &fb_display[vc->vc_num]; + par->p = &fb_display[vc->vc_num]; if ((info->flags & FBINFO_MISC_TILEBLITTING)) fbcon_set_tileops(vc, info); else { fbcon_set_rotation(info); - fbcon_set_bitops(ops); + fbcon_set_bitops(par); } } @@ -684,12 +756,12 @@ static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) #else static void set_blitting_type(struct vc_data *vc, struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; info->flags &= ~FBINFO_MISC_TILEBLITTING; - ops->p = &fb_display[vc->vc_num]; + par->p = &fb_display[vc->vc_num]; fbcon_set_rotation(info); - fbcon_set_bitops(ops); + fbcon_set_bitops(par); } static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) @@ -709,13 +781,15 @@ static void fbcon_release(struct fb_info *info) module_put(info->fbops->owner); if (info->fbcon_par) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; fbcon_del_cursor_work(info); - kfree(ops->cursor_state.mask); - kfree(ops->cursor_data); - kfree(ops->cursor_src); - kfree(ops->fontbuffer); + kfree(par->cursor_state.mask); + kfree(par->cursor_data); + kfree(par->cursor_src); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION + kfree(par->rotated.buf); +#endif kfree(info->fbcon_par); info->fbcon_par = NULL; } @@ -723,7 +797,7 @@ static void fbcon_release(struct fb_info *info) static int fbcon_open(struct fb_info *info) { - struct fbcon_ops *ops; + struct fbcon_par *par; if (!try_module_get(info->fbops->owner)) return -ENODEV; @@ -737,16 +811,16 @@ static int fbcon_open(struct fb_info *info) } unlock_fb_info(info); - ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); - if (!ops) { + par = kzalloc_obj(*par); + if (!par) { fbcon_release(info); return -ENOMEM; } - INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); - ops->info = info; - info->fbcon_par = ops; - ops->cur_blink_jiffies = HZ / 5; + INIT_DELAYED_WORK(&par->cursor_work, fb_flashcursor); + par->info = info; + info->fbcon_par = par; + par->cur_blink_jiffies = HZ / 5; return 0; } @@ -793,12 +867,12 @@ static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, int unit, int show_logo) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int ret; - ops->currcon = fg_console; + par->currcon = fg_console; - if (info->fbops->fb_set_par && !ops->initialized) { + if (info->fbops->fb_set_par && !par->initialized) { ret = info->fbops->fb_set_par(info); if (ret) @@ -807,8 +881,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, "error code %d\n", ret); } - ops->initialized = true; - ops->graphics = 0; + par->initialized = true; + par->graphics = 0; fbcon_set_disp(info, &info->var, unit); if (show_logo) { @@ -821,7 +895,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, fg_vc->vc_rows); } - update_screen(vc_cons[fg_console].d); + if (fg_console != unit) + update_screen(vc_cons[fg_console].d); } /** @@ -944,17 +1019,17 @@ static const char *fbcon_startup(void) struct vc_data *vc = vc_cons[fg_console].d; const struct font_desc *font = NULL; struct fb_info *info = NULL; - struct fbcon_ops *ops; + struct fbcon_par *par; int rows, cols; /* - * If num_registered_fb is zero, this is a call for the dummy part. + * If fbcon_num_registered_fb is zero, this is a call for the dummy part. * The frame buffer devices weren't initialized yet. */ if (!fbcon_num_registered_fb || info_idx == -1) return display_desc; /* - * Instead of blindly using registered_fb[0], we use info_idx, set by + * Instead of blindly using fbcon_registered_fb[0], we use info_idx, set by * fbcon_fb_registered(); */ info = fbcon_registered_fb[info_idx]; @@ -964,10 +1039,12 @@ static const char *fbcon_startup(void) if (fbcon_open(info)) return NULL; - ops = info->fbcon_par; - ops->currcon = -1; - ops->graphics = 1; - ops->cur_rotate = -1; + par = info->fbcon_par; + par->currcon = -1; + par->graphics = 1; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION + par->rotated.buf_rotate = -1; +#endif p->con_rotate = initial_rotation; if (p->con_rotate == -1) @@ -986,12 +1063,15 @@ static const char *fbcon_startup(void) info->pixmap.blit_y); vc->vc_font.width = font->width; vc->vc_font.height = font->height; - vc->vc_font.data = (void *)(p->fontdata = font->data); + vc->vc_font.data = font_data_buf(font->data); vc->vc_font.charcount = font->charcount; + + p->fontdata = font->data; + font_data_get(p->fontdata); } - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; vc_resize(vc, cols, rows); @@ -1009,7 +1089,7 @@ static const char *fbcon_startup(void) static void fbcon_init(struct vc_data *vc, bool init) { struct fb_info *info; - struct fbcon_ops *ops; + struct fbcon_par *par; struct vc_data **default_mode = vc->vc_display_fg; struct vc_data *svc = *default_mode; struct fbcon_display *t, *p = &fb_display[vc->vc_num]; @@ -1035,7 +1115,8 @@ static void fbcon_init(struct vc_data *vc, bool init) return; if (!info->fbcon_par) - con2fb_acquire_newinfo(vc, info, vc->vc_num); + if (con2fb_acquire_newinfo(vc, info, vc->vc_num)) + return; /* If we are not the first console on this fb, copy the font from that console */ @@ -1044,15 +1125,13 @@ static void fbcon_init(struct vc_data *vc, bool init) if (t->fontdata) { struct vc_data *fvc = vc_cons[fg_console].d; - vc->vc_font.data = (void *)(p->fontdata = - fvc->vc_font.data); + vc->vc_font.data = fvc->vc_font.data; vc->vc_font.width = fvc->vc_font.width; vc->vc_font.height = fvc->vc_font.height; vc->vc_font.charcount = fvc->vc_font.charcount; - p->userfont = t->userfont; - if (p->userfont) - REFCOUNT(p->fontdata)++; + p->fontdata = t->fontdata; + font_data_get(p->fontdata); } else { const struct font_desc *font = NULL; @@ -1063,8 +1142,11 @@ static void fbcon_init(struct vc_data *vc, bool init) info->pixmap.blit_y); vc->vc_font.width = font->width; vc->vc_font.height = font->height; - vc->vc_font.data = (void *)(p->fontdata = font->data); + vc->vc_font.data = font_data_buf(font->data); vc->vc_font.charcount = font->charcount; + + p->fontdata = font->data; + font_data_get(p->fontdata); } } @@ -1083,8 +1165,8 @@ static void fbcon_init(struct vc_data *vc, bool init) if (!*vc->uni_pagedict_loc) con_copy_unimap(vc, svc); - ops = info->fbcon_par; - ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); + par = info->fbcon_par; + par->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); p->con_rotate = initial_rotation; if (p->con_rotate == -1) @@ -1096,8 +1178,8 @@ static void fbcon_init(struct vc_data *vc, bool init) cols = vc->vc_cols; rows = vc->vc_rows; - new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + new_cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + new_rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); new_cols /= vc->vc_font.width; new_rows /= vc->vc_font.height; @@ -1109,7 +1191,7 @@ static void fbcon_init(struct vc_data *vc, bool init) * We need to do it in fbcon_init() to prevent screen corruption. */ if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) { - if (info->fbops->fb_set_par && !ops->initialized) { + if (info->fbops->fb_set_par && !par->initialized) { ret = info->fbops->fb_set_par(info); if (ret) @@ -1118,10 +1200,10 @@ static void fbcon_init(struct vc_data *vc, bool init) "error code %d\n", ret); } - ops->initialized = true; + par->initialized = true; } - ops->graphics = 0; + par->graphics = 0; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION if ((info->flags & FBINFO_HWACCEL_COPYAREA) && @@ -1145,20 +1227,20 @@ static void fbcon_init(struct vc_data *vc, bool init) if (logo) fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); - if (ops->rotate_font && ops->rotate_font(info, vc)) { - ops->rotate = FB_ROTATE_UR; + if (par->bitops->rotate_font && par->bitops->rotate_font(info, vc)) { + par->rotate = FB_ROTATE_UR; set_blitting_type(vc, info); } - ops->p = &fb_display[fg_console]; + par->p = &fb_display[fg_console]; } static void fbcon_free_font(struct fbcon_display *p) { - if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) - kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); - p->fontdata = NULL; - p->userfont = 0; + if (p->fontdata) { + font_data_put(p->fontdata); + p->fontdata = NULL; + } } static void set_vc_hi_font(struct vc_data *vc, bool set); @@ -1188,7 +1270,7 @@ static void fbcon_deinit(struct vc_data *vc) { struct fbcon_display *p = &fb_display[vc->vc_num]; struct fb_info *info; - struct fbcon_ops *ops; + struct fbcon_par *par; int idx; fbcon_free_font(p); @@ -1202,15 +1284,15 @@ static void fbcon_deinit(struct vc_data *vc) if (!info) goto finished; - ops = info->fbcon_par; + par = info->fbcon_par; - if (!ops) + if (!par) goto finished; if (con_is_visible(vc)) fbcon_del_cursor_work(info); - ops->initialized = false; + par->initialized = false; finished: fbcon_free_font(p); @@ -1257,12 +1339,12 @@ static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, unsigned int height, unsigned int width) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; - + struct fbcon_par *par = info->fbcon_par; + int fg, bg; struct fbcon_display *p = &fb_display[vc->vc_num]; u_int y_break; - if (fbcon_is_inactive(vc, info)) + if (!fbcon_is_active(vc, info)) return; if (!height || !width) @@ -1272,23 +1354,25 @@ static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, vc->vc_top = 0; /* * If the font dimensions are not an integral of the display - * dimensions then the ops->clear below won't end up clearing + * dimensions then the par->clear below won't end up clearing * the margins. Call clear_margins here in case the logo * bitmap stretched into the margin area. */ 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 + b), sx, height - b, - width); + par->bitops->clear(vc, info, real_y(p, sy), sx, b, width, fg, bg); + par->bitops->clear(vc, info, real_y(p, sy + b), sx, height - b, + width, fg, bg); } else - ops->clear(vc, info, real_y(p, sy), sx, height, width); + par->bitops->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, @@ -1302,32 +1386,32 @@ static void fbcon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - if (!fbcon_is_inactive(vc, info)) - ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, - get_color(vc, info, scr_readw(s), 1), - get_color(vc, info, scr_readw(s), 0)); + if (fbcon_is_active(vc, info)) + par->bitops->putcs(vc, info, s, count, real_y(p, ypos), xpos, + get_fg_color(vc, info, scr_readw(s)), + get_bg_color(vc, info, scr_readw(s))); } static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - if (!fbcon_is_inactive(vc, info)) - ops->clear_margins(vc, info, margin_color, bottom_only); + if (fbcon_is_active(vc, info)) + par->bitops->clear_margins(vc, info, margin_color, bottom_only); } static void fbcon_cursor(struct vc_data *vc, bool enable) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int c = scr_readw((u16 *) vc->vc_pos); - ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); + par->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); - if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) + if (!fbcon_is_active(vc, info) || vc->vc_deccm != 1) return; if (vc->vc_cursor_type & CUR_SW) @@ -1335,13 +1419,14 @@ static void fbcon_cursor(struct vc_data *vc, bool enable) else fbcon_add_cursor_work(info); - ops->cursor_flash = enable; + par->cursor_flash = enable; - if (!ops->cursor) + if (!par->bitops->cursor) return; - ops->cursor(vc, info, enable, get_color(vc, info, c, 1), - get_color(vc, info, c, 0)); + par->bitops->cursor(vc, info, enable, + get_fg_color(vc, info, c), + get_bg_color(vc, info, c)); } static int scrollback_phys_max = 0; @@ -1354,8 +1439,9 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, struct fbcon_display *p, *t; struct vc_data **default_mode, *vc; struct vc_data *svc; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int rows, cols; + unsigned long ret = 0; p = &fb_display[unit]; @@ -1371,14 +1457,13 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, svc = *default_mode; t = &fb_display[svc->vc_num]; - if (!vc->vc_font.data) { - vc->vc_font.data = (void *)(p->fontdata = t->fontdata); + if (!p->fontdata) { + vc->vc_font.data = font_data_buf(t->fontdata); vc->vc_font.width = (*default_mode)->vc_font.width; vc->vc_font.height = (*default_mode)->vc_font.height; vc->vc_font.charcount = (*default_mode)->vc_font.charcount; - p->userfont = t->userfont; - if (p->userfont) - REFCOUNT(p->fontdata)++; + p->fontdata = t->fontdata; + font_data_get(p->fontdata); } var->activate = FB_ACTIVATE_NOW; @@ -1386,7 +1471,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, var->yoffset = info->var.yoffset; var->xoffset = info->var.xoffset; fb_set_var(info, var); - ops->var = info->var; + par->var = info->var; vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; if (vc->vc_font.charcount == 256) { @@ -1402,30 +1487,29 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, if (!*vc->uni_pagedict_loc) con_copy_unimap(vc, svc); - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; - vc_resize(vc, cols, rows); + ret = vc_resize(vc, cols, rows); - if (con_is_visible(vc)) { + if (con_is_visible(vc) && !ret) update_screen(vc); - } } static __inline__ void ywrap_up(struct vc_data *vc, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; p->yscroll += count; if (p->yscroll >= p->vrows) /* Deal with wrap */ p->yscroll -= p->vrows; - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode |= FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode |= FB_VMODE_YWRAP; + par->bitops->update_start(info); scrollback_max += count; if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; @@ -1435,16 +1519,16 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count) static __inline__ void ywrap_down(struct vc_data *vc, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; p->yscroll -= count; if (p->yscroll < 0) /* Deal with wrap */ p->yscroll += p->vrows; - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode |= FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode |= FB_VMODE_YWRAP; + par->bitops->update_start(info); scrollback_max -= count; if (scrollback_max < 0) scrollback_max = 0; @@ -1455,19 +1539,19 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; p->yscroll += count; if (p->yscroll > p->vrows - vc->vc_rows) { - ops->bmove(vc, info, p->vrows - vc->vc_rows, - 0, 0, 0, vc->vc_rows, vc->vc_cols); + par->bitops->bmove(vc, info, p->vrows - vc->vc_rows, + 0, 0, 0, vc->vc_rows, vc->vc_cols); p->yscroll -= p->vrows - vc->vc_rows; } - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode &= ~FB_VMODE_YWRAP; + par->bitops->update_start(info); fbcon_clear_margins(vc, 1); scrollback_max += count; if (scrollback_max > scrollback_phys_max) @@ -1478,7 +1562,7 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; p->yscroll += count; @@ -1488,10 +1572,10 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); } - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode &= ~FB_VMODE_YWRAP; + par->bitops->update_start(info); fbcon_clear_margins(vc, 1); scrollback_max += count; if (scrollback_max > scrollback_phys_max) @@ -1503,19 +1587,19 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; p->yscroll -= count; if (p->yscroll < 0) { - ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, - 0, vc->vc_rows, vc->vc_cols); + par->bitops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, + 0, vc->vc_rows, vc->vc_cols); p->yscroll += p->vrows - vc->vc_rows; } - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode &= ~FB_VMODE_YWRAP; + par->bitops->update_start(info); fbcon_clear_margins(vc, 1); scrollback_max -= count; if (scrollback_max < 0) @@ -1526,7 +1610,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; p->yscroll -= count; @@ -1536,10 +1620,10 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); } - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); + par->var.xoffset = 0; + par->var.yoffset = p->yscroll * vc->vc_font.height; + par->var.vmode &= ~FB_VMODE_YWRAP; + par->bitops->update_start(info); fbcon_clear_margins(vc, 1); scrollback_max -= count; if (scrollback_max < 0) @@ -1571,12 +1655,10 @@ static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, start = s; } } - console_conditional_schedule(); s++; } while (s < le); if (s > start) fbcon_putcs(vc, start, s - start, dy, x); - console_conditional_schedule(); dy++; } } @@ -1588,7 +1670,7 @@ static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * line); unsigned short *s = d + offset; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; while (count--) { unsigned short *start = s; @@ -1601,8 +1683,8 @@ static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, if (c == scr_readw(d)) { if (s > start) { - ops->bmove(vc, info, line + ycount, x, - line, x, 1, s-start); + par->bitops->bmove(vc, info, line + ycount, x, + line, x, 1, s - start); x += s - start + 1; start = s + 1; } else { @@ -1612,14 +1694,12 @@ static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, } scr_writew(c, d); - console_conditional_schedule(); s++; d++; } while (s < le); if (s > start) - ops->bmove(vc, info, line + ycount, x, line, x, 1, - s-start); - console_conditional_schedule(); + par->bitops->bmove(vc, info, line + ycount, x, line, x, 1, + s - start); if (ycount > 0) line++; else { @@ -1667,13 +1747,11 @@ static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset) } } scr_writew(c, d); - console_conditional_schedule(); s++; d++; } while (s < le); if (s > start) fbcon_putcs(vc, start, s - start, line, x); - console_conditional_schedule(); if (offset > 0) line++; else { @@ -1689,7 +1767,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int dy, int dx, int height, int width, u_int y_break) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u_int b; if (sy < y_break && sy + height > y_break) { @@ -1723,8 +1801,8 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, } return; } - ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); + par->bitops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); } static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, @@ -1733,7 +1811,7 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_display *p = &fb_display[vc->vc_num]; - if (fbcon_is_inactive(vc, info)) + if (!fbcon_is_active(vc, info)) return; if (!width || !height) @@ -1757,7 +1835,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, struct fbcon_display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - if (fbcon_is_inactive(vc, info)) + if (!fbcon_is_active(vc, info)) return true; fbcon_cursor(vc, false); @@ -1951,15 +2029,13 @@ static void updatescrollmode_accel(struct fbcon_display *p, struct vc_data *vc) { #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int cap = info->flags; u16 t = 0; - int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep, - info->fix.xpanstep); - int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t); - int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual, - info->var.xres_virtual); + int ypan = FBCON_SWAP(par->rotate, info->fix.ypanstep, info->fix.xpanstep); + int ywrap = FBCON_SWAP(par->rotate, info->fix.ywrapstep, t); + int yres = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); + int vyres = FBCON_SWAP(par->rotate, info->var.yres_virtual, info->var.xres_virtual); int good_pan = (cap & FBINFO_HWACCEL_YPAN) && divides(ypan, vc->vc_font.height) && vyres > yres; int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) && @@ -1992,11 +2068,10 @@ static void updatescrollmode(struct fbcon_display *p, struct fb_info *info, struct vc_data *vc) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int fh = vc->vc_font.height; - int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual, - info->var.xres_virtual); + int yres = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); + int vyres = FBCON_SWAP(par->rotate, info->var.yres_virtual, info->var.xres_virtual); p->vrows = vyres/fh; if (yres > (fh * (vc->vc_rows + 1))) @@ -2008,21 +2083,17 @@ static void updatescrollmode(struct fbcon_display *p, updatescrollmode_accel(p, info, vc); } -#define PITCH(w) (((w) + 7) >> 3) -#define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */ - static int fbcon_resize(struct vc_data *vc, unsigned int width, unsigned int height, bool from_user) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var = info->var; int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh; - if (p->userfont && FNTSIZE(vc->vc_font.data)) { - int size; - int pitch = PITCH(vc->vc_font.width); + if (font_data_size(p->fontdata)) { + unsigned int size = vc_font_size(&vc->vc_font); /* * If user font, ensure that a possible change to user font @@ -2031,19 +2102,14 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, * charcount can change and cannot be used to determine the * font data allocated size. */ - if (pitch <= 0) - return -EINVAL; - size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount); - if (size > FNTSIZE(vc->vc_font.data)) + if (!size || size > font_data_size(p->fontdata)) return -EINVAL; } - virt_w = FBCON_SWAP(ops->rotate, width, height); - virt_h = FBCON_SWAP(ops->rotate, height, width); - virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width, - vc->vc_font.height); - virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height, - vc->vc_font.width); + virt_w = FBCON_SWAP(par->rotate, width, height); + virt_h = FBCON_SWAP(par->rotate, height, width); + virt_fw = FBCON_SWAP(par->rotate, vc->vc_font.width, vc->vc_font.height); + virt_fh = FBCON_SWAP(par->rotate, vc->vc_font.height, vc->vc_font.width); var.xres = virt_w * virt_fw; var.yres = virt_h * virt_fh; x_diff = info->var.xres - var.xres; @@ -2069,7 +2135,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, fb_set_var(info, &var); } var_to_display(p, &info->var, info); - ops->var = info->var; + par->var = info->var; } updatescrollmode(p, info, vc); return 0; @@ -2078,13 +2144,13 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, static bool fbcon_switch(struct vc_data *vc) { struct fb_info *info, *old_info = NULL; - struct fbcon_ops *ops; + struct fbcon_par *par; struct fbcon_display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var; int i, ret, prev_console; info = fbcon_info_from_console(vc->vc_num); - ops = info->fbcon_par; + par = info->fbcon_par; if (logo_shown >= 0) { struct vc_data *conp2 = vc_cons[logo_shown].d; @@ -2095,7 +2161,7 @@ static bool fbcon_switch(struct vc_data *vc) logo_shown = FBCON_LOGO_CANSHOW; } - prev_console = ops->currcon; + prev_console = par->currcon; if (prev_console != -1) old_info = fbcon_info_from_console(prev_console); /* @@ -2108,9 +2174,9 @@ static bool fbcon_switch(struct vc_data *vc) */ fbcon_for_each_registered_fb(i) { if (fbcon_registered_fb[i]->fbcon_par) { - struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par; + struct fbcon_par *par = fbcon_registered_fb[i]->fbcon_par; - o->currcon = vc->vc_num; + par->currcon = vc->vc_num; } } memset(&var, 0, sizeof(struct fb_var_screeninfo)); @@ -2124,7 +2190,7 @@ static bool fbcon_switch(struct vc_data *vc) info->var.activate = var.activate; var.vmode |= info->var.vmode & ~FB_VMODE_MASK; fb_set_var(info, &var); - ops->var = info->var; + par->var = info->var; if (old_info != NULL && (old_info != info || info->flags & FBINFO_MISC_ALWAYS_SETPAR)) { @@ -2141,17 +2207,16 @@ static bool fbcon_switch(struct vc_data *vc) fbcon_del_cursor_work(old_info); } - if (fbcon_is_inactive(vc, info) || - ops->blank_state != FB_BLANK_UNBLANK) + if (!fbcon_is_active(vc, info) || par->blank_state != FB_BLANK_UNBLANK) fbcon_del_cursor_work(info); else fbcon_add_cursor_work(info); set_blitting_type(vc, info); - ops->cursor_reset = 1; + par->cursor_reset = 1; - if (ops->rotate_font && ops->rotate_font(info, vc)) { - ops->rotate = FB_ROTATE_UR; + if (par->bitops->rotate_font && par->bitops->rotate_font(info, vc)) { + par->rotate = FB_ROTATE_UR; set_blitting_type(vc, info); } @@ -2181,9 +2246,9 @@ static bool fbcon_switch(struct vc_data *vc) scrollback_max = 0; scrollback_current = 0; - if (!fbcon_is_inactive(vc, info)) { - ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; - ops->update_start(info); + if (fbcon_is_active(vc, info)) { + par->var.xoffset = par->var.yoffset = p->yscroll = 0; + par->bitops->update_start(info); } fbcon_set_palette(vc, color_table); @@ -2192,7 +2257,7 @@ static bool fbcon_switch(struct vc_data *vc) if (logo_shown == FBCON_LOGO_DRAW) { logo_shown = fg_console; - fb_show_logo(info, ops->rotate); + fb_show_logo(info, par->rotate); update_region(vc, vc->vc_origin + vc->vc_size_row * vc->vc_top, vc->vc_size_row * (vc->vc_bottom - @@ -2221,27 +2286,27 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, bool mode_switch) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; if (mode_switch) { struct fb_var_screeninfo var = info->var; - ops->graphics = 1; + par->graphics = 1; if (!blank) { var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE | FB_ACTIVATE_KD_TEXT; fb_set_var(info, &var); - ops->graphics = 0; - ops->var = info->var; + par->graphics = 0; + par->var = info->var; } } - if (!fbcon_is_inactive(vc, info)) { - if (ops->blank_state != blank) { - ops->blank_state = blank; + if (fbcon_is_active(vc, info)) { + if (par->blank_state != blank) { + par->blank_state = blank; fbcon_cursor(vc, !blank); - ops->cursor_flash = (!blank); + par->cursor_flash = (!blank); if (fb_blank(info, blank)) fbcon_generic_blank(vc, info, blank); @@ -2251,8 +2316,7 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, update_screen(vc); } - if (mode_switch || fbcon_is_inactive(vc, info) || - ops->blank_state != FB_BLANK_UNBLANK) + if (mode_switch || !fbcon_is_active(vc, info) || par->blank_state != FB_BLANK_UNBLANK) fbcon_del_cursor_work(info); else fbcon_add_cursor_work(info); @@ -2260,91 +2324,17 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, return false; } -static void fbcon_debug_enter(struct vc_data *vc) -{ - struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; - - ops->save_graphics = ops->graphics; - ops->graphics = 0; - if (info->fbops->fb_debug_enter) - info->fbops->fb_debug_enter(info); - fbcon_set_palette(vc, color_table); -} - -static void fbcon_debug_leave(struct vc_data *vc) -{ - struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; - - ops->graphics = ops->save_graphics; - if (info->fbops->fb_debug_leave) - info->fbops->fb_debug_leave(info); -} - static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch) { - u8 *fontdata = vc->vc_font.data; - u8 *data = font->data; - int i, j; + const struct fbcon_display *p = &fb_display[vc->vc_num]; font->width = vc->vc_font.width; font->height = vc->vc_font.height; if (font->height > vpitch) return -ENOSPC; font->charcount = vc->vc_hi_font_mask ? 512 : 256; - if (!font->data) - return 0; - if (font->width <= 8) { - j = vc->vc_font.height; - if (font->charcount * j > FNTSIZE(fontdata)) - return -EINVAL; - - for (i = 0; i < font->charcount; i++) { - memcpy(data, fontdata, j); - memset(data + j, 0, vpitch - j); - data += vpitch; - fontdata += j; - } - } else if (font->width <= 16) { - j = vc->vc_font.height * 2; - if (font->charcount * j > FNTSIZE(fontdata)) - return -EINVAL; - - for (i = 0; i < font->charcount; i++) { - memcpy(data, fontdata, j); - memset(data + j, 0, 2*vpitch - j); - data += 2*vpitch; - fontdata += j; - } - } else if (font->width <= 24) { - if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata)) - return -EINVAL; - - for (i = 0; i < font->charcount; i++) { - for (j = 0; j < vc->vc_font.height; j++) { - *data++ = fontdata[0]; - *data++ = fontdata[1]; - *data++ = fontdata[2]; - fontdata += sizeof(u32); - } - memset(data, 0, 3 * (vpitch - j)); - data += 3 * (vpitch - j); - } - } else { - j = vc->vc_font.height * 4; - if (font->charcount * j > FNTSIZE(fontdata)) - return -EINVAL; - - for (i = 0; i < font->charcount; i++) { - memcpy(data, fontdata, j); - memset(data + j, 0, 4 * vpitch - j); - data += 4 * vpitch; - fontdata += j; - } - } - return 0; + return font_data_export(p->fontdata, font, vpitch); } /* set/clear vc_hi_font_mask and update vc attrs accordingly */ @@ -2409,20 +2399,20 @@ static void set_vc_hi_font(struct vc_data *vc, bool set) } static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, - const u8 * data, int userfont) + font_data_t *data) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fbcon_display *p = &fb_display[vc->vc_num]; - int resize, ret, old_userfont, old_width, old_height, old_charcount; - u8 *old_data = vc->vc_font.data; + int resize, ret, old_width, old_height, old_charcount; + font_data_t *old_fontdata = p->fontdata; + const u8 *old_data = vc->vc_font.data; - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); - vc->vc_font.data = (void *)(p->fontdata = data); - old_userfont = p->userfont; - if ((p->userfont = userfont)) - REFCOUNT(data)++; + font_data_get(data); + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + p->fontdata = data; + vc->vc_font.data = font_data_buf(p->fontdata); old_width = vc->vc_font.width; old_height = vc->vc_font.height; old_charcount = vc->vc_font.charcount; @@ -2438,8 +2428,8 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, if (resize) { int cols, rows; - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); cols /= w; rows /= h; ret = vc_resize(vc, cols, rows); @@ -2451,24 +2441,20 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, update_screen(vc); } - if (old_userfont && (--REFCOUNT(old_data) == 0)) - kfree(old_data - FONT_EXTRA_WORDS * sizeof(int)); + if (old_fontdata) + font_data_put(old_fontdata); + return 0; err_out: - p->fontdata = old_data; + p->fontdata = old_fontdata; vc->vc_font.data = old_data; - - if (userfont) { - p->userfont = old_userfont; - if (--REFCOUNT(data) == 0) - kfree(data - FONT_EXTRA_WORDS * sizeof(int)); - } - vc->vc_font.width = old_width; vc->vc_font.height = old_height; vc->vc_font.charcount = old_charcount; + font_data_put(data); + return ret; } @@ -2484,10 +2470,8 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, unsigned charcount = font->charcount; int w = font->width; int h = font->height; - int size; - int i, csum; - u8 *new_data, *data = font->data; - int pitch = PITCH(font->width); + int i, ret; + font_data_t *new_data; /* Is there a reason why fbconsole couldn't handle any charcount >256? * If not this check should be changed to charcount < 256 */ @@ -2511,43 +2495,24 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, if (fbcon_invalid_charcount(info, charcount)) return -EINVAL; - size = CALC_FONTSZ(h, pitch, charcount); - - new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER); - - if (!new_data) - return -ENOMEM; - - memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int)); - - new_data += FONT_EXTRA_WORDS * sizeof(int); - FNTSIZE(new_data) = size; - REFCOUNT(new_data) = 0; /* usage counter */ - for (i=0; i< charcount; i++) { - memcpy(new_data + i*h*pitch, data + i*vpitch*pitch, h*pitch); - } - - /* Since linux has a nice crc32 function use it for counting font - * checksums. */ - csum = crc32(0, new_data, size); + new_data = font_data_import(font, vpitch, crc32); + if (IS_ERR(new_data)) + return PTR_ERR(new_data); - FNTSUM(new_data) = csum; /* Check if the same font is on some other console already */ for (i = first_fb_vc; i <= last_fb_vc; i++) { - struct vc_data *tmp = vc_cons[i].d; - - if (fb_display[i].userfont && - fb_display[i].fontdata && - FNTSUM(fb_display[i].fontdata) == csum && - FNTSIZE(fb_display[i].fontdata) == size && - tmp->vc_font.width == w && - !memcmp(fb_display[i].fontdata, new_data, size)) { - kfree(new_data - FONT_EXTRA_WORDS * sizeof(int)); - new_data = (u8 *)fb_display[i].fontdata; + if (fb_display[i].fontdata && + font_data_is_equal(fb_display[i].fontdata, new_data)) { + font_data_get(fb_display[i].fontdata); + font_data_put(new_data); + new_data = fb_display[i].fontdata; break; } } - return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1); + ret = fbcon_do_set_font(vc, font->width, font->height, charcount, new_data); + font_data_put(new_data); + + return ret; } static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, @@ -2564,7 +2529,7 @@ static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, font->width = f->width; font->height = f->height; - return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0); + return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data); } static u16 palette_red[16]; @@ -2581,7 +2546,7 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) int i, j, k, depth; u8 val; - if (fbcon_is_inactive(vc, info)) + if (!fbcon_is_active(vc, info)) return; if (!con_is_visible(vc)) @@ -2631,11 +2596,11 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) void fbcon_suspended(struct fb_info *info) { struct vc_data *vc = NULL; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - if (!ops || ops->currcon < 0) + if (!par || par->currcon < 0) return; - vc = vc_cons[ops->currcon].d; + vc = vc_cons[par->currcon].d; /* Clear cursor, restore saved data */ fbcon_cursor(vc, false); @@ -2644,27 +2609,27 @@ void fbcon_suspended(struct fb_info *info) void fbcon_resumed(struct fb_info *info) { struct vc_data *vc; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - if (!ops || ops->currcon < 0) + if (!par || par->currcon < 0) return; - vc = vc_cons[ops->currcon].d; + vc = vc_cons[par->currcon].d; update_screen(vc); } static void fbcon_modechanged(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct vc_data *vc; struct fbcon_display *p; int rows, cols; - if (!ops || ops->currcon < 0) + if (!par || par->currcon < 0) return; - vc = vc_cons[ops->currcon].d; + vc = vc_cons[par->currcon].d; if (vc->vc_mode != KD_TEXT || - fbcon_info_from_console(ops->currcon) != info) + fbcon_info_from_console(par->currcon) != info) return; p = &fb_display[vc->vc_num]; @@ -2672,8 +2637,8 @@ static void fbcon_modechanged(struct fb_info *info) if (con_is_visible(vc)) { var_to_display(p, &info->var, info); - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; vc_resize(vc, cols, rows); @@ -2681,9 +2646,9 @@ static void fbcon_modechanged(struct fb_info *info) scrollback_max = 0; scrollback_current = 0; - if (!fbcon_is_inactive(vc, info)) { - ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; - ops->update_start(info); + if (fbcon_is_active(vc, info)) { + par->var.xoffset = par->var.yoffset = p->yscroll = 0; + par->bitops->update_start(info); } fbcon_set_palette(vc, color_table); @@ -2693,12 +2658,12 @@ static void fbcon_modechanged(struct fb_info *info) static void fbcon_set_all_vcs(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct vc_data *vc; struct fbcon_display *p; int i, rows, cols, fg = -1; - if (!ops || ops->currcon < 0) + if (!par || par->currcon < 0) return; for (i = first_fb_vc; i <= last_fb_vc; i++) { @@ -2715,8 +2680,8 @@ static void fbcon_set_all_vcs(struct fb_info *info) p = &fb_display[vc->vc_num]; set_blitting_type(vc, info); var_to_display(p, &info->var, info); - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; vc_resize(vc, cols, rows); @@ -2739,13 +2704,13 @@ EXPORT_SYMBOL(fbcon_update_vcs); /* let fbcon check if it supports a new screen resolution */ int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct vc_data *vc; unsigned int i; WARN_CONSOLE_UNLOCKED(); - if (!ops) + if (!par) return 0; /* prevent setting a screen size which is smaller than font size */ @@ -2790,6 +2755,25 @@ int fbcon_mode_deleted(struct fb_info *info, return found; } +static void fbcon_delete_mode(struct fb_videomode *m) +{ + struct fbcon_display *p; + + for (int i = first_fb_vc; i <= last_fb_vc; i++) { + p = &fb_display[i]; + if (p->mode == m) + p->mode = NULL; + } +} + +void fbcon_delete_modelist(struct list_head *head) +{ + struct fb_modelist *modelist; + + list_for_each_entry(modelist, head, list) + fbcon_delete_mode(&modelist->mode); +} + #ifdef CONFIG_VT_HW_CONSOLE_BINDING static void fbcon_unbind(void) { @@ -2799,7 +2783,7 @@ static void fbcon_unbind(void) fbcon_is_default); if (!ret) - fbcon_has_console_bind = 0; + fbcon_has_console_bind = false; } #else static inline void fbcon_unbind(void) {} @@ -2860,6 +2844,9 @@ void fbcon_fb_unregistered(struct fb_info *info) console_lock(); + if (info->device && dev_is_pci(info->device)) + vga_switcheroo_client_fb_set(to_pci_dev(info->device), NULL); + fbcon_registered_fb[info->node] = NULL; fbcon_num_registered_fb--; @@ -2993,6 +2980,10 @@ static int do_fb_registered(struct fb_info *info) } } + /* Set the fb info for vga_switcheroo clients. Does nothing otherwise. */ + if (info->device && dev_is_pci(info->device)) + vga_switcheroo_client_fb_set(to_pci_dev(info->device), info); + return ret; } @@ -3017,15 +3008,14 @@ int fbcon_fb_registered(struct fb_info *info) void fbcon_fb_blanked(struct fb_info *info, int blank) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct vc_data *vc; - if (!ops || ops->currcon < 0) + if (!par || par->currcon < 0) return; - vc = vc_cons[ops->currcon].d; - if (vc->vc_mode != KD_TEXT || - fbcon_info_from_console(ops->currcon) != info) + vc = vc_cons[par->currcon].d; + if (vc->vc_mode != KD_TEXT || fbcon_info_from_console(par->currcon) != info) return; if (con_is_visible(vc)) { @@ -3034,7 +3024,7 @@ void fbcon_fb_blanked(struct fb_info *info, int blank) else do_unblank_screen(0); } - ops->blank_state = blank; + par->blank_state = blank; } void fbcon_new_modelist(struct fb_info *info) @@ -3153,11 +3143,9 @@ static const struct consw fb_con = { .con_set_palette = fbcon_set_palette, .con_invert_region = fbcon_invert_region, .con_resize = fbcon_resize, - .con_debug_enter = fbcon_debug_enter, - .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 +3167,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 +3189,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,11 +3208,11 @@ 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; - struct fbcon_ops *ops; + struct fbcon_par *par; int idx, blink = -1; console_lock(); @@ -3234,24 +3222,25 @@ static ssize_t show_cursor_blink(struct device *device, goto err; info = fbcon_registered_fb[idx]; - ops = info->fbcon_par; + par = info->fbcon_par; - if (!ops) + if (!par) goto err; - blink = delayed_work_pending(&ops->cursor_work); + blink = delayed_work_pending(&par->cursor_work); err: console_unlock(); 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) { struct fb_info *info; - int blink, idx; char **last = NULL; + bool blink; + int idx; console_lock(); idx = con2fb_map[fg_console]; @@ -3267,10 +3256,10 @@ static ssize_t store_cursor_blink(struct device *device, blink = simple_strtoul(buf, last, 0); if (blink) { - fbcon_cursor_noblink = 0; + fbcon_cursor_blink = true; fbcon_add_cursor_work(info); } else { - fbcon_cursor_noblink = 1; + fbcon_cursor_blink = false; fbcon_del_cursor_work(info); } @@ -3279,35 +3268,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]); - - if (error) - break; - } +static DEVICE_ATTR_RW(cursor_blink); +static DEVICE_ATTR_RW(rotate); +static DEVICE_ATTR_WO(rotate_all); - if (error) { - while (--i >= 0) - device_remove_file(fbcon_device, &device_attrs[i]); - - fbcon_has_sysfs = 0; - } +static struct attribute *fbcon_device_attrs[] = { + &dev_attr_cursor_blink.attr, + &dev_attr_rotate.attr, + &dev_attr_rotate_all.attr, + NULL +}; - return 0; -} +ATTRIBUTE_GROUPS(fbcon_device); #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER static void fbcon_register_existing_fbs(struct work_struct *work) @@ -3365,16 +3337,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 +3357,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 +3369,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..321cc7f44baa 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -11,6 +11,7 @@ #ifndef _VIDEO_FBCON_H #define _VIDEO_FBCON_H +#include <linux/font.h> #include <linux/types.h> #include <linux/vt_buffer.h> #include <linux/vt_kern.h> @@ -25,12 +26,10 @@ struct fbcon_display { /* Filled in by the low-level console driver */ - const u_char *fontdata; - int userfont; /* != 0 if fontdata kmalloc()ed */ + font_data_t *fontdata; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION u_short scrollmode; /* Scroll Method, use fb_scrollmode() */ #endif - u_short inverse; /* != 0 text black on white as default */ short yscroll; /* Hardware scrolling */ int vrows; /* number of virtual rows */ int cursor_shape; @@ -51,11 +50,11 @@ struct fbcon_display { const struct fb_videomode *mode; }; -struct fbcon_ops { +struct fbcon_bitops { 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); @@ -65,6 +64,9 @@ struct fbcon_ops { bool enable, int fg, int bg); int (*update_start)(struct fb_info *info); int (*rotate_font)(struct fb_info *info, struct vc_data *vc); +}; + +struct fbcon_par { struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ struct delayed_work cursor_work; /* Cursor timer */ struct fb_cursor cursor_state; @@ -76,17 +78,23 @@ struct fbcon_ops { int cursor_reset; int blank_state; int graphics; - int save_graphics; /* for debug enter/leave */ bool initialized; int rotate; - int cur_rotate; char *cursor_data; - u8 *fontbuffer; - u8 *fontdata; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION + struct { + font_data_t *fontdata; /* source font */ + u8 *buf; /* rotated glyphs */ + size_t bufsize; + int buf_rotate; /* rotation of buf */ + } rotated; +#endif u8 *cursor_src; u32 cursor_size; - u32 fd_size; + + const struct fbcon_bitops *bitops; }; + /* * Attribute Decoding */ @@ -106,7 +114,6 @@ struct fbcon_ops { ((s) & 0x400) #define attr_blink(s) \ ((s) & 0x8000) - static inline int mono_col(const struct fb_info *info) { @@ -116,42 +123,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 */ @@ -222,9 +193,11 @@ static inline u_short fb_scrollmode(struct fbcon_display *fb) #ifdef CONFIG_FB_TILEBLITTING extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); #endif -extern void fbcon_set_bitops(struct fbcon_ops *ops); +extern void fbcon_set_bitops_ur(struct fbcon_par *par); extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor); +void fbcon_fill_cursor_mask(struct fbcon_par *par, struct vc_data *vc, unsigned char *mask); + #define FBCON_ATTRIBUTE_UNDERLINE 1 #define FBCON_ATTRIBUTE_REVERSE 2 #define FBCON_ATTRIBUTE_BOLD 4 @@ -260,10 +233,4 @@ static inline int get_attribute(struct fb_info *info, u16 c) (void) (&_r == &_v); \ (i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; }) -#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION -extern void fbcon_set_rotate(struct fbcon_ops *ops); -#else -#define fbcon_set_rotate(x) do {} while(0) -#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */ - #endif /* _VIDEO_FBCON_H */ diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c index f9b794ff7d39..33f02d579e02 100644 --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/fb.h> +#include <linux/font.h> #include <linux/vt_kern.h> #include <linux/console.h> #include <asm/types.h> @@ -26,7 +27,7 @@ static void ccw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; - int width = (vc->vc_font.height + 7) >> 3; + int width = font_glyph_pitch(vc->vc_font.height); int mod = vc->vc_font.height % 8; u8 c, msk = ~(0xff << offset), msk1 = 0; @@ -63,9 +64,9 @@ static void ccw_update_attr(u8 *dst, u8 *src, int attribute, static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy, int sx, int dy, int dx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fb_copyarea area; - u32 vyres = GETVYRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); area.sx = sy * vc->vc_font.height; area.sy = vyres - ((sx + width) * vc->vc_font.width); @@ -78,14 +79,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 fbcon_par *par = info->fbcon_par; struct fb_fillrect region; - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - u32 vyres = GETVYRES(ops->p, info); + u32 vyres = GETVYRES(par->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; @@ -100,13 +100,13 @@ static inline void ccw_putcs_aligned(struct vc_data *vc, struct fb_info *info, u32 d_pitch, u32 s_pitch, u32 cellsize, struct fb_image *image, u8 *buf, u8 *dst) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - u32 idx = (vc->vc_font.height + 7) >> 3; + u32 idx = font_glyph_pitch(vc->vc_font.height); u8 *src; while (cnt--) { - src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize; + src = par->rotated.buf + (scr_readw(s--) & charmask) * cellsize; if (attr) { ccw_update_attr(buf, src, attr, vc); @@ -131,8 +131,8 @@ static void ccw_putcs(struct vc_data *vc, struct fb_info *info, int fg, int bg) { struct fb_image image; - struct fbcon_ops *ops = info->fbcon_par; - u32 width = (vc->vc_font.height + 7)/8; + struct fbcon_par *par = info->fbcon_par; + u32 width = font_glyph_pitch(vc->vc_font.height); u32 cellsize = width * vc->vc_font.width; u32 maxcnt = info->pixmap.size/cellsize; u32 scan_align = info->pixmap.scan_align - 1; @@ -140,9 +140,9 @@ static void ccw_putcs(struct vc_data *vc, struct fb_info *info, u32 cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vyres = GETVYRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; image.fg_color = fg; @@ -222,28 +222,29 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - int w = (vc->vc_font.height + 7) >> 3, c; - int y = real_y(ops->p, vc->state.y); + int w = font_glyph_pitch(vc->vc_font.height); + int c; + int y = real_y(par->p, vc->state.y); int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vyres = GETVYRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; cursor.set = 0; c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); + src = par->rotated.buf + ((c & charmask) * (w * vc->vc_font.width)); - if (ops->cursor_state.image.data != src || - ops->cursor_reset) { - ops->cursor_state.image.data = src; - cursor.set |= FB_CUR_SETIMAGE; + if (par->cursor_state.image.data != src || + par->cursor_reset) { + par->cursor_state.image.data = src; + cursor.set |= FB_CUR_SETIMAGE; } if (attribute) { @@ -252,116 +253,84 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, dst = kmalloc_array(w, vc->vc_font.width, GFP_ATOMIC); if (!dst) return; - kfree(ops->cursor_data); - ops->cursor_data = dst; + kfree(par->cursor_data); + par->cursor_data = dst; ccw_update_attr(dst, src, attribute, vc); src = dst; } - if (ops->cursor_state.image.fg_color != fg || - ops->cursor_state.image.bg_color != bg || - ops->cursor_reset) { - ops->cursor_state.image.fg_color = fg; - ops->cursor_state.image.bg_color = bg; + if (par->cursor_state.image.fg_color != fg || + par->cursor_state.image.bg_color != bg || + par->cursor_reset) { + par->cursor_state.image.fg_color = fg; + par->cursor_state.image.bg_color = bg; cursor.set |= FB_CUR_SETCMAP; } - if (ops->cursor_state.image.height != vc->vc_font.width || - ops->cursor_state.image.width != vc->vc_font.height || - ops->cursor_reset) { - ops->cursor_state.image.height = vc->vc_font.width; - ops->cursor_state.image.width = vc->vc_font.height; + if (par->cursor_state.image.height != vc->vc_font.width || + par->cursor_state.image.width != vc->vc_font.height || + par->cursor_reset) { + par->cursor_state.image.height = vc->vc_font.width; + par->cursor_state.image.width = vc->vc_font.height; cursor.set |= FB_CUR_SETSIZE; } dx = y * vc->vc_font.height; dy = vyres - ((vc->state.x + 1) * vc->vc_font.width); - if (ops->cursor_state.image.dx != dx || - ops->cursor_state.image.dy != dy || - ops->cursor_reset) { - ops->cursor_state.image.dx = dx; - ops->cursor_state.image.dy = dy; + if (par->cursor_state.image.dx != dx || + par->cursor_state.image.dy != dy || + par->cursor_reset) { + par->cursor_state.image.dx = dx; + par->cursor_state.image.dy = dy; cursor.set |= FB_CUR_SETPOS; } - if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || - ops->cursor_reset) { - ops->cursor_state.hot.x = cursor.hot.y = 0; + if (par->cursor_state.hot.x || par->cursor_state.hot.y || + par->cursor_reset) { + par->cursor_state.hot.x = cursor.hot.y = 0; cursor.set |= FB_CUR_SETHOT; } if (cursor.set & FB_CUR_SETSIZE || - vc->vc_cursor_type != ops->p->cursor_shape || - ops->cursor_state.mask == NULL || - ops->cursor_reset) { - char *tmp, *mask = kmalloc_array(w, vc->vc_font.width, - GFP_ATOMIC); - int cur_height, size, i = 0; - int width = (vc->vc_font.width + 7)/8; - - if (!mask) - return; + vc->vc_cursor_type != par->p->cursor_shape || + par->cursor_state.mask == NULL || + par->cursor_reset) { + unsigned char *tmp, *mask; - tmp = kmalloc_array(width, vc->vc_font.height, GFP_ATOMIC); + tmp = kmalloc_array(vc->vc_font.height, vc_font_pitch(&vc->vc_font), GFP_ATOMIC); + if (!tmp) + return; + fbcon_fill_cursor_mask(par, vc, tmp); - if (!tmp) { - kfree(mask); + mask = kmalloc_array(vc->vc_font.width, w, GFP_ATOMIC); + if (!mask) { + kfree(tmp); return; } + font_glyph_rotate_270(tmp, vc->vc_font.width, vc->vc_font.height, mask); + kfree(tmp); - kfree(ops->cursor_state.mask); - ops->cursor_state.mask = mask; + kfree(par->cursor_state.mask); + par->cursor_state.mask = (const char *)mask; - ops->p->cursor_shape = vc->vc_cursor_type; + par->p->cursor_shape = vc->vc_cursor_type; cursor.set |= FB_CUR_SETSHAPE; - - switch (CUR_SIZE(ops->p->cursor_shape)) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; - } - - size = (vc->vc_font.height - cur_height) * width; - while (size--) - tmp[i++] = 0; - size = cur_height * width; - while (size--) - tmp[i++] = 0xff; - memset(mask, 0, w * vc->vc_font.width); - rotate_ccw(tmp, mask, vc->vc_font.width, vc->vc_font.height); - kfree(tmp); } - ops->cursor_state.enable = enable && !use_sw; + par->cursor_state.enable = enable && !use_sw; cursor.image.data = src; - cursor.image.fg_color = ops->cursor_state.image.fg_color; - cursor.image.bg_color = ops->cursor_state.image.bg_color; - cursor.image.dx = ops->cursor_state.image.dx; - cursor.image.dy = ops->cursor_state.image.dy; - cursor.image.height = ops->cursor_state.image.height; - cursor.image.width = ops->cursor_state.image.width; - cursor.hot.x = ops->cursor_state.hot.x; - cursor.hot.y = ops->cursor_state.hot.y; - cursor.mask = ops->cursor_state.mask; - cursor.enable = ops->cursor_state.enable; + cursor.image.fg_color = par->cursor_state.image.fg_color; + cursor.image.bg_color = par->cursor_state.image.bg_color; + cursor.image.dx = par->cursor_state.image.dx; + cursor.image.dy = par->cursor_state.image.dy; + cursor.image.height = par->cursor_state.image.height; + cursor.image.width = par->cursor_state.image.width; + cursor.hot.x = par->cursor_state.hot.x; + cursor.hot.y = par->cursor_state.hot.y; + cursor.mask = par->cursor_state.mask; + cursor.enable = par->cursor_state.enable; cursor.image.depth = 1; cursor.rop = ROP_XOR; @@ -371,32 +340,37 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, if (err) soft_cursor(info, &cursor); - ops->cursor_reset = 0; + par->cursor_reset = 0; } static int ccw_update_start(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u32 yoffset; - u32 vyres = GETVYRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); int err; - yoffset = (vyres - info->var.yres) - ops->var.xoffset; - ops->var.xoffset = ops->var.yoffset; - ops->var.yoffset = yoffset; - err = fb_pan_display(info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + yoffset = (vyres - info->var.yres) - par->var.xoffset; + par->var.xoffset = par->var.yoffset; + par->var.yoffset = yoffset; + err = fb_pan_display(info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode; return err; } -void fbcon_rotate_ccw(struct fbcon_ops *ops) +static const struct fbcon_bitops ccw_fbcon_bitops = { + .bmove = ccw_bmove, + .clear = ccw_clear, + .putcs = ccw_putcs, + .clear_margins = ccw_clear_margins, + .cursor = ccw_cursor, + .update_start = ccw_update_start, + .rotate_font = fbcon_rotate_font, +}; + +void fbcon_set_bitops_ccw(struct fbcon_par *par) { - ops->bmove = ccw_bmove; - ops->clear = ccw_clear; - ops->putcs = ccw_putcs; - ops->clear_margins = ccw_clear_margins; - ops->cursor = ccw_cursor; - ops->update_start = ccw_update_start; + par->bitops = &ccw_fbcon_bitops; } diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c index 903f6fc174e1..bde820967eb9 100644 --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/fb.h> +#include <linux/font.h> #include <linux/vt_kern.h> #include <linux/console.h> #include <asm/types.h> @@ -26,7 +27,7 @@ static void cw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; - int width = (vc->vc_font.height + 7) >> 3; + int width = font_glyph_pitch(vc->vc_font.height); u8 c, msk = ~(0xff >> offset); for (i = 0; i < vc->vc_font.width; i++) { @@ -48,9 +49,9 @@ static void cw_update_attr(u8 *dst, u8 *src, int attribute, static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy, int sx, int dy, int dx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fb_copyarea area; - u32 vxres = GETVXRES(ops->p, info); + u32 vxres = GETVXRES(par->p, info); area.sx = vxres - ((sy + height) * vc->vc_font.height); area.sy = sx * vc->vc_font.width; @@ -63,14 +64,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 fbcon_par *par = info->fbcon_par; struct fb_fillrect region; - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - u32 vxres = GETVXRES(ops->p, info); + u32 vxres = GETVXRES(par->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; @@ -85,13 +85,13 @@ static inline void cw_putcs_aligned(struct vc_data *vc, struct fb_info *info, u32 d_pitch, u32 s_pitch, u32 cellsize, struct fb_image *image, u8 *buf, u8 *dst) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - u32 idx = (vc->vc_font.height + 7) >> 3; + u32 idx = font_glyph_pitch(vc->vc_font.height); u8 *src; while (cnt--) { - src = ops->fontbuffer + (scr_readw(s++) & charmask)*cellsize; + src = par->rotated.buf + (scr_readw(s++) & charmask) * cellsize; if (attr) { cw_update_attr(buf, src, attr, vc); @@ -116,8 +116,8 @@ static void cw_putcs(struct vc_data *vc, struct fb_info *info, int fg, int bg) { struct fb_image image; - struct fbcon_ops *ops = info->fbcon_par; - u32 width = (vc->vc_font.height + 7)/8; + struct fbcon_par *par = info->fbcon_par; + u32 width = font_glyph_pitch(vc->vc_font.height); u32 cellsize = width * vc->vc_font.width; u32 maxcnt = info->pixmap.size/cellsize; u32 scan_align = info->pixmap.scan_align - 1; @@ -125,9 +125,9 @@ static void cw_putcs(struct vc_data *vc, struct fb_info *info, u32 cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vxres = GETVXRES(ops->p, info); + u32 vxres = GETVXRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; image.fg_color = fg; @@ -205,28 +205,29 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - int w = (vc->vc_font.height + 7) >> 3, c; - int y = real_y(ops->p, vc->state.y); + int w = font_glyph_pitch(vc->vc_font.height); + int c; + int y = real_y(par->p, vc->state.y); int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vxres = GETVXRES(ops->p, info); + u32 vxres = GETVXRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; cursor.set = 0; c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); + src = par->rotated.buf + ((c & charmask) * (w * vc->vc_font.width)); - if (ops->cursor_state.image.data != src || - ops->cursor_reset) { - ops->cursor_state.image.data = src; - cursor.set |= FB_CUR_SETIMAGE; + if (par->cursor_state.image.data != src || + par->cursor_reset) { + par->cursor_state.image.data = src; + cursor.set |= FB_CUR_SETIMAGE; } if (attribute) { @@ -235,116 +236,84 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, dst = kmalloc_array(w, vc->vc_font.width, GFP_ATOMIC); if (!dst) return; - kfree(ops->cursor_data); - ops->cursor_data = dst; + kfree(par->cursor_data); + par->cursor_data = dst; cw_update_attr(dst, src, attribute, vc); src = dst; } - if (ops->cursor_state.image.fg_color != fg || - ops->cursor_state.image.bg_color != bg || - ops->cursor_reset) { - ops->cursor_state.image.fg_color = fg; - ops->cursor_state.image.bg_color = bg; + if (par->cursor_state.image.fg_color != fg || + par->cursor_state.image.bg_color != bg || + par->cursor_reset) { + par->cursor_state.image.fg_color = fg; + par->cursor_state.image.bg_color = bg; cursor.set |= FB_CUR_SETCMAP; } - if (ops->cursor_state.image.height != vc->vc_font.width || - ops->cursor_state.image.width != vc->vc_font.height || - ops->cursor_reset) { - ops->cursor_state.image.height = vc->vc_font.width; - ops->cursor_state.image.width = vc->vc_font.height; + if (par->cursor_state.image.height != vc->vc_font.width || + par->cursor_state.image.width != vc->vc_font.height || + par->cursor_reset) { + par->cursor_state.image.height = vc->vc_font.width; + par->cursor_state.image.width = vc->vc_font.height; cursor.set |= FB_CUR_SETSIZE; } dx = vxres - ((y * vc->vc_font.height) + vc->vc_font.height); dy = vc->state.x * vc->vc_font.width; - if (ops->cursor_state.image.dx != dx || - ops->cursor_state.image.dy != dy || - ops->cursor_reset) { - ops->cursor_state.image.dx = dx; - ops->cursor_state.image.dy = dy; + if (par->cursor_state.image.dx != dx || + par->cursor_state.image.dy != dy || + par->cursor_reset) { + par->cursor_state.image.dx = dx; + par->cursor_state.image.dy = dy; cursor.set |= FB_CUR_SETPOS; } - if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || - ops->cursor_reset) { - ops->cursor_state.hot.x = cursor.hot.y = 0; + if (par->cursor_state.hot.x || par->cursor_state.hot.y || + par->cursor_reset) { + par->cursor_state.hot.x = cursor.hot.y = 0; cursor.set |= FB_CUR_SETHOT; } if (cursor.set & FB_CUR_SETSIZE || - vc->vc_cursor_type != ops->p->cursor_shape || - ops->cursor_state.mask == NULL || - ops->cursor_reset) { - char *tmp, *mask = kmalloc_array(w, vc->vc_font.width, - GFP_ATOMIC); - int cur_height, size, i = 0; - int width = (vc->vc_font.width + 7)/8; - - if (!mask) - return; + vc->vc_cursor_type != par->p->cursor_shape || + par->cursor_state.mask == NULL || + par->cursor_reset) { + unsigned char *tmp, *mask; - tmp = kmalloc_array(width, vc->vc_font.height, GFP_ATOMIC); + tmp = kmalloc_array(vc->vc_font.height, vc_font_pitch(&vc->vc_font), GFP_ATOMIC); + if (!tmp) + return; + fbcon_fill_cursor_mask(par, vc, tmp); - if (!tmp) { - kfree(mask); + mask = kmalloc_array(vc->vc_font.width, w, GFP_ATOMIC); + if (!mask) { + kfree(tmp); return; } + font_glyph_rotate_90(tmp, vc->vc_font.width, vc->vc_font.height, mask); + kfree(tmp); - kfree(ops->cursor_state.mask); - ops->cursor_state.mask = mask; + kfree(par->cursor_state.mask); + par->cursor_state.mask = (const char *)mask; - ops->p->cursor_shape = vc->vc_cursor_type; + par->p->cursor_shape = vc->vc_cursor_type; cursor.set |= FB_CUR_SETSHAPE; - - switch (CUR_SIZE(ops->p->cursor_shape)) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; - } - - size = (vc->vc_font.height - cur_height) * width; - while (size--) - tmp[i++] = 0; - size = cur_height * width; - while (size--) - tmp[i++] = 0xff; - memset(mask, 0, w * vc->vc_font.width); - rotate_cw(tmp, mask, vc->vc_font.width, vc->vc_font.height); - kfree(tmp); } - ops->cursor_state.enable = enable && !use_sw; + par->cursor_state.enable = enable && !use_sw; cursor.image.data = src; - cursor.image.fg_color = ops->cursor_state.image.fg_color; - cursor.image.bg_color = ops->cursor_state.image.bg_color; - cursor.image.dx = ops->cursor_state.image.dx; - cursor.image.dy = ops->cursor_state.image.dy; - cursor.image.height = ops->cursor_state.image.height; - cursor.image.width = ops->cursor_state.image.width; - cursor.hot.x = ops->cursor_state.hot.x; - cursor.hot.y = ops->cursor_state.hot.y; - cursor.mask = ops->cursor_state.mask; - cursor.enable = ops->cursor_state.enable; + cursor.image.fg_color = par->cursor_state.image.fg_color; + cursor.image.bg_color = par->cursor_state.image.bg_color; + cursor.image.dx = par->cursor_state.image.dx; + cursor.image.dy = par->cursor_state.image.dy; + cursor.image.height = par->cursor_state.image.height; + cursor.image.width = par->cursor_state.image.width; + cursor.hot.x = par->cursor_state.hot.x; + cursor.hot.y = par->cursor_state.hot.y; + cursor.mask = par->cursor_state.mask; + cursor.enable = par->cursor_state.enable; cursor.image.depth = 1; cursor.rop = ROP_XOR; @@ -354,32 +323,37 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, if (err) soft_cursor(info, &cursor); - ops->cursor_reset = 0; + par->cursor_reset = 0; } static int cw_update_start(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; - u32 vxres = GETVXRES(ops->p, info); + struct fbcon_par *par = info->fbcon_par; + u32 vxres = GETVXRES(par->p, info); u32 xoffset; int err; - xoffset = vxres - (info->var.xres + ops->var.yoffset); - ops->var.yoffset = ops->var.xoffset; - ops->var.xoffset = xoffset; - err = fb_pan_display(info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + xoffset = vxres - (info->var.xres + par->var.yoffset); + par->var.yoffset = par->var.xoffset; + par->var.xoffset = xoffset; + err = fb_pan_display(info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode; return err; } -void fbcon_rotate_cw(struct fbcon_ops *ops) +static const struct fbcon_bitops cw_fbcon_bitops = { + .bmove = cw_bmove, + .clear = cw_clear, + .putcs = cw_putcs, + .clear_margins = cw_clear_margins, + .cursor = cw_cursor, + .update_start = cw_update_start, + .rotate_font = fbcon_rotate_font, +}; + +void fbcon_set_bitops_cw(struct fbcon_par *par) { - ops->bmove = cw_bmove; - ops->clear = cw_clear; - ops->putcs = cw_putcs; - ops->clear_margins = cw_clear_margins; - ops->cursor = cw_cursor; - ops->update_start = cw_update_start; + par->bitops = &cw_fbcon_bitops; } diff --git a/drivers/video/fbdev/core/fbcon_rotate.c b/drivers/video/fbdev/core/fbcon_rotate.c index ec3c883400f7..6cdbc96eeca6 100644 --- a/drivers/video/fbdev/core/fbcon_rotate.c +++ b/drivers/video/fbdev/core/fbcon_rotate.c @@ -8,104 +8,45 @@ * more details. */ -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/string.h> +#include <linux/errno.h> #include <linux/fb.h> -#include <linux/vt_kern.h> -#include <linux/console.h> -#include <asm/types.h> +#include <linux/font.h> + #include "fbcon.h" #include "fbcon_rotate.h" -static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc) +int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc) { - struct fbcon_ops *ops = info->fbcon_par; - int len, err = 0; - int s_cellsize, d_cellsize, i; - const u8 *src; - u8 *dst; - - if (vc->vc_font.data == ops->fontdata && - ops->p->con_rotate == ops->cur_rotate) - goto finished; + struct fbcon_par *par = info->fbcon_par; + unsigned char *buf; + int ret; - src = ops->fontdata = vc->vc_font.data; - ops->cur_rotate = ops->p->con_rotate; - len = vc->vc_font.charcount; - s_cellsize = ((vc->vc_font.width + 7)/8) * - vc->vc_font.height; - d_cellsize = s_cellsize; + if (par->p->fontdata == par->rotated.fontdata && par->rotate == par->rotated.buf_rotate) + return 0; - if (ops->rotate == FB_ROTATE_CW || - ops->rotate == FB_ROTATE_CCW) - d_cellsize = ((vc->vc_font.height + 7)/8) * - vc->vc_font.width; + par->rotated.fontdata = par->p->fontdata; + par->rotated.buf_rotate = par->rotate; if (info->fbops->fb_sync) info->fbops->fb_sync(info); - if (ops->fd_size < d_cellsize * len) { - dst = kmalloc_array(len, d_cellsize, GFP_KERNEL); - - if (dst == NULL) { - err = -ENOMEM; - goto finished; - } - - ops->fd_size = d_cellsize * len; - kfree(ops->fontbuffer); - ops->fontbuffer = dst; + buf = font_data_rotate(par->rotated.fontdata, vc->vc_font.width, + vc->vc_font.height, vc->vc_font.charcount, + par->rotated.buf_rotate, par->rotated.buf, + &par->rotated.bufsize); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + goto err_kfree; } - dst = ops->fontbuffer; - memset(dst, 0, ops->fd_size); - - switch (ops->rotate) { - case FB_ROTATE_UD: - for (i = len; i--; ) { - rotate_ud(src, dst, vc->vc_font.width, - vc->vc_font.height); + par->rotated.buf = buf; - src += s_cellsize; - dst += d_cellsize; - } - break; - case FB_ROTATE_CW: - for (i = len; i--; ) { - rotate_cw(src, dst, vc->vc_font.width, - vc->vc_font.height); - src += s_cellsize; - dst += d_cellsize; - } - break; - case FB_ROTATE_CCW: - for (i = len; i--; ) { - rotate_ccw(src, dst, vc->vc_font.width, - vc->vc_font.height); - src += s_cellsize; - dst += d_cellsize; - } - break; - } + return 0; -finished: - return err; -} +err_kfree: + kfree(par->rotated.buf); + par->rotated.buf = NULL; /* clear here to avoid output */ + par->rotated.bufsize = 0; -void fbcon_set_rotate(struct fbcon_ops *ops) -{ - ops->rotate_font = fbcon_rotate_font; - - switch(ops->rotate) { - case FB_ROTATE_CW: - fbcon_rotate_cw(ops); - break; - case FB_ROTATE_UD: - fbcon_rotate_ud(ops); - break; - case FB_ROTATE_CCW: - fbcon_rotate_ccw(ops); - break; - } + return ret; } diff --git a/drivers/video/fbdev/core/fbcon_rotate.h b/drivers/video/fbdev/core/fbcon_rotate.h index 01cbe303b8a2..725bcae2df61 100644 --- a/drivers/video/fbdev/core/fbcon_rotate.h +++ b/drivers/video/fbdev/core/fbcon_rotate.h @@ -19,78 +19,19 @@ (fb_scrollmode(s) == SCROLL_REDRAW || fb_scrollmode(s) == SCROLL_MOVE || !(i)->fix.xpanstep) ? \ (i)->var.xres : (i)->var.xres_virtual; }) +int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc); + +#if defined(CONFIG_FRAMEBUFFER_CONSOLE_ROTATION) +void fbcon_set_bitops_cw(struct fbcon_par *par); +void fbcon_set_bitops_ud(struct fbcon_par *par); +void fbcon_set_bitops_ccw(struct fbcon_par *par); +#else +static inline void fbcon_set_bitops_cw(struct fbcon_par *par) +{ } +static inline void fbcon_set_bitops_ud(struct fbcon_par *par) +{ } +static inline void fbcon_set_bitops_ccw(struct fbcon_par *par) +{ } +#endif -static inline int pattern_test_bit(u32 x, u32 y, u32 pitch, const char *pat) -{ - u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8; - - pat +=index; - return (*pat) & (0x80 >> bit); -} - -static inline void pattern_set_bit(u32 x, u32 y, u32 pitch, char *pat) -{ - u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8; - - pat += index; - - (*pat) |= 0x80 >> bit; -} - -static inline void rotate_ud(const char *in, char *out, u32 width, u32 height) -{ - int i, j; - int shift = (8 - (width % 8)) & 7; - - width = (width + 7) & ~7; - - for (i = 0; i < height; i++) { - for (j = 0; j < width - shift; j++) { - if (pattern_test_bit(j, i, width, in)) - pattern_set_bit(width - (1 + j + shift), - height - (1 + i), - width, out); - } - - } -} - -static inline void rotate_cw(const char *in, char *out, u32 width, u32 height) -{ - int i, j, h = height, w = width; - int shift = (8 - (height % 8)) & 7; - - width = (width + 7) & ~7; - height = (height + 7) & ~7; - - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (pattern_test_bit(j, i, width, in)) - pattern_set_bit(height - 1 - i - shift, j, - height, out); - - } - } -} - -static inline void rotate_ccw(const char *in, char *out, u32 width, u32 height) -{ - int i, j, h = height, w = width; - int shift = (8 - (width % 8)) & 7; - - width = (width + 7) & ~7; - height = (height + 7) & ~7; - - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (pattern_test_bit(j, i, width, in)) - pattern_set_bit(i, width - 1 - j - shift, - height, out); - } - } -} - -extern void fbcon_rotate_cw(struct fbcon_ops *ops); -extern void fbcon_rotate_ud(struct fbcon_ops *ops); -extern void fbcon_rotate_ccw(struct fbcon_ops *ops); #endif diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c index 594331936fd3..eaf08999e249 100644 --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -26,7 +26,7 @@ static void ud_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; - int width = (vc->vc_font.width + 7) >> 3; + int width = font_glyph_pitch(vc->vc_font.width); unsigned int cellsize = vc->vc_font.height * width; u8 c; @@ -48,10 +48,10 @@ static void ud_update_attr(u8 *dst, u8 *src, int attribute, static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy, int sx, int dy, int dx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; struct fb_copyarea area; - u32 vyres = GETVYRES(ops->p, info); - u32 vxres = GETVXRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); + u32 vxres = GETVXRES(par->p, info); area.sy = vyres - ((sy + height) * vc->vc_font.height); area.sx = vxres - ((sx + width) * vc->vc_font.width); @@ -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 fbcon_par *par = 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); + u32 vyres = GETVYRES(par->p, info); + u32 vxres = GETVXRES(par->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; @@ -87,13 +86,13 @@ static inline void ud_putcs_aligned(struct vc_data *vc, struct fb_info *info, u32 d_pitch, u32 s_pitch, u32 cellsize, struct fb_image *image, u8 *buf, u8 *dst) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; u32 idx = vc->vc_font.width >> 3; u8 *src; while (cnt--) { - src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize; + src = par->rotated.buf + (scr_readw(s--) & charmask) * cellsize; if (attr) { ud_update_attr(buf, src, attr, vc); @@ -120,7 +119,7 @@ static inline void ud_putcs_unaligned(struct vc_data *vc, struct fb_image *image, u8 *buf, u8 *dst) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; u32 shift_low = 0, mod = vc->vc_font.width % 8; u32 shift_high = 8; @@ -128,7 +127,7 @@ static inline void ud_putcs_unaligned(struct vc_data *vc, u8 *src; while (cnt--) { - src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize; + src = par->rotated.buf + (scr_readw(s--) & charmask) * cellsize; if (attr) { ud_update_attr(buf, src, attr, vc); @@ -153,8 +152,8 @@ static void ud_putcs(struct vc_data *vc, struct fb_info *info, int fg, int bg) { struct fb_image image; - struct fbcon_ops *ops = info->fbcon_par; - u32 width = (vc->vc_font.width + 7)/8; + struct fbcon_par *par = info->fbcon_par; + u32 width = font_glyph_pitch(vc->vc_font.width); u32 cellsize = width * vc->vc_font.height; u32 maxcnt = info->pixmap.size/cellsize; u32 scan_align = info->pixmap.scan_align - 1; @@ -162,10 +161,10 @@ static void ud_putcs(struct vc_data *vc, struct fb_info *info, u32 mod = vc->vc_font.width % 8, cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vyres = GETVYRES(ops->p, info); - u32 vxres = GETVXRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); + u32 vxres = GETVXRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; image.fg_color = fg; @@ -252,29 +251,30 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - int w = (vc->vc_font.width + 7) >> 3, c; - int y = real_y(ops->p, vc->state.y); + int w = font_glyph_pitch(vc->vc_font.width); + int c; + int y = real_y(par->p, vc->state.y); int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vyres = GETVYRES(ops->p, info); - u32 vxres = GETVXRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); + u32 vxres = GETVXRES(par->p, info); - if (!ops->fontbuffer) + if (!par->rotated.buf) return; cursor.set = 0; c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height)); + src = par->rotated.buf + ((c & charmask) * (w * vc->vc_font.height)); - if (ops->cursor_state.image.data != src || - ops->cursor_reset) { - ops->cursor_state.image.data = src; - cursor.set |= FB_CUR_SETIMAGE; + if (par->cursor_state.image.data != src || + par->cursor_reset) { + par->cursor_state.image.data = src; + cursor.set |= FB_CUR_SETIMAGE; } if (attribute) { @@ -283,108 +283,84 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, bool enable, dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC); if (!dst) return; - kfree(ops->cursor_data); - ops->cursor_data = dst; + kfree(par->cursor_data); + par->cursor_data = dst; ud_update_attr(dst, src, attribute, vc); src = dst; } - if (ops->cursor_state.image.fg_color != fg || - ops->cursor_state.image.bg_color != bg || - ops->cursor_reset) { - ops->cursor_state.image.fg_color = fg; - ops->cursor_state.image.bg_color = bg; + if (par->cursor_state.image.fg_color != fg || + par->cursor_state.image.bg_color != bg || + par->cursor_reset) { + par->cursor_state.image.fg_color = fg; + par->cursor_state.image.bg_color = bg; cursor.set |= FB_CUR_SETCMAP; } - if (ops->cursor_state.image.height != vc->vc_font.height || - ops->cursor_state.image.width != vc->vc_font.width || - ops->cursor_reset) { - ops->cursor_state.image.height = vc->vc_font.height; - ops->cursor_state.image.width = vc->vc_font.width; + if (par->cursor_state.image.height != vc->vc_font.height || + par->cursor_state.image.width != vc->vc_font.width || + par->cursor_reset) { + par->cursor_state.image.height = vc->vc_font.height; + par->cursor_state.image.width = vc->vc_font.width; cursor.set |= FB_CUR_SETSIZE; } dy = vyres - ((y * vc->vc_font.height) + vc->vc_font.height); dx = vxres - ((vc->state.x * vc->vc_font.width) + vc->vc_font.width); - if (ops->cursor_state.image.dx != dx || - ops->cursor_state.image.dy != dy || - ops->cursor_reset) { - ops->cursor_state.image.dx = dx; - ops->cursor_state.image.dy = dy; + if (par->cursor_state.image.dx != dx || + par->cursor_state.image.dy != dy || + par->cursor_reset) { + par->cursor_state.image.dx = dx; + par->cursor_state.image.dy = dy; cursor.set |= FB_CUR_SETPOS; } - if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || - ops->cursor_reset) { - ops->cursor_state.hot.x = cursor.hot.y = 0; + if (par->cursor_state.hot.x || par->cursor_state.hot.y || + par->cursor_reset) { + par->cursor_state.hot.x = cursor.hot.y = 0; cursor.set |= FB_CUR_SETHOT; } if (cursor.set & FB_CUR_SETSIZE || - vc->vc_cursor_type != ops->p->cursor_shape || - ops->cursor_state.mask == NULL || - ops->cursor_reset) { - char *mask = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC); - int cur_height, size, i = 0; - u8 msk = 0xff; - - if (!mask) - return; - - kfree(ops->cursor_state.mask); - ops->cursor_state.mask = mask; + vc->vc_cursor_type != par->p->cursor_shape || + par->cursor_state.mask == NULL || + par->cursor_reset) { + unsigned char *tmp, *mask; - ops->p->cursor_shape = vc->vc_cursor_type; - cursor.set |= FB_CUR_SETSHAPE; + tmp = kmalloc_array(vc->vc_font.height, w, GFP_ATOMIC); + if (!tmp) + return; + fbcon_fill_cursor_mask(par, vc, tmp); - switch (CUR_SIZE(ops->p->cursor_shape)) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; + mask = kmalloc_array(vc->vc_font.height, w, GFP_ATOMIC); + if (!mask) { + kfree(tmp); + return; } + font_glyph_rotate_180(tmp, vc->vc_font.width, vc->vc_font.height, mask); + kfree(tmp); - size = cur_height * w; - - while (size--) - mask[i++] = msk; + kfree(par->cursor_state.mask); + par->cursor_state.mask = (const char *)mask; - size = (vc->vc_font.height - cur_height) * w; - - while (size--) - mask[i++] = ~msk; + par->p->cursor_shape = vc->vc_cursor_type; + cursor.set |= FB_CUR_SETSHAPE; } - ops->cursor_state.enable = enable && !use_sw; + par->cursor_state.enable = enable && !use_sw; cursor.image.data = src; - cursor.image.fg_color = ops->cursor_state.image.fg_color; - cursor.image.bg_color = ops->cursor_state.image.bg_color; - cursor.image.dx = ops->cursor_state.image.dx; - cursor.image.dy = ops->cursor_state.image.dy; - cursor.image.height = ops->cursor_state.image.height; - cursor.image.width = ops->cursor_state.image.width; - cursor.hot.x = ops->cursor_state.hot.x; - cursor.hot.y = ops->cursor_state.hot.y; - cursor.mask = ops->cursor_state.mask; - cursor.enable = ops->cursor_state.enable; + cursor.image.fg_color = par->cursor_state.image.fg_color; + cursor.image.bg_color = par->cursor_state.image.bg_color; + cursor.image.dx = par->cursor_state.image.dx; + cursor.image.dy = par->cursor_state.image.dy; + cursor.image.height = par->cursor_state.image.height; + cursor.image.width = par->cursor_state.image.width; + cursor.hot.x = par->cursor_state.hot.x; + cursor.hot.y = par->cursor_state.hot.y; + cursor.mask = par->cursor_state.mask; + cursor.enable = par->cursor_state.enable; cursor.image.depth = 1; cursor.rop = ROP_XOR; @@ -394,36 +370,41 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, bool enable, if (err) soft_cursor(info, &cursor); - ops->cursor_reset = 0; + par->cursor_reset = 0; } static int ud_update_start(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int xoffset, yoffset; - u32 vyres = GETVYRES(ops->p, info); - u32 vxres = GETVXRES(ops->p, info); + u32 vyres = GETVYRES(par->p, info); + u32 vxres = GETVXRES(par->p, info); int err; - xoffset = vxres - info->var.xres - ops->var.xoffset; - yoffset = vyres - info->var.yres - ops->var.yoffset; + xoffset = vxres - info->var.xres - par->var.xoffset; + yoffset = vyres - info->var.yres - par->var.yoffset; if (yoffset < 0) yoffset += vyres; - ops->var.xoffset = xoffset; - ops->var.yoffset = yoffset; - err = fb_pan_display(info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + par->var.xoffset = xoffset; + par->var.yoffset = yoffset; + err = fb_pan_display(info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode; return err; } -void fbcon_rotate_ud(struct fbcon_ops *ops) +static const struct fbcon_bitops ud_fbcon_bitops = { + .bmove = ud_bmove, + .clear = ud_clear, + .putcs = ud_putcs, + .clear_margins = ud_clear_margins, + .cursor = ud_cursor, + .update_start = ud_update_start, + .rotate_font = fbcon_rotate_font, +}; + +void fbcon_set_bitops_ud(struct fbcon_par *par) { - ops->bmove = ud_bmove; - ops->clear = ud_clear; - ops->putcs = ud_putcs; - ops->clear_margins = ud_clear_margins; - ops->cursor = ud_cursor; - ops->update_start = ud_update_start; + par->bitops = &ud_fbcon_bitops; } diff --git a/drivers/video/fbdev/core/fbcvt.c b/drivers/video/fbdev/core/fbcvt.c index 64843464c661..cd3821bd82e5 100644 --- a/drivers/video/fbdev/core/fbcvt.c +++ b/drivers/video/fbdev/core/fbcvt.c @@ -312,7 +312,7 @@ int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb) cvt.f_refresh = cvt.refresh; cvt.interlace = 1; - if (!cvt.xres || !cvt.yres || !cvt.refresh) { + if (!cvt.xres || !cvt.yres || !cvt.refresh || cvt.f_refresh > INT_MAX) { printk(KERN_INFO "fbcvt: Invalid input parameters\n"); return 1; } diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 3c568cff2913..30f2b59c47bf 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -15,6 +15,8 @@ #include <linux/export.h> #include <linux/fb.h> #include <linux/fbcon.h> +#include <linux/lcd.h> +#include <linux/leds.h> #include <video/nomodeset.h> @@ -89,16 +91,16 @@ EXPORT_SYMBOL(fb_get_color_depth); /* * Data padding functions. */ -void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height) +void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 s_pitch, u32 height) { __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height); } EXPORT_SYMBOL(fb_pad_aligned_buffer); -void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height, - u32 shift_high, u32 shift_low, u32 mod) +void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 idx, u32 height, + u32 shift_high, u32 shift_low, u32 mod) { - u8 mask = (u8) (0xfff << shift_high), tmp; + u8 mask = (u8) (0xff << shift_high), tmp; int i, j; for (i = height; i--; ) { @@ -220,6 +222,12 @@ static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, return err; } +static void fb_lcd_notify_mode_change(struct fb_info *info, + struct fb_videomode *mode) +{ + lcd_notify_mode_change_all(info->device, mode->xres, mode->yres); +} + int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) { @@ -227,7 +235,6 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) u32 activate; struct fb_var_screeninfo old_var; struct fb_videomode mode; - struct fb_event event; u32 unused; if (var->activate & FB_ACTIVATE_INV_MODE) { @@ -328,35 +335,76 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) !list_empty(&info->modelist)) ret = fb_add_videomode(&mode, &info->modelist); - if (ret) + if (ret) { + info->var = old_var; return ret; + } - event.info = info; - event.data = &mode; - fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event); + fb_lcd_notify_mode_change(info, &mode); return 0; } EXPORT_SYMBOL(fb_set_var); -int -fb_blank(struct fb_info *info, int blank) +static void fb_lcd_notify_blank(struct fb_info *info) +{ + int power; + + switch (info->blank) { + case FB_BLANK_UNBLANK: + power = LCD_POWER_ON; + break; + /* deprecated; TODO: should become 'off' */ + case FB_BLANK_NORMAL: + power = LCD_POWER_REDUCED; + break; + case FB_BLANK_VSYNC_SUSPEND: + power = LCD_POWER_REDUCED_VSYNC_SUSPEND; + break; + /* 'off' */ + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + default: + power = LCD_POWER_OFF; + break; + } + + lcd_notify_blank_all(info->device, power); +} + +static void fb_ledtrig_backlight_notify_blank(struct fb_info *info) +{ + if (info->blank == FB_BLANK_UNBLANK) + ledtrig_backlight_blank(false); + else + ledtrig_backlight_blank(true); +} + +int fb_blank(struct fb_info *info, int blank) { - struct fb_event event; - int ret = -EINVAL; + int old_blank = info->blank; + int ret; + + if (!info->fbops->fb_blank) + return -EINVAL; if (blank > FB_BLANK_POWERDOWN) blank = FB_BLANK_POWERDOWN; - event.info = info; - event.data = ␣ + info->blank = blank; - if (info->fbops->fb_blank) - ret = info->fbops->fb_blank(blank, info); + ret = info->fbops->fb_blank(blank, info); + if (ret) + goto err; - if (!ret) - fb_notifier_call_chain(FB_EVENT_BLANK, &event); + fb_bl_notify_blank(info, old_blank); + fb_lcd_notify_blank(info); + fb_ledtrig_backlight_notify_blank(info); + return 0; + +err: + info->blank = old_blank; return ret; } EXPORT_SYMBOL(fb_blank); @@ -388,7 +436,7 @@ static int fb_check_foreignness(struct fb_info *fi) static int do_register_framebuffer(struct fb_info *fb_info) { - int i; + int i, err = 0; struct fb_videomode mode; if (fb_check_foreignness(fb_info)) @@ -397,15 +445,34 @@ static int do_register_framebuffer(struct fb_info *fb_info) if (num_registered_fb == FB_MAX) return -ENXIO; - num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; + + if (i >= FB_MAX) + return -ENXIO; + + if (!fb_info->modelist.prev || !fb_info->modelist.next) + INIT_LIST_HEAD(&fb_info->modelist); + + fb_var_to_videomode(&mode, &fb_info->var); + err = fb_add_videomode(&mode, &fb_info->modelist); + if (err < 0) + return err; + fb_info->node = i; refcount_set(&fb_info->count, 1); mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); + /* + * With an fb_blank callback present, we assume that the + * display is blank, so that fb_blank() enables it on the + * first modeset. + */ + if (fb_info->fbops->fb_blank) + fb_info->blank = FB_BLANK_POWERDOWN; + fb_device_create(fb_info); if (fb_info->pixmap.addr == NULL) { @@ -426,16 +493,12 @@ static int do_register_framebuffer(struct fb_info *fb_info) if (bitmap_empty(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT)) bitmap_fill(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); - if (!fb_info->modelist.prev || !fb_info->modelist.next) - INIT_LIST_HEAD(&fb_info->modelist); - if (fb_info->skip_vt_switch) pm_vt_switch_required(fb_info->device, false); else pm_vt_switch_required(fb_info->device, true); - fb_var_to_videomode(&mode, &fb_info->var); - fb_add_videomode(&mode, &fb_info->modelist); + num_registered_fb++; registered_fb[i] = fb_info; #ifdef CONFIG_GUMSTIX_AM200EPD @@ -481,6 +544,7 @@ static void do_unregister_framebuffer(struct fb_info *fb_info) fb_info->pixmap.addr = NULL; } + fbcon_delete_modelist(&fb_info->modelist); fb_destroy_modelist(&fb_info->modelist); registered_fb[fb_info->node] = NULL; num_registered_fb--; diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index 0a26399dbc89..f96b4e7b7f21 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -26,13 +26,18 @@ * for more details. * */ + +#include <linux/export.h> #include <linux/fb.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/slab.h> -#include <video/edid.h> +#include <linux/string_choices.h> +#include <linux/sysfb.h> + #include <video/of_videomode.h> #include <video/videomode.h> + #include "../edid.h" /* @@ -318,9 +323,9 @@ static void get_dpms_capabilities(unsigned char flags, if (flags & DPMS_STANDBY) specs->dpms |= FB_DPMS_STANDBY; DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n", - (flags & DPMS_ACTIVE_OFF) ? "yes" : "no", - (flags & DPMS_SUSPEND) ? "yes" : "no", - (flags & DPMS_STANDBY) ? "yes" : "no"); + str_yes_no(flags & DPMS_ACTIVE_OFF), + str_yes_no(flags & DPMS_SUSPEND), + str_yes_no(flags & DPMS_STANDBY)); } static void get_chroma(unsigned char *block, struct fb_monspecs *specs) @@ -383,7 +388,7 @@ static void calc_mode_timings(int xres, int yres, int refresh, { struct fb_var_screeninfo *var; - var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); + var = kzalloc_obj(struct fb_var_screeninfo); if (var) { var->xres = xres; @@ -621,7 +626,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, int num = 0, i, first = 1; int ver, rev; - mode = kcalloc(50, sizeof(struct fb_videomode), GFP_KERNEL); + mode = kzalloc_objs(struct fb_videomode, 50); if (mode == NULL) return NULL; @@ -672,7 +677,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, } *dbsize = num; - m = kmalloc_array(num, sizeof(struct fb_videomode), GFP_KERNEL); + m = kmalloc_objs(struct fb_videomode, num); if (!m) return mode; memmove(m, mode, num * sizeof(struct fb_videomode)); @@ -1219,7 +1224,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0; - timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL); + timings = kzalloc_obj(struct __fb_timings); if (!timings) return -ENOMEM; @@ -1482,13 +1487,12 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) -EINVAL : 0; } -#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86) - /* * We need to ensure that the EDID block is only returned for * the primary graphics adapter. */ +#if defined(CONFIG_FIRMWARE_EDID) const unsigned char *fb_firmware_edid(struct device *device) { struct pci_dev *dev = NULL; @@ -1502,7 +1506,7 @@ const unsigned char *fb_firmware_edid(struct device *device) res = &dev->resource[PCI_ROM_RESOURCE]; if (res && res->flags & IORESOURCE_ROM_SHADOW) - edid = edid_info.dummy; + edid = sysfb_primary_display.edid.dummy; return edid; } diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 1b3c9958ef5c..baa2bae0fb5b 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -12,8 +12,6 @@ #include "fb_internal.h" -#define FB_SYSFS_FLAG_ATTR 1 - static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { int err; @@ -242,11 +240,11 @@ static ssize_t store_blank(struct device *device, return count; } -static ssize_t show_blank(struct device *device, - struct device_attribute *attr, char *buf) +static ssize_t show_blank(struct device *device, struct device_attribute *attr, char *buf) { -// struct fb_info *fb_info = dev_get_drvdata(device); - return 0; + struct fb_info *fb_info = dev_get_drvdata(device); + + return sysfs_emit(buf, "%d\n", fb_info->blank); } static ssize_t store_console(struct device *device, @@ -416,59 +414,42 @@ 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 int fb_init_device(struct fb_info *fb_info) -{ - int i, error = 0; - - 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]); - 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]); +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, +}; - fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; - } -} +ATTRIBUTE_GROUPS(fb_device); int fb_device_create(struct fb_info *fb_info) { @@ -476,14 +457,13 @@ int fb_device_create(struct fb_info *fb_info) dev_t devt = MKDEV(FB_MAJOR, node); int ret; - fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node); + fb_info->dev = device_create_with_groups(fb_class, fb_info->device, devt, fb_info, + fb_device_groups, "fb%d", node); if (IS_ERR(fb_info->dev)) { /* Not fatal */ ret = PTR_ERR(fb_info->dev); pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret); fb_info->dev = NULL; - } else { - fb_init_device(fb_info); } return 0; @@ -496,7 +476,6 @@ void fb_device_destroy(struct fb_info *fb_info) if (!fb_info->dev) return; - fb_cleanup_device(fb_info); device_destroy(fb_class, devt); fb_info->dev = NULL; } diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 7196b055f2bd..703d0b7aec32 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -11,6 +11,7 @@ * more details. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/fb.h> @@ -1069,8 +1070,7 @@ int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head) } } if (!found) { - modelist = kmalloc(sizeof(struct fb_modelist), - GFP_KERNEL); + modelist = kmalloc_obj(struct fb_modelist); if (!modelist) return -ENOMEM; diff --git a/drivers/video/fbdev/core/softcursor.c b/drivers/video/fbdev/core/softcursor.c index 29e5b21cf373..900788c05915 100644 --- a/drivers/video/fbdev/core/softcursor.c +++ b/drivers/video/fbdev/core/softcursor.c @@ -21,7 +21,7 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; unsigned int scan_align = info->pixmap.scan_align - 1; unsigned int buf_align = info->pixmap.buf_align - 1; unsigned int i, size, dsize, s_pitch, d_pitch; @@ -34,19 +34,19 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor) s_pitch = (cursor->image.width + 7) >> 3; dsize = s_pitch * cursor->image.height; - if (dsize + sizeof(struct fb_image) != ops->cursor_size) { - kfree(ops->cursor_src); - ops->cursor_size = dsize + sizeof(struct fb_image); + if (dsize + sizeof(struct fb_image) != par->cursor_size) { + kfree(par->cursor_src); + par->cursor_size = dsize + sizeof(struct fb_image); - ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC); - if (!ops->cursor_src) { - ops->cursor_size = 0; + par->cursor_src = kmalloc(par->cursor_size, GFP_ATOMIC); + if (!par->cursor_src) { + par->cursor_size = 0; return -ENOMEM; } } - src = ops->cursor_src + sizeof(struct fb_image); - image = (struct fb_image *)ops->cursor_src; + src = par->cursor_src + sizeof(struct fb_image); + image = (struct fb_image *)par->cursor_src; *image = cursor->image; d_pitch = (s_pitch + scan_align) & ~scan_align; diff --git a/drivers/video/fbdev/core/svgalib.c b/drivers/video/fbdev/core/svgalib.c index 821b89a0a645..0e0ce4e024d9 100644 --- a/drivers/video/fbdev/core/svgalib.c +++ b/drivers/video/fbdev/core/svgalib.c @@ -10,6 +10,7 @@ * Some parts are based on David Boucher's viafb (http://davesdomain.org.uk/viafb/) */ +#include <linux/export.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -19,7 +20,6 @@ #include <asm/types.h> #include <asm/io.h> - /* Write a CRT register value spread across multiple registers */ void svga_wcrt_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value) { @@ -31,12 +31,13 @@ void svga_wcrt_multi(void __iomem *regbase, const struct vga_regset *regset, u32 while (bitnum <= regset->highbit) { bitval = 1 << bitnum; regval = regval & ~bitval; - if (value & 1) regval = regval | bitval; - bitnum ++; + if (value & 1) + regval = regval | bitval; + bitnum++; value = value >> 1; } vga_wcrt(regbase, regset->regnum, regval); - regset ++; + regset++; } } @@ -51,12 +52,13 @@ void svga_wseq_multi(void __iomem *regbase, const struct vga_regset *regset, u32 while (bitnum <= regset->highbit) { bitval = 1 << bitnum; regval = regval & ~bitval; - if (value & 1) regval = regval | bitval; - bitnum ++; + if (value & 1) + regval = regval | bitval; + bitnum++; value = value >> 1; } vga_wseq(regbase, regset->regnum, regval); - regset ++; + regset++; } } @@ -66,15 +68,13 @@ static unsigned int svga_regset_size(const struct vga_regset *regset) while (regset->regnum != VGA_REGSET_END_VAL) { count += regset->highbit - regset->lowbit + 1; - regset ++; + regset++; } return 1 << count; } - /* ------------------------------------------------------------------------- */ - /* Set graphics controller registers to sane values */ void svga_set_default_gfx_regs(void __iomem *regbase) { @@ -102,7 +102,7 @@ void svga_set_default_atc_regs(void __iomem *regbase) vga_w(regbase, VGA_ATT_W, 0x00); /* All standard ATC registers (AR00 - AR14) */ - for (count = 0; count <= 0xF; count ++) + for (count = 0; count <= 0xF; count++) svga_wattr(regbase, count, count); svga_wattr(regbase, VGA_ATC_MODE, 0x01); @@ -187,10 +187,8 @@ void svga_dump_var(struct fb_var_screeninfo *var, int node) } #endif /* 0 */ - /* ------------------------------------------------------------------------- */ - void svga_settile(struct fb_info *info, struct fb_tilemap *map) { const u8 *font = map->data; @@ -229,7 +227,7 @@ void svga_tilecopy(struct fb_info *info, struct fb_tilearea *area) ((area->sy == area->dy) && (area->sx > area->dx))) { src = fb + area->sx * colstride + area->sy * rowstride; dst = fb + area->dx * colstride + area->dy * rowstride; - } else { + } else { src = fb + (area->sx + area->width - 1) * colstride + (area->sy + area->height - 1) * rowstride; dst = fb + (area->dx + area->width - 1) * colstride @@ -237,7 +235,7 @@ void svga_tilecopy(struct fb_info *info, struct fb_tilearea *area) colstride = -colstride; rowstride = -rowstride; - } + } for (dy = 0; dy < area->height; dy++) { u16 __iomem *src2 = src; @@ -284,19 +282,19 @@ void svga_tileblit(struct fb_info *info, struct fb_tileblit *blit) u8 __iomem *fb = (u8 __iomem *)info->screen_base; fb += blit->sx * colstride + blit->sy * rowstride; - i=0; - for (dy=0; dy < blit->height; dy ++) { + i = 0; + for (dy = 0; dy < blit->height; dy++) { u8 __iomem *fb2 = fb; - for (dx = 0; dx < blit->width; dx ++) { + for (dx = 0; dx < blit->width; dx++) { fb_writeb(blit->indices[i], fb2); fb_writeb(attr, fb2 + 1); fb2 += colstride; - i ++; - if (i == blit->length) return; + i++; + if (i == blit->length) + return; } fb += rowstride; } - } /* Set cursor in text (tileblit) mode */ @@ -308,15 +306,15 @@ void svga_tilecursor(void __iomem *regbase, struct fb_info *info, struct fb_tile + (cursor->sy + (info->var.yoffset / 16)) * (info->var.xres_virtual / 8); - if (! cursor -> mode) + if (!cursor->mode) return; svga_wcrt_mask(regbase, 0x0A, 0x20, 0x20); /* disable cursor */ - if (cursor -> shape == FB_TILE_CURSOR_NONE) + if (cursor->shape == FB_TILE_CURSOR_NONE) return; - switch (cursor -> shape) { + switch (cursor->shape) { case FB_TILE_CURSOR_UNDERLINE: cs = 0x0d; break; @@ -374,7 +372,6 @@ EXPORT_SYMBOL(svga_get_caps); /* ------------------------------------------------------------------------- */ - /* * Compute PLL settings (M, N, R) * F_VCO = (F_BASE * M) / N @@ -385,7 +382,7 @@ int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u u16 am, an, ar; u32 f_vco, f_current, delta_current, delta_best; - pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int) f_wanted); + pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int)f_wanted); ar = pll->r_max; f_vco = f_wanted << ar; @@ -416,7 +413,7 @@ int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u while ((am <= pll->m_max) && (an <= pll->n_max)) { f_current = (pll->f_base * am) / an; - delta_current = abs_diff (f_current, f_vco); + delta_current = abs_diff(f_current, f_vco); if (delta_current < delta_best) { delta_best = delta_current; @@ -424,58 +421,55 @@ int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u *n = an; } - if (f_current <= f_vco) { - am ++; - } else { - an ++; - } + if (f_current <= f_vco) + am++; + else + an++; } f_current = (pll->f_base * *m) / *n; - pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int) (f_current >> ar), (int) f_current); - pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int) *m, (unsigned int) *n, (unsigned int) *r); + pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int)(f_current >> ar), (int)f_current); + pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int)*m, (unsigned int)*n, (unsigned int)*r); return 0; } - /* ------------------------------------------------------------------------- */ - /* Check CRT timing values */ int svga_check_timings(const struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node) { u32 value; - var->xres = (var->xres+7)&~7; - var->left_margin = (var->left_margin+7)&~7; - var->right_margin = (var->right_margin+7)&~7; - var->hsync_len = (var->hsync_len+7)&~7; + var->xres = (var->xres + 7) & ~7; + var->left_margin = (var->left_margin + 7) & ~7; + var->right_margin = (var->right_margin + 7) & ~7; + var->hsync_len = (var->hsync_len + 7) & ~7; /* Check horizontal total */ value = var->xres + var->left_margin + var->right_margin + var->hsync_len; - if (((value / 8) - 5) >= svga_regset_size (tm->h_total_regs)) + if (((value / 8) - 5) >= svga_regset_size(tm->h_total_regs)) return -EINVAL; /* Check horizontal display and blank start */ value = var->xres; - if (((value / 8) - 1) >= svga_regset_size (tm->h_display_regs)) + if (((value / 8) - 1) >= svga_regset_size(tm->h_display_regs)) return -EINVAL; - if (((value / 8) - 1) >= svga_regset_size (tm->h_blank_start_regs)) + if (((value / 8) - 1) >= svga_regset_size(tm->h_blank_start_regs)) return -EINVAL; /* Check horizontal sync start */ value = var->xres + var->right_margin; - if (((value / 8) - 1) >= svga_regset_size (tm->h_sync_start_regs)) + if (((value / 8) - 1) >= svga_regset_size(tm->h_sync_start_regs)) return -EINVAL; /* Check horizontal blank end (or length) */ value = var->left_margin + var->right_margin + var->hsync_len; - if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_blank_end_regs))) + if ((value == 0) || ((value / 8) >= svga_regset_size(tm->h_blank_end_regs))) return -EINVAL; /* Check horizontal sync end (or length) */ value = var->hsync_len; - if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_sync_end_regs))) + if ((value == 0) || ((value / 8) >= svga_regset_size(tm->h_sync_end_regs))) return -EINVAL; /* Check vertical total */ @@ -497,12 +491,12 @@ int svga_check_timings(const struct svga_timing_regs *tm, struct fb_var_screenin /* Check vertical blank end (or length) */ value = var->upper_margin + var->lower_margin + var->vsync_len; - if ((value == 0) || (value >= svga_regset_size (tm->v_blank_end_regs))) + if ((value == 0) || (value >= svga_regset_size(tm->v_blank_end_regs))) return -EINVAL; /* Check vertical sync end (or length) */ value = var->vsync_len; - if ((value == 0) || (value >= svga_regset_size (tm->v_sync_end_regs))) + if ((value == 0) || (value >= svga_regset_size(tm->v_sync_end_regs))) return -EINVAL; return 0; @@ -596,18 +590,15 @@ void svga_set_timings(void __iomem *regbase, const struct svga_timing_regs *tm, vga_w(regbase, VGA_MIS_W, regval); } - /* ------------------------------------------------------------------------- */ - static inline int match_format(const struct svga_fb_format *frm, struct fb_var_screeninfo *var) { int i = 0; int stored = -EINVAL; - while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL) - { + while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL) { if ((var->bits_per_pixel == frm->bits_per_pixel) && (var->red.length <= frm->red.length) && (var->green.length <= frm->green.length) && @@ -647,7 +638,6 @@ int svga_match_format(const struct svga_fb_format *frm, return i; } - EXPORT_SYMBOL(svga_wcrt_multi); EXPORT_SYMBOL(svga_wseq_multi); diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c index 75e7001e8450..773569bce67c 100644 --- a/drivers/video/fbdev/core/syscopyarea.c +++ b/drivers/video/fbdev/core/syscopyarea.c @@ -1,373 +1,30 @@ +// 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/export.h> #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 - */ - -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; +#ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE +#define FB_REV_PIXELS_IN_BYTE +#endif - 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..12eea3e424bb 100644 --- a/drivers/video/fbdev/core/sysfillrect.c +++ b/drivers/video/fbdev/core/sysfillrect.c @@ -1,328 +1,30 @@ +// 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/export.h> #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..0a5bfd8ad095 100644 --- a/drivers/video/fbdev/core/sysimgblt.c +++ b/drivers/video/fbdev/core/sysimgblt.c @@ -1,339 +1,30 @@ +// 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/export.h> #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..a9db668caf72 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, @@ -118,34 +151,38 @@ static void tile_cursor(struct vc_data *vc, struct fb_info *info, bool enable, static int tile_update_start(struct fb_info *info) { - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; int err; - err = fb_pan_display(info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + err = fb_pan_display(info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode; return err; } +static const struct fbcon_bitops tile_fbcon_bitops = { + .bmove = tile_bmove, + .clear = tile_clear, + .putcs = tile_putcs, + .clear_margins = tile_clear_margins, + .cursor = tile_cursor, + .update_start = tile_update_start, +}; + void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info) { struct fb_tilemap map; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_par *par = info->fbcon_par; - ops->bmove = tile_bmove; - ops->clear = tile_clear; - ops->putcs = tile_putcs; - ops->clear_margins = tile_clear_margins; - ops->cursor = tile_cursor; - ops->update_start = tile_update_start; + par->bitops = &tile_fbcon_bitops; - if (ops->p) { + if (par->p) { map.width = vc->vc_font.width; map.height = vc->vc_font.height; map.depth = 1; map.length = vc->vc_font.charcount; - map.data = ops->p->fontdata; + map.data = par->p->fontdata; info->tileops->fb_settile(info, &map); } } diff --git a/drivers/video/fbdev/cyber2000fb.c b/drivers/video/fbdev/cyber2000fb.c index 986760b90465..2d12f8e96c7e 100644 --- a/drivers/video/fbdev/cyber2000fb.c +++ b/drivers/video/fbdev/cyber2000fb.c @@ -1089,7 +1089,6 @@ void cyber2000fb_enable_extregs(struct cfb_info *cfb) cyber2000_grphw(EXT_FUNC_CTL, old, cfb); } } -EXPORT_SYMBOL(cyber2000fb_enable_extregs); /* * Disable access to the extended registers @@ -1109,41 +1108,6 @@ void cyber2000fb_disable_extregs(struct cfb_info *cfb) else cfb->func_use_count -= 1; } -EXPORT_SYMBOL(cyber2000fb_disable_extregs); - -/* - * Attach a capture/tv driver to the core CyberX0X0 driver. - */ -int cyber2000fb_attach(struct cyberpro_info *info, int idx) -{ - if (int_cfb_info != NULL) { - info->dev = int_cfb_info->fb.device; -#ifdef CONFIG_FB_CYBER2000_I2C - info->i2c = &int_cfb_info->i2c_adapter; -#else - info->i2c = NULL; -#endif - info->regs = int_cfb_info->regs; - info->irq = int_cfb_info->irq; - info->fb = int_cfb_info->fb.screen_base; - info->fb_size = int_cfb_info->fb.fix.smem_len; - info->info = int_cfb_info; - - strscpy(info->dev_name, int_cfb_info->fb.fix.id, - sizeof(info->dev_name)); - } - - return int_cfb_info != NULL; -} -EXPORT_SYMBOL(cyber2000fb_attach); - -/* - * Detach a capture/tv driver from the core CyberX0X0 driver. - */ -void cyber2000fb_detach(int idx) -{ -} -EXPORT_SYMBOL(cyber2000fb_detach); #ifdef CONFIG_FB_CYBER2000_DDC @@ -1399,7 +1363,7 @@ static struct cfb_info *cyberpro_alloc_fb_info(unsigned int id, char *name) { struct cfb_info *cfb; - cfb = kzalloc(sizeof(struct cfb_info), GFP_KERNEL); + cfb = kzalloc_obj(struct cfb_info); if (!cfb) return NULL; diff --git a/drivers/video/fbdev/cyber2000fb.h b/drivers/video/fbdev/cyber2000fb.h index 04641aa13acc..21095df8fdb0 100644 --- a/drivers/video/fbdev/cyber2000fb.h +++ b/drivers/video/fbdev/cyber2000fb.h @@ -488,7 +488,5 @@ struct cyberpro_info { * Note! Writing to the Cyber20x0 registers from an interrupt * routine is definitely a bad idea atm. */ -int cyber2000fb_attach(struct cyberpro_info *info, int idx); -void cyber2000fb_detach(int idx); void cyber2000fb_enable_extregs(struct cfb_info *cfb); void cyber2000fb_disable_extregs(struct cfb_info *cfb); diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 0e1bd3dba255..47ebc0107209 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -15,7 +15,7 @@ #include <linux/fb.h> #include <linux/platform_device.h> #include <linux/printk.h> -#include <linux/screen_info.h> +#include <linux/sysfb.h> #include <video/vga.h> #include <asm/efi.h> #include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */ @@ -345,6 +345,7 @@ ATTRIBUTE_GROUPS(efifb); static int efifb_probe(struct platform_device *dev) { + struct sysfb_display_info *dpy; struct screen_info *si; struct fb_info *info; struct efifb_par *par; @@ -360,10 +361,11 @@ static int efifb_probe(struct platform_device *dev) * driver. We get a copy of the attached screen_info, so that we can * modify its values without affecting later drivers. */ - si = dev_get_platdata(&dev->dev); - if (!si) + dpy = dev_get_platdata(&dev->dev); + if (!dpy) return -ENODEV; - si = devm_kmemdup(&dev->dev, si, sizeof(*si), GFP_KERNEL); + + si = devm_kmemdup(&dev->dev, &dpy->screen, sizeof(*si), GFP_KERNEL); if (!si) return -ENOMEM; diff --git a/drivers/video/fbdev/ffb.c b/drivers/video/fbdev/ffb.c index 34b6abff9493..da531b4cb451 100644 --- a/drivers/video/fbdev/ffb.c +++ b/drivers/video/fbdev/ffb.c @@ -335,6 +335,9 @@ struct ffb_dac { }; #define FFB_DAC_UCTRL 0x1001 /* User Control */ +#define FFB_DAC_UCTRL_OVENAB 0x00000008 /* Overlay Enable */ +#define FFB_DAC_UCTRL_WMODE 0x00000030 /* Window Mode */ +#define FFB_DAC_UCTRL_WM_COMB 0x00000000 /* Window Mode = Combined */ #define FFB_DAC_UCTRL_MANREV 0x00000f00 /* 4-bit Manufacturing Revision */ #define FFB_DAC_UCTRL_MANREV_SHIFT 8 #define FFB_DAC_TGEN 0x6000 /* Timing Generator */ @@ -425,7 +428,7 @@ static void ffb_switch_from_graph(struct ffb_par *par) { struct ffb_fbc __iomem *fbc = par->fbc; struct ffb_dac __iomem *dac = par->dac; - unsigned long flags; + unsigned long flags, uctrl; spin_lock_irqsave(&par->lock, flags); FFBWait(par); @@ -450,6 +453,15 @@ static void ffb_switch_from_graph(struct ffb_par *par) upa_writel((FFB_DAC_CUR_CTRL_P0 | FFB_DAC_CUR_CTRL_P1), &dac->value2); + /* Disable overlay and window modes. */ + upa_writel(FFB_DAC_UCTRL, &dac->type); + uctrl = upa_readl(&dac->value); + uctrl &= ~FFB_DAC_UCTRL_WMODE; + uctrl |= FFB_DAC_UCTRL_WM_COMB; + uctrl &= ~FFB_DAC_UCTRL_OVENAB; + upa_writel(FFB_DAC_UCTRL, &dac->type); + upa_writel(uctrl, &dac->value); + spin_unlock_irqrestore(&par->lock, flags); } 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/gbefb.c b/drivers/video/fbdev/gbefb.c index 4c36a3e409be..cb6ff15a21db 100644 --- a/drivers/video/fbdev/gbefb.c +++ b/drivers/video/fbdev/gbefb.c @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/dma-direct.h> #include <linux/errno.h> #include <linux/gfp.h> #include <linux/fb.h> @@ -65,7 +66,7 @@ struct gbefb_par { static unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024; static void *gbe_mem; static dma_addr_t gbe_dma_addr; -static unsigned long gbe_mem_phys; +static phys_addr_t gbe_mem_phys; static struct { uint16_t *cpu; @@ -1183,7 +1184,7 @@ static int gbefb_probe(struct platform_device *p_dev) goto out_release_mem_region; } - gbe_mem_phys = (unsigned long) gbe_dma_addr; + gbe_mem_phys = dma_to_phys(&p_dev->dev, gbe_dma_addr); } par = info->par; diff --git a/drivers/video/fbdev/geode/display_gx.c b/drivers/video/fbdev/geode/display_gx.c index b5f25dffd274..099322cefce0 100644 --- a/drivers/video/fbdev/geode/display_gx.c +++ b/drivers/video/fbdev/geode/display_gx.c @@ -13,6 +13,7 @@ #include <asm/io.h> #include <asm/div64.h> #include <asm/delay.h> +#include <asm/msr.h> #include <linux/cs5535.h> #include "gxfb.h" diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c index af996634c1a9..8d69be7c9d31 100644 --- a/drivers/video/fbdev/geode/gxfb_core.c +++ b/drivers/video/fbdev/geode/gxfb_core.c @@ -29,6 +29,7 @@ #include <linux/pci.h> #include <linux/cs5535.h> +#include <asm/msr.h> #include <asm/olpc.h> #include "gxfb.h" @@ -377,7 +378,7 @@ static int gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Figure out if this is a TFT or CRT part */ - rdmsrl(MSR_GX_GLD_MSR_CONFIG, val); + rdmsrq(MSR_GX_GLD_MSR_CONFIG, val); if ((val & MSR_GX_GLD_MSR_CONFIG_FP) == MSR_GX_GLD_MSR_CONFIG_FP) par->enable_crt = 0; diff --git a/drivers/video/fbdev/geode/lxfb_ops.c b/drivers/video/fbdev/geode/lxfb_ops.c index 32baaf59fcf7..2e33da9849b0 100644 --- a/drivers/video/fbdev/geode/lxfb_ops.c +++ b/drivers/video/fbdev/geode/lxfb_ops.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/cs5535.h> +#include <asm/msr.h> #include "lxfb.h" /* TODO @@ -358,7 +359,7 @@ void lx_set_mode(struct fb_info *info) /* Set output mode */ - rdmsrl(MSR_LX_GLD_MSR_CONFIG, msrval); + rdmsrq(MSR_LX_GLD_MSR_CONFIG, msrval); msrval &= ~MSR_LX_GLD_MSR_CONFIG_FMT; if (par->output & OUTPUT_PANEL) { @@ -371,7 +372,7 @@ void lx_set_mode(struct fb_info *info) } else msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_CRT; - wrmsrl(MSR_LX_GLD_MSR_CONFIG, msrval); + wrmsrq(MSR_LX_GLD_MSR_CONFIG, msrval); /* Clear the various buffers */ /* FIXME: Adjust for panning here */ @@ -419,7 +420,7 @@ void lx_set_mode(struct fb_info *info) /* Set default watermark values */ - rdmsrl(MSR_LX_SPARE_MSR, msrval); + rdmsrq(MSR_LX_SPARE_MSR, msrval); msrval &= ~(MSR_LX_SPARE_MSR_DIS_CFIFO_HGO | MSR_LX_SPARE_MSR_VFIFO_ARB_SEL @@ -427,7 +428,7 @@ void lx_set_mode(struct fb_info *info) | MSR_LX_SPARE_MSR_WM_LPEN_OVRD); msrval |= MSR_LX_SPARE_MSR_DIS_VIFO_WM | MSR_LX_SPARE_MSR_DIS_INIT_V_PRI; - wrmsrl(MSR_LX_SPARE_MSR, msrval); + wrmsrq(MSR_LX_SPARE_MSR, msrval); gcfg = DC_GENERAL_CFG_DFLE; /* Display fifo enable */ gcfg |= (0x6 << DC_GENERAL_CFG_DFHPSL_SHIFT) | /* default priority */ @@ -591,10 +592,10 @@ static void lx_save_regs(struct lxfb_par *par) } while ((i & GP_BLT_STATUS_PB) || !(i & GP_BLT_STATUS_CE)); /* save MSRs */ - rdmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel); - rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll); - rdmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg); - rdmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare); + rdmsrq(MSR_LX_MSR_PADSEL, par->msr.padsel); + rdmsrq(MSR_GLCP_DOTPLL, par->msr.dotpll); + rdmsrq(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg); + rdmsrq(MSR_LX_SPARE_MSR, par->msr.dcspare); write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); @@ -664,7 +665,7 @@ static void lx_restore_display_ctlr(struct lxfb_par *par) uint32_t filt; int i; - wrmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare); + wrmsrq(MSR_LX_SPARE_MSR, par->msr.dcspare); for (i = 0; i < ARRAY_SIZE(par->dc); i++) { switch (i) { @@ -729,8 +730,8 @@ static void lx_restore_video_proc(struct lxfb_par *par) { int i; - wrmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg); - wrmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel); + wrmsrq(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg); + wrmsrq(MSR_LX_MSR_PADSEL, par->msr.padsel); for (i = 0; i < ARRAY_SIZE(par->vp); i++) { switch (i) { diff --git a/drivers/video/fbdev/geode/suspend_gx.c b/drivers/video/fbdev/geode/suspend_gx.c index 8c49d4e98772..73307f42e343 100644 --- a/drivers/video/fbdev/geode/suspend_gx.c +++ b/drivers/video/fbdev/geode/suspend_gx.c @@ -21,8 +21,8 @@ static void gx_save_regs(struct gxfb_par *par) } while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY)); /* save MSRs */ - rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); - rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll); + rdmsrq(MSR_GX_MSR_PADSEL, par->msr.padsel); + rdmsrq(MSR_GLCP_DOTPLL, par->msr.dotpll); write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); @@ -43,14 +43,14 @@ static void gx_set_dotpll(uint32_t dotpll_hi) uint32_t dotpll_lo; int i; - rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); + rdmsrq(MSR_GLCP_DOTPLL, dotpll_lo); dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET; dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS; wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); /* wait for the PLL to lock */ for (i = 0; i < 200; i++) { - rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); + rdmsrq(MSR_GLCP_DOTPLL, dotpll_lo); if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK) break; udelay(1); @@ -133,7 +133,7 @@ static void gx_restore_video_proc(struct gxfb_par *par) { int i; - wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); + wrmsrq(MSR_GX_MSR_PADSEL, par->msr.padsel); for (i = 0; i < ARRAY_SIZE(par->vp); i++) { switch (i) { diff --git a/drivers/video/fbdev/geode/video_gx.c b/drivers/video/fbdev/geode/video_gx.c index 91dac2aa247c..5717c3356949 100644 --- a/drivers/video/fbdev/geode/video_gx.c +++ b/drivers/video/fbdev/geode/video_gx.c @@ -142,8 +142,8 @@ void gx_set_dclk_frequency(struct fb_info *info) } } - rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); - rdmsrl(MSR_GLCP_DOTPLL, dotpll); + rdmsrq(MSR_GLCP_SYS_RSTPLL, sys_rstpll); + rdmsrq(MSR_GLCP_DOTPLL, dotpll); /* Program new M, N and P. */ dotpll &= 0x00000000ffffffffull; @@ -151,7 +151,7 @@ void gx_set_dclk_frequency(struct fb_info *info) dotpll |= MSR_GLCP_DOTPLL_DOTRESET; dotpll &= ~MSR_GLCP_DOTPLL_BYPASS; - wrmsrl(MSR_GLCP_DOTPLL, dotpll); + wrmsrq(MSR_GLCP_DOTPLL, dotpll); /* Program dividers. */ sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 @@ -159,15 +159,15 @@ void gx_set_dclk_frequency(struct fb_info *info) | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 ); sys_rstpll |= pll_table[best_i].sys_rstpll_bits; - wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); + wrmsrq(MSR_GLCP_SYS_RSTPLL, sys_rstpll); /* Clear reset bit to start PLL. */ dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET); - wrmsrl(MSR_GLCP_DOTPLL, dotpll); + wrmsrq(MSR_GLCP_DOTPLL, dotpll); /* Wait for LOCK bit. */ do { - rdmsrl(MSR_GLCP_DOTPLL, dotpll); + rdmsrq(MSR_GLCP_DOTPLL, dotpll); } while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK)); } @@ -180,10 +180,10 @@ gx_configure_tft(struct fb_info *info) /* Set up the DF pad select MSR */ - rdmsrl(MSR_GX_MSR_PADSEL, val); + rdmsrq(MSR_GX_MSR_PADSEL, val); val &= ~MSR_GX_MSR_PADSEL_MASK; val |= MSR_GX_MSR_PADSEL_TFT; - wrmsrl(MSR_GX_MSR_PADSEL, val); + wrmsrq(MSR_GX_MSR_PADSEL, val); /* Turn off the panel */ diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index 7704f2ab18c0..c9871281bc1d 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -174,13 +174,12 @@ static const struct fb_ops goldfish_fb_ops = { static int goldfish_fb_probe(struct platform_device *pdev) { int ret; - struct resource *r; struct goldfish_fb *fb; size_t framesize; u32 width, height; dma_addr_t fbpaddr; - fb = kzalloc(sizeof(*fb), GFP_KERNEL); + fb = kzalloc_obj(*fb); if (fb == NULL) { ret = -ENOMEM; goto err_fb_alloc_failed; @@ -189,14 +188,9 @@ static int goldfish_fb_probe(struct platform_device *pdev) init_waitqueue_head(&fb->wait); platform_set_drvdata(pdev, fb); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - ret = -ENODEV; - goto err_no_io_base; - } - fb->reg_base = ioremap(r->start, PAGE_SIZE); - if (fb->reg_base == NULL) { - ret = -ENOMEM; + fb->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fb->reg_base)) { + ret = PTR_ERR(fb->reg_base); goto err_no_io_base; } @@ -273,7 +267,6 @@ err_fb_set_var_failed: fb->fb.fix.smem_start); err_alloc_screen_base_failed: err_no_irq: - iounmap(fb->reg_base); err_no_io_base: kfree(fb); err_fb_alloc_failed: @@ -291,7 +284,6 @@ static void goldfish_fb_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base, fb->fb.fix.smem_start); - iounmap(fb->reg_base); kfree(fb); } diff --git a/drivers/video/fbdev/gxt4500.c b/drivers/video/fbdev/gxt4500.c index 15a82c6b609e..1ee0a1efa18e 100644 --- a/drivers/video/fbdev/gxt4500.c +++ b/drivers/video/fbdev/gxt4500.c @@ -704,7 +704,7 @@ static int gxt4500_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } info->var = var; if (gxt4500_set_par(info)) { - printk(KERN_ERR "gxt4500: cannot set video mode\n"); + dev_err(&pdev->dev, "cannot set video mode\n"); goto err_free_cmap; } diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 14418aa3791a..d32fd1c5217c 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -276,7 +276,7 @@ static void hga_blank(int blank_mode) spin_unlock_irqrestore(&hga_reg_lock, flags); } -static int hga_card_detect(void) +static int hga_card_detect(struct platform_device *pdev) { int count = 0; void __iomem *p, *q; @@ -284,6 +284,11 @@ static int hga_card_detect(void) hga_vram_len = 0x08000; + if (!devm_request_mem_region(&pdev->dev, 0xb0000, hga_vram_len, "hgafb")) { + dev_err(&pdev->dev, "cannot reserve video memory at 0xb0000\n"); + return -EBUSY; + } + hga_vram = ioremap(0xb0000, hga_vram_len); if (!hga_vram) return -ENOMEM; @@ -568,7 +573,7 @@ static int hgafb_probe(struct platform_device *pdev) struct fb_info *info; int ret; - ret = hga_card_detect(); + ret = hga_card_detect(pdev); if (ret) return ret; diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c deleted file mode 100644 index 75338ffc703f..000000000000 --- a/drivers/video/fbdev/hyperv_fb.c +++ /dev/null @@ -1,1386 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2012, Microsoft Corporation. - * - * Author: - * Haiyang Zhang <haiyangz@microsoft.com> - */ - -/* - * Hyper-V Synthetic Video Frame Buffer Driver - * - * This is the driver for the Hyper-V Synthetic Video, which supports - * screen resolution up to Full HD 1920x1080 with 32 bit color on Windows - * Server 2012, and 1600x1200 with 16 bit color on Windows Server 2008 R2 - * or earlier. - * - * It also solves the double mouse cursor issue of the emulated video mode. - * - * The default screen resolution is 1152x864, which may be changed by a - * kernel parameter: - * video=hyperv_fb:<width>x<height> - * For example: video=hyperv_fb:1280x1024 - * - * Portrait orientation is also supported: - * For example: video=hyperv_fb:864x1152 - * - * When a Windows 10 RS5+ host is used, the virtual machine screen - * resolution is obtained from the host. The "video=hyperv_fb" option is - * not needed, but still can be used to overwrite what the host specifies. - * The VM resolution on the host could be set by executing the powershell - * "set-vmvideo" command. For example - * set-vmvideo -vmname name -horizontalresolution:1920 \ - * -verticalresolution:1200 -resolutiontype single - * - * Gen 1 VMs also support direct using VM's physical memory for framebuffer. - * It could improve the efficiency and performance for framebuffer and VM. - * This requires to allocate contiguous physical memory from Linux kernel's - * CMA memory allocator. To enable this, supply a kernel parameter to give - * enough memory space to CMA allocator for framebuffer. For example: - * cma=130m - * This gives 130MB memory to CMA allocator that can be allocated to - * framebuffer. For reference, 8K resolution (7680x4320) takes about - * 127MB memory. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/aperture.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/vmalloc.h> -#include <linux/init.h> -#include <linux/completion.h> -#include <linux/fb.h> -#include <linux/pci.h> -#include <linux/panic_notifier.h> -#include <linux/efi.h> -#include <linux/console.h> - -#include <linux/hyperv.h> - -/* Hyper-V Synthetic Video Protocol definitions and structures */ -#define MAX_VMBUS_PKT_SIZE 0x4000 - -#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) -/* Support for VERSION_WIN7 is removed. #define is retained for reference. */ -#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) -#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) -#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) - -#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) -#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) - -#define SYNTHVID_DEPTH_WIN8 32 -#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024) - -enum pipe_msg_type { - PIPE_MSG_INVALID, - PIPE_MSG_DATA, - PIPE_MSG_MAX -}; - -struct pipe_msg_hdr { - u32 type; - u32 size; /* size of message after this field */ -} __packed; - - -enum synthvid_msg_type { - SYNTHVID_ERROR = 0, - SYNTHVID_VERSION_REQUEST = 1, - SYNTHVID_VERSION_RESPONSE = 2, - SYNTHVID_VRAM_LOCATION = 3, - SYNTHVID_VRAM_LOCATION_ACK = 4, - SYNTHVID_SITUATION_UPDATE = 5, - SYNTHVID_SITUATION_UPDATE_ACK = 6, - SYNTHVID_POINTER_POSITION = 7, - SYNTHVID_POINTER_SHAPE = 8, - SYNTHVID_FEATURE_CHANGE = 9, - SYNTHVID_DIRT = 10, - SYNTHVID_RESOLUTION_REQUEST = 13, - SYNTHVID_RESOLUTION_RESPONSE = 14, - - SYNTHVID_MAX = 15 -}; - -#define SYNTHVID_EDID_BLOCK_SIZE 128 -#define SYNTHVID_MAX_RESOLUTION_COUNT 64 - -struct hvd_screen_info { - u16 width; - u16 height; -} __packed; - -struct synthvid_msg_hdr { - u32 type; - u32 size; /* size of this header + payload after this field*/ -} __packed; - -struct synthvid_version_req { - u32 version; -} __packed; - -struct synthvid_version_resp { - u32 version; - u8 is_accepted; - u8 max_video_outputs; -} __packed; - -struct synthvid_supported_resolution_req { - u8 maximum_resolution_count; -} __packed; - -struct synthvid_supported_resolution_resp { - u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; - u8 resolution_count; - u8 default_resolution_index; - u8 is_standard; - struct hvd_screen_info - supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; -} __packed; - -struct synthvid_vram_location { - u64 user_ctx; - u8 is_vram_gpa_specified; - u64 vram_gpa; -} __packed; - -struct synthvid_vram_location_ack { - u64 user_ctx; -} __packed; - -struct video_output_situation { - u8 active; - u32 vram_offset; - u8 depth_bits; - u32 width_pixels; - u32 height_pixels; - u32 pitch_bytes; -} __packed; - -struct synthvid_situation_update { - u64 user_ctx; - u8 video_output_count; - struct video_output_situation video_output[1]; -} __packed; - -struct synthvid_situation_update_ack { - u64 user_ctx; -} __packed; - -struct synthvid_pointer_position { - u8 is_visible; - u8 video_output; - s32 image_x; - s32 image_y; -} __packed; - - -#define CURSOR_MAX_X 96 -#define CURSOR_MAX_Y 96 -#define CURSOR_ARGB_PIXEL_SIZE 4 -#define CURSOR_MAX_SIZE (CURSOR_MAX_X * CURSOR_MAX_Y * CURSOR_ARGB_PIXEL_SIZE) -#define CURSOR_COMPLETE (-1) - -struct synthvid_pointer_shape { - u8 part_idx; - u8 is_argb; - u32 width; /* CURSOR_MAX_X at most */ - u32 height; /* CURSOR_MAX_Y at most */ - u32 hot_x; /* hotspot relative to upper-left of pointer image */ - u32 hot_y; - u8 data[4]; -} __packed; - -struct synthvid_feature_change { - u8 is_dirt_needed; - u8 is_ptr_pos_needed; - u8 is_ptr_shape_needed; - u8 is_situ_needed; -} __packed; - -struct rect { - s32 x1, y1; /* top left corner */ - s32 x2, y2; /* bottom right corner, exclusive */ -} __packed; - -struct synthvid_dirt { - u8 video_output; - u8 dirt_count; - struct rect rect[1]; -} __packed; - -struct synthvid_msg { - struct pipe_msg_hdr pipe_hdr; - struct synthvid_msg_hdr vid_hdr; - union { - struct synthvid_version_req ver_req; - struct synthvid_version_resp ver_resp; - struct synthvid_vram_location vram; - struct synthvid_vram_location_ack vram_ack; - struct synthvid_situation_update situ; - struct synthvid_situation_update_ack situ_ack; - struct synthvid_pointer_position ptr_pos; - struct synthvid_pointer_shape ptr_shape; - struct synthvid_feature_change feature_chg; - struct synthvid_dirt dirt; - struct synthvid_supported_resolution_req resolution_req; - struct synthvid_supported_resolution_resp resolution_resp; - }; -} __packed; - - -/* FB driver definitions and structures */ -#define HVFB_WIDTH 1152 /* default screen width */ -#define HVFB_HEIGHT 864 /* default screen height */ -#define HVFB_WIDTH_MIN 640 -#define HVFB_HEIGHT_MIN 480 - -#define RING_BUFSIZE (256 * 1024) -#define VSP_TIMEOUT (10 * HZ) -#define HVFB_UPDATE_DELAY (HZ / 20) -#define HVFB_ONDEMAND_THROTTLE (HZ / 20) - -struct hvfb_par { - struct fb_info *info; - struct resource *mem; - bool fb_ready; /* fb device is ready */ - struct completion wait; - u32 synthvid_version; - - struct delayed_work dwork; - bool update; - bool update_saved; /* The value of 'update' before hibernation */ - - u32 pseudo_palette[16]; - u8 init_buf[MAX_VMBUS_PKT_SIZE]; - u8 recv_buf[MAX_VMBUS_PKT_SIZE]; - - /* If true, the VSC notifies the VSP on every framebuffer change */ - bool synchronous_fb; - - /* If true, need to copy from deferred IO mem to framebuffer mem */ - bool need_docopy; - - struct notifier_block hvfb_panic_nb; - - /* Memory for deferred IO and frame buffer itself */ - unsigned char *dio_vp; - unsigned char *mmio_vp; - phys_addr_t mmio_pp; - - /* Dirty rectangle, protected by delayed_refresh_lock */ - int x1, y1, x2, y2; - bool delayed_refresh; - spinlock_t delayed_refresh_lock; -}; - -static uint screen_width = HVFB_WIDTH; -static uint screen_height = HVFB_HEIGHT; -static uint screen_depth; -static uint screen_fb_size; -static uint dio_fb_size; /* FB size for deferred IO */ - -static void hvfb_putmem(struct fb_info *info); - -/* Send message to Hyper-V host */ -static inline int synthvid_send(struct hv_device *hdev, - struct synthvid_msg *msg) -{ - static atomic64_t request_id = ATOMIC64_INIT(0); - int ret; - - msg->pipe_hdr.type = PIPE_MSG_DATA; - msg->pipe_hdr.size = msg->vid_hdr.size; - - ret = vmbus_sendpacket(hdev->channel, msg, - msg->vid_hdr.size + sizeof(struct pipe_msg_hdr), - atomic64_inc_return(&request_id), - VM_PKT_DATA_INBAND, 0); - - if (ret) - pr_err_ratelimited("Unable to send packet via vmbus; error %d\n", ret); - - return ret; -} - - -/* Send screen resolution info to host */ -static int synthvid_send_situ(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct synthvid_msg msg; - - if (!info) - return -ENODEV; - - memset(&msg, 0, sizeof(struct synthvid_msg)); - - msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE; - msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_situation_update); - msg.situ.user_ctx = 0; - msg.situ.video_output_count = 1; - msg.situ.video_output[0].active = 1; - msg.situ.video_output[0].vram_offset = 0; - msg.situ.video_output[0].depth_bits = info->var.bits_per_pixel; - msg.situ.video_output[0].width_pixels = info->var.xres; - msg.situ.video_output[0].height_pixels = info->var.yres; - msg.situ.video_output[0].pitch_bytes = info->fix.line_length; - - synthvid_send(hdev, &msg); - - return 0; -} - -/* Send mouse pointer info to host */ -static int synthvid_send_ptr(struct hv_device *hdev) -{ - struct synthvid_msg msg; - - memset(&msg, 0, sizeof(struct synthvid_msg)); - msg.vid_hdr.type = SYNTHVID_POINTER_POSITION; - msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_pointer_position); - msg.ptr_pos.is_visible = 1; - msg.ptr_pos.video_output = 0; - msg.ptr_pos.image_x = 0; - msg.ptr_pos.image_y = 0; - synthvid_send(hdev, &msg); - - memset(&msg, 0, sizeof(struct synthvid_msg)); - msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; - msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_pointer_shape); - msg.ptr_shape.part_idx = CURSOR_COMPLETE; - msg.ptr_shape.is_argb = 1; - msg.ptr_shape.width = 1; - msg.ptr_shape.height = 1; - msg.ptr_shape.hot_x = 0; - msg.ptr_shape.hot_y = 0; - msg.ptr_shape.data[0] = 0; - msg.ptr_shape.data[1] = 1; - msg.ptr_shape.data[2] = 1; - msg.ptr_shape.data[3] = 1; - synthvid_send(hdev, &msg); - - return 0; -} - -/* Send updated screen area (dirty rectangle) location to host */ -static int -synthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2) -{ - struct hv_device *hdev = device_to_hv_device(info->device); - struct synthvid_msg msg; - - memset(&msg, 0, sizeof(struct synthvid_msg)); - if (x2 == INT_MAX) - x2 = info->var.xres; - if (y2 == INT_MAX) - y2 = info->var.yres; - - msg.vid_hdr.type = SYNTHVID_DIRT; - msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_dirt); - msg.dirt.video_output = 0; - msg.dirt.dirt_count = 1; - msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1; - msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1; - msg.dirt.rect[0].x2 = - (x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2; - msg.dirt.rect[0].y2 = - (y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2; - - synthvid_send(hdev, &msg); - - return 0; -} - -static void hvfb_docopy(struct hvfb_par *par, - unsigned long offset, - unsigned long size) -{ - if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready || - size == 0 || offset >= dio_fb_size) - return; - - if (offset + size > dio_fb_size) - size = dio_fb_size - offset; - - memcpy(par->mmio_vp + offset, par->dio_vp + offset, size); -} - -/* Deferred IO callback */ -static void synthvid_deferred_io(struct fb_info *p, struct list_head *pagereflist) -{ - struct hvfb_par *par = p->par; - struct fb_deferred_io_pageref *pageref; - unsigned long start, end; - int y1, y2, miny, maxy; - - miny = INT_MAX; - maxy = 0; - - /* - * Merge dirty pages. It is possible that last page cross - * over the end of frame buffer row yres. This is taken care of - * in synthvid_update function by clamping the y2 - * value to yres. - */ - list_for_each_entry(pageref, pagereflist, list) { - start = pageref->offset; - end = start + PAGE_SIZE - 1; - y1 = start / p->fix.line_length; - y2 = end / p->fix.line_length; - miny = min_t(int, miny, y1); - maxy = max_t(int, maxy, y2); - - /* Copy from dio space to mmio address */ - if (par->fb_ready && par->need_docopy) - hvfb_docopy(par, start, PAGE_SIZE); - } - - if (par->fb_ready && par->update) - synthvid_update(p, 0, miny, p->var.xres, maxy + 1); -} - -static struct fb_deferred_io synthvid_defio = { - .delay = HZ / 20, - .deferred_io = synthvid_deferred_io, -}; - -/* - * Actions on received messages from host: - * Complete the wait event. - * Or, reply with screen and cursor info. - */ -static void synthvid_recv_sub(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par; - struct synthvid_msg *msg; - - if (!info) - return; - - par = info->par; - msg = (struct synthvid_msg *)par->recv_buf; - - /* Complete the wait event */ - if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { - memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE); - complete(&par->wait); - return; - } - - /* Reply with screen and cursor info */ - if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { - if (par->fb_ready) { - synthvid_send_ptr(hdev); - synthvid_send_situ(hdev); - } - - par->update = msg->feature_chg.is_dirt_needed; - if (par->update) - schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); - } -} - -/* Receive callback for messages from the host */ -static void synthvid_receive(void *ctx) -{ - struct hv_device *hdev = ctx; - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par; - struct synthvid_msg *recv_buf; - u32 bytes_recvd; - u64 req_id; - int ret; - - if (!info) - return; - - par = info->par; - recv_buf = (struct synthvid_msg *)par->recv_buf; - - do { - ret = vmbus_recvpacket(hdev->channel, recv_buf, - MAX_VMBUS_PKT_SIZE, - &bytes_recvd, &req_id); - if (bytes_recvd > 0 && - recv_buf->pipe_hdr.type == PIPE_MSG_DATA) - synthvid_recv_sub(hdev); - } while (bytes_recvd > 0 && ret == 0); -} - -/* Check if the ver1 version is equal or greater than ver2 */ -static inline bool synthvid_ver_ge(u32 ver1, u32 ver2) -{ - if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || - (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && - SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) - return true; - - return false; -} - -/* Check synthetic video protocol version with the host */ -static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; - int ret = 0; - unsigned long t; - - memset(msg, 0, sizeof(struct synthvid_msg)); - msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST; - msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_version_req); - msg->ver_req.version = ver; - synthvid_send(hdev, msg); - - t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); - if (!t) { - pr_err("Time out on waiting version response\n"); - ret = -ETIMEDOUT; - goto out; - } - if (!msg->ver_resp.is_accepted) { - ret = -ENODEV; - goto out; - } - - par->synthvid_version = ver; - pr_info("Synthvid Version major %d, minor %d\n", - SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); - -out: - return ret; -} - -/* Get current resolution from the host */ -static int synthvid_get_supported_resolution(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; - int ret = 0; - unsigned long t; - u8 index; - - memset(msg, 0, sizeof(struct synthvid_msg)); - msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; - msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_supported_resolution_req); - - msg->resolution_req.maximum_resolution_count = - SYNTHVID_MAX_RESOLUTION_COUNT; - synthvid_send(hdev, msg); - - t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); - if (!t) { - pr_err("Time out on waiting resolution response\n"); - ret = -ETIMEDOUT; - goto out; - } - - if (msg->resolution_resp.resolution_count == 0) { - pr_err("No supported resolutions\n"); - ret = -ENODEV; - goto out; - } - - index = msg->resolution_resp.default_resolution_index; - if (index >= msg->resolution_resp.resolution_count) { - pr_err("Invalid resolution index: %d\n", index); - ret = -ENODEV; - goto out; - } - - screen_width = - msg->resolution_resp.supported_resolution[index].width; - screen_height = - msg->resolution_resp.supported_resolution[index].height; - -out: - return ret; -} - -/* Connect to VSP (Virtual Service Provider) on host */ -static int synthvid_connect_vsp(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - int ret; - - ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE, - NULL, 0, synthvid_receive, hdev); - if (ret) { - pr_err("Unable to open vmbus channel\n"); - return ret; - } - - /* Negotiate the protocol version with host */ - switch (vmbus_proto_version) { - case VERSION_WIN10: - case VERSION_WIN10_V5: - ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); - if (!ret) - break; - fallthrough; - case VERSION_WIN8: - case VERSION_WIN8_1: - ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8); - break; - default: - ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); - break; - } - - if (ret) { - pr_err("Synthetic video device version not accepted\n"); - goto error; - } - - screen_depth = SYNTHVID_DEPTH_WIN8; - if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) { - ret = synthvid_get_supported_resolution(hdev); - if (ret) - pr_info("Failed to get supported resolution from host, use default\n"); - } - - screen_fb_size = hdev->channel->offermsg.offer. - mmio_megabytes * 1024 * 1024; - - return 0; - -error: - vmbus_close(hdev->channel); - return ret; -} - -/* Send VRAM and Situation messages to the host */ -static int synthvid_send_config(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; - int ret = 0; - unsigned long t; - - /* Send VRAM location */ - memset(msg, 0, sizeof(struct synthvid_msg)); - msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; - msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + - sizeof(struct synthvid_vram_location); - msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp; - msg->vram.is_vram_gpa_specified = 1; - synthvid_send(hdev, msg); - - t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); - if (!t) { - pr_err("Time out on waiting vram location ack\n"); - ret = -ETIMEDOUT; - goto out; - } - if (msg->vram_ack.user_ctx != par->mmio_pp) { - pr_err("Unable to set VRAM location\n"); - ret = -ENODEV; - goto out; - } - - /* Send pointer and situation update */ - synthvid_send_ptr(hdev); - synthvid_send_situ(hdev); - -out: - return ret; -} - - -/* - * Delayed work callback: - * It is scheduled to call whenever update request is received and it has - * not been called in last HVFB_ONDEMAND_THROTTLE time interval. - */ -static void hvfb_update_work(struct work_struct *w) -{ - struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work); - struct fb_info *info = par->info; - unsigned long flags; - int x1, x2, y1, y2; - int j; - - spin_lock_irqsave(&par->delayed_refresh_lock, flags); - /* Reset the request flag */ - par->delayed_refresh = false; - - /* Store the dirty rectangle to local variables */ - x1 = par->x1; - x2 = par->x2; - y1 = par->y1; - y2 = par->y2; - - /* Clear dirty rectangle */ - par->x1 = par->y1 = INT_MAX; - par->x2 = par->y2 = 0; - - spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); - - if (x1 > info->var.xres || x2 > info->var.xres || - y1 > info->var.yres || y2 > info->var.yres || x2 <= x1) - return; - - /* Copy the dirty rectangle to frame buffer memory */ - if (par->need_docopy) - for (j = y1; j < y2; j++) - hvfb_docopy(par, - j * info->fix.line_length + - (x1 * screen_depth / 8), - (x2 - x1) * screen_depth / 8); - - /* Refresh */ - if (par->fb_ready && par->update) - synthvid_update(info, x1, y1, x2, y2); -} - -/* - * Control the on-demand refresh frequency. It schedules a delayed - * screen update if it has not yet. - */ -static void hvfb_ondemand_refresh_throttle(struct hvfb_par *par, - int x1, int y1, int w, int h) -{ - unsigned long flags; - int x2 = x1 + w; - int y2 = y1 + h; - - spin_lock_irqsave(&par->delayed_refresh_lock, flags); - - /* Merge dirty rectangle */ - par->x1 = min_t(int, par->x1, x1); - par->y1 = min_t(int, par->y1, y1); - par->x2 = max_t(int, par->x2, x2); - par->y2 = max_t(int, par->y2, y2); - - /* Schedule a delayed screen update if not yet */ - if (par->delayed_refresh == false) { - schedule_delayed_work(&par->dwork, - HVFB_ONDEMAND_THROTTLE); - par->delayed_refresh = true; - } - - spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); -} - -static int hvfb_on_panic(struct notifier_block *nb, - unsigned long e, void *p) -{ - struct hv_device *hdev; - struct hvfb_par *par; - struct fb_info *info; - - par = container_of(nb, struct hvfb_par, hvfb_panic_nb); - info = par->info; - hdev = device_to_hv_device(info->device); - - if (hv_ringbuffer_spinlock_busy(hdev->channel)) - return NOTIFY_DONE; - - par->synchronous_fb = true; - if (par->need_docopy) - hvfb_docopy(par, 0, dio_fb_size); - synthvid_update(info, 0, 0, INT_MAX, INT_MAX); - - return NOTIFY_DONE; -} - -/* Framebuffer operation handlers */ - -static int hvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) -{ - if (var->xres < HVFB_WIDTH_MIN || var->yres < HVFB_HEIGHT_MIN || - var->xres > screen_width || var->yres > screen_height || - var->bits_per_pixel != screen_depth) - return -EINVAL; - - var->xres_virtual = var->xres; - var->yres_virtual = var->yres; - - return 0; -} - -static int hvfb_set_par(struct fb_info *info) -{ - struct hv_device *hdev = device_to_hv_device(info->device); - - return synthvid_send_situ(hdev); -} - - -static inline u32 chan_to_field(u32 chan, struct fb_bitfield *bf) -{ - return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; -} - -static int hvfb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, struct fb_info *info) -{ - u32 *pal = info->pseudo_palette; - - if (regno > 15) - return -EINVAL; - - pal[regno] = chan_to_field(red, &info->var.red) - | chan_to_field(green, &info->var.green) - | chan_to_field(blue, &info->var.blue) - | chan_to_field(transp, &info->var.transp); - - return 0; -} - -static int hvfb_blank(int blank, struct fb_info *info) -{ - return 1; /* get fb_blank to set the colormap to all black */ -} - -static void hvfb_ops_damage_range(struct fb_info *info, off_t off, size_t len) -{ - /* TODO: implement damage handling */ -} - -static void hvfb_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) -{ - struct hvfb_par *par = info->par; - - if (par->synchronous_fb) - synthvid_update(info, 0, 0, INT_MAX, INT_MAX); - else - hvfb_ondemand_refresh_throttle(par, x, y, width, height); -} - -/* - * fb_ops.fb_destroy is called by the last put_fb_info() call at the end - * of unregister_framebuffer() or fb_release(). Do any cleanup related to - * framebuffer here. - */ -static void hvfb_destroy(struct fb_info *info) -{ - hvfb_putmem(info); - framebuffer_release(info); -} - -/* - * TODO: GEN1 codepaths allocate from system or DMA-able memory. Fix the - * driver to use the _SYSMEM_ or _DMAMEM_ helpers in these cases. - */ -FB_GEN_DEFAULT_DEFERRED_IOMEM_OPS(hvfb_ops, - hvfb_ops_damage_range, - hvfb_ops_damage_area) - -static const struct fb_ops hvfb_ops = { - .owner = THIS_MODULE, - FB_DEFAULT_DEFERRED_OPS(hvfb_ops), - .fb_check_var = hvfb_check_var, - .fb_set_par = hvfb_set_par, - .fb_setcolreg = hvfb_setcolreg, - .fb_blank = hvfb_blank, - .fb_destroy = hvfb_destroy, -}; - -/* Get options from kernel paramenter "video=" */ -static void hvfb_get_option(struct fb_info *info) -{ - struct hvfb_par *par = info->par; - char *opt = NULL, *p; - uint x = 0, y = 0; - - if (fb_get_options(KBUILD_MODNAME, &opt) || !opt || !*opt) - return; - - p = strsep(&opt, "x"); - if (!*p || kstrtouint(p, 0, &x) || - !opt || !*opt || kstrtouint(opt, 0, &y)) { - pr_err("Screen option is invalid: skipped\n"); - return; - } - - if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN || - (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) && - (x * y * screen_depth / 8 > screen_fb_size)) || - (par->synthvid_version == SYNTHVID_VERSION_WIN8 && - x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8)) { - pr_err("Screen resolution option is out of range: skipped\n"); - return; - } - - screen_width = x; - screen_height = y; - return; -} - -/* - * Allocate enough contiguous physical memory. - * Return physical address if succeeded or -1 if failed. - */ -static phys_addr_t hvfb_get_phymem(struct hv_device *hdev, - unsigned int request_size) -{ - struct page *page = NULL; - dma_addr_t dma_handle; - void *vmem; - phys_addr_t paddr = 0; - unsigned int order = get_order(request_size); - - if (request_size == 0) - return -1; - - if (order <= MAX_PAGE_ORDER) { - /* Call alloc_pages if the size is less than 2^MAX_PAGE_ORDER */ - page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); - if (!page) - return -1; - - paddr = (page_to_pfn(page) << PAGE_SHIFT); - } else { - /* Allocate from CMA */ - hdev->device.coherent_dma_mask = DMA_BIT_MASK(64); - - vmem = dma_alloc_coherent(&hdev->device, - round_up(request_size, PAGE_SIZE), - &dma_handle, - GFP_KERNEL | __GFP_NOWARN); - - if (!vmem) - return -1; - - paddr = virt_to_phys(vmem); - } - - return paddr; -} - -/* Release contiguous physical memory */ -static void hvfb_release_phymem(struct device *device, - phys_addr_t paddr, unsigned int size) -{ - unsigned int order = get_order(size); - - if (order <= MAX_PAGE_ORDER) - __free_pages(pfn_to_page(paddr >> PAGE_SHIFT), order); - else - dma_free_coherent(device, - round_up(size, PAGE_SIZE), - phys_to_virt(paddr), - paddr); -} - - -/* Get framebuffer memory from Hyper-V video pci space */ -static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) -{ - struct hvfb_par *par = info->par; - struct pci_dev *pdev = NULL; - void __iomem *fb_virt; - int gen2vm = efi_enabled(EFI_BOOT); - resource_size_t base = 0; - resource_size_t size = 0; - phys_addr_t paddr; - int ret; - - if (!gen2vm) { - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, - PCI_DEVICE_ID_HYPERV_VIDEO, NULL); - if (!pdev) { - pr_err("Unable to find PCI Hyper-V video\n"); - return -ENODEV; - } - - base = pci_resource_start(pdev, 0); - size = pci_resource_len(pdev, 0); - aperture_remove_conflicting_devices(base, size, KBUILD_MODNAME); - - /* - * For Gen 1 VM, we can directly use the contiguous memory - * from VM. If we succeed, deferred IO happens directly - * on this allocated framebuffer memory, avoiding extra - * memory copy. - */ - paddr = hvfb_get_phymem(hdev, screen_fb_size); - if (paddr != (phys_addr_t) -1) { - par->mmio_pp = paddr; - par->mmio_vp = par->dio_vp = __va(paddr); - - info->fix.smem_start = paddr; - info->fix.smem_len = screen_fb_size; - info->screen_base = par->mmio_vp; - info->screen_size = screen_fb_size; - - par->need_docopy = false; - goto getmem_done; - } - pr_info("Unable to allocate enough contiguous physical memory on Gen 1 VM. Using MMIO instead.\n"); - } else { - aperture_remove_all_conflicting_devices(KBUILD_MODNAME); - } - - /* - * Cannot use contiguous physical memory, so allocate MMIO space for - * the framebuffer. At this point in the function, conflicting devices - * that might have claimed the framebuffer MMIO space based on - * screen_info.lfb_base must have already been removed so that - * vmbus_allocate_mmio() does not allocate different MMIO space. If the - * kdump image were to be loaded using kexec_file_load(), the - * framebuffer location in the kdump image would be set from - * screen_info.lfb_base at the time that kdump is enabled. If the - * framebuffer has moved elsewhere, this could be the wrong location, - * causing kdump to hang when efifb (for example) loads. - */ - dio_fb_size = - screen_width * screen_height * screen_depth / 8; - - ret = vmbus_allocate_mmio(&par->mem, hdev, 0, -1, - screen_fb_size, 0x100000, true); - if (ret != 0) { - pr_err("Unable to allocate framebuffer memory\n"); - goto err1; - } - - /* - * Map the VRAM cacheable for performance. This is also required for - * VM Connect to display properly for ARM64 Linux VM, as the host also - * maps the VRAM cacheable. - */ - fb_virt = ioremap_cache(par->mem->start, screen_fb_size); - if (!fb_virt) - goto err2; - - /* Allocate memory for deferred IO */ - par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE)); - if (par->dio_vp == NULL) - goto err3; - - /* Physical address of FB device */ - par->mmio_pp = par->mem->start; - /* Virtual address of FB device */ - par->mmio_vp = (unsigned char *) fb_virt; - - info->fix.smem_start = par->mem->start; - info->fix.smem_len = dio_fb_size; - info->screen_base = par->dio_vp; - info->screen_size = dio_fb_size; - -getmem_done: - if (!gen2vm) - pci_dev_put(pdev); - - return 0; - -err3: - iounmap(fb_virt); -err2: - vmbus_free_mmio(par->mem->start, screen_fb_size); - par->mem = NULL; -err1: - if (!gen2vm) - pci_dev_put(pdev); - - return -ENOMEM; -} - -/* Release the framebuffer */ -static void hvfb_putmem(struct fb_info *info) -{ - struct hvfb_par *par = info->par; - - if (par->need_docopy) { - vfree(par->dio_vp); - iounmap(par->mmio_vp); - vmbus_free_mmio(par->mem->start, screen_fb_size); - } else { - hvfb_release_phymem(info->device, info->fix.smem_start, - screen_fb_size); - } - - par->mem = NULL; -} - - -static int hvfb_probe(struct hv_device *hdev, - const struct hv_vmbus_device_id *dev_id) -{ - struct fb_info *info; - struct hvfb_par *par; - int ret; - - info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device); - if (!info) - return -ENOMEM; - - par = info->par; - par->info = info; - par->fb_ready = false; - par->need_docopy = true; - init_completion(&par->wait); - INIT_DELAYED_WORK(&par->dwork, hvfb_update_work); - - par->delayed_refresh = false; - spin_lock_init(&par->delayed_refresh_lock); - par->x1 = par->y1 = INT_MAX; - par->x2 = par->y2 = 0; - - /* Connect to VSP */ - hv_set_drvdata(hdev, info); - ret = synthvid_connect_vsp(hdev); - if (ret) { - pr_err("Unable to connect to VSP\n"); - goto error1; - } - - hvfb_get_option(info); - pr_info("Screen resolution: %dx%d, Color depth: %d, Frame buffer size: %d\n", - screen_width, screen_height, screen_depth, screen_fb_size); - - ret = hvfb_getmem(hdev, info); - if (ret) { - pr_err("No memory for framebuffer\n"); - goto error2; - } - - /* Set up fb_info */ - info->var.xres_virtual = info->var.xres = screen_width; - info->var.yres_virtual = info->var.yres = screen_height; - info->var.bits_per_pixel = screen_depth; - - if (info->var.bits_per_pixel == 16) { - info->var.red = (struct fb_bitfield){11, 5, 0}; - info->var.green = (struct fb_bitfield){5, 6, 0}; - info->var.blue = (struct fb_bitfield){0, 5, 0}; - info->var.transp = (struct fb_bitfield){0, 0, 0}; - } else { - info->var.red = (struct fb_bitfield){16, 8, 0}; - info->var.green = (struct fb_bitfield){8, 8, 0}; - info->var.blue = (struct fb_bitfield){0, 8, 0}; - info->var.transp = (struct fb_bitfield){24, 8, 0}; - } - - info->var.activate = FB_ACTIVATE_NOW; - info->var.height = -1; - info->var.width = -1; - info->var.vmode = FB_VMODE_NONINTERLACED; - - strcpy(info->fix.id, KBUILD_MODNAME); - info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = FB_VISUAL_TRUECOLOR; - info->fix.line_length = screen_width * screen_depth / 8; - info->fix.accel = FB_ACCEL_NONE; - - info->fbops = &hvfb_ops; - info->pseudo_palette = par->pseudo_palette; - - /* Initialize deferred IO */ - info->fbdefio = &synthvid_defio; - fb_deferred_io_init(info); - - /* Send config to host */ - ret = synthvid_send_config(hdev); - if (ret) - goto error; - - ret = devm_register_framebuffer(&hdev->device, info); - if (ret) { - pr_err("Unable to register framebuffer\n"); - goto error; - } - - par->fb_ready = true; - - par->synchronous_fb = false; - - /* - * We need to be sure this panic notifier runs _before_ the - * vmbus disconnect, so order it by priority. It must execute - * before the function hv_panic_vmbus_unload() [drivers/hv/vmbus_drv.c], - * which is almost at the end of list, with priority = INT_MIN + 1. - */ - par->hvfb_panic_nb.notifier_call = hvfb_on_panic; - par->hvfb_panic_nb.priority = INT_MIN + 10; - atomic_notifier_chain_register(&panic_notifier_list, - &par->hvfb_panic_nb); - - return 0; - -error: - fb_deferred_io_cleanup(info); - hvfb_putmem(info); -error2: - vmbus_close(hdev->channel); -error1: - cancel_delayed_work_sync(&par->dwork); - hv_set_drvdata(hdev, NULL); - framebuffer_release(info); - return ret; -} - -static void hvfb_remove(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - - atomic_notifier_chain_unregister(&panic_notifier_list, - &par->hvfb_panic_nb); - - par->update = false; - par->fb_ready = false; - - fb_deferred_io_cleanup(info); - - cancel_delayed_work_sync(&par->dwork); - - vmbus_close(hdev->channel); - hv_set_drvdata(hdev, NULL); -} - -static int hvfb_suspend(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - - console_lock(); - - /* 1 means do suspend */ - fb_set_suspend(info, 1); - - cancel_delayed_work_sync(&par->dwork); - cancel_delayed_work_sync(&info->deferred_work); - - par->update_saved = par->update; - par->update = false; - par->fb_ready = false; - - vmbus_close(hdev->channel); - - console_unlock(); - - return 0; -} - -static int hvfb_resume(struct hv_device *hdev) -{ - struct fb_info *info = hv_get_drvdata(hdev); - struct hvfb_par *par = info->par; - int ret; - - console_lock(); - - ret = synthvid_connect_vsp(hdev); - if (ret != 0) - goto out; - - ret = synthvid_send_config(hdev); - if (ret != 0) { - vmbus_close(hdev->channel); - goto out; - } - - par->fb_ready = true; - par->update = par->update_saved; - - schedule_delayed_work(&info->deferred_work, info->fbdefio->delay); - schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); - - /* 0 means do resume */ - fb_set_suspend(info, 0); - -out: - console_unlock(); - - return ret; -} - - -static const struct pci_device_id pci_stub_id_table[] = { - { - .vendor = PCI_VENDOR_ID_MICROSOFT, - .device = PCI_DEVICE_ID_HYPERV_VIDEO, - }, - { /* end of list */ } -}; - -static const struct hv_vmbus_device_id id_table[] = { - /* Synthetic Video Device GUID */ - {HV_SYNTHVID_GUID}, - {} -}; - -MODULE_DEVICE_TABLE(pci, pci_stub_id_table); -MODULE_DEVICE_TABLE(vmbus, id_table); - -static struct hv_driver hvfb_drv = { - .name = KBUILD_MODNAME, - .id_table = id_table, - .probe = hvfb_probe, - .remove = hvfb_remove, - .suspend = hvfb_suspend, - .resume = hvfb_resume, - .driver = { - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, -}; - -static int hvfb_pci_stub_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - return 0; -} - -static void hvfb_pci_stub_remove(struct pci_dev *pdev) -{ -} - -static struct pci_driver hvfb_pci_stub_driver = { - .name = KBUILD_MODNAME, - .id_table = pci_stub_id_table, - .probe = hvfb_pci_stub_probe, - .remove = hvfb_pci_stub_remove, - .driver = { - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - } -}; - -static int __init hvfb_drv_init(void) -{ - int ret; - - if (fb_modesetting_disabled("hyper_fb")) - return -ENODEV; - - ret = vmbus_driver_register(&hvfb_drv); - if (ret != 0) - return ret; - - ret = pci_register_driver(&hvfb_pci_stub_driver); - if (ret != 0) { - vmbus_driver_unregister(&hvfb_drv); - return ret; - } - - return 0; -} - -static void __exit hvfb_drv_exit(void) -{ - pci_unregister_driver(&hvfb_pci_stub_driver); - vmbus_driver_unregister(&hvfb_drv); -} - -module_init(hvfb_drv_init); -module_exit(hvfb_drv_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver"); diff --git a/drivers/video/fbdev/i810/i810_main.c b/drivers/video/fbdev/i810/i810_main.c index d73a795fe1be..10b914a24114 100644 --- a/drivers/video/fbdev/i810/i810_main.c +++ b/drivers/video/fbdev/i810/i810_main.c @@ -1012,7 +1012,7 @@ static int i810_check_params(struct fb_var_screeninfo *var, var->bits_per_pixel); vidmem = line_length * info->var.yres; if (vxres < var->xres) { - printk("i810fb: required video memory, " + dev_info(&par->dev->dev, "required video memory, " "%d bytes, for %dx%d-%d (virtual) " "is out of range\n", vidmem, vxres, vyres, @@ -1067,9 +1067,9 @@ static int i810_check_params(struct fb_var_screeninfo *var, |(info->monspecs.hfmax-HFMAX) |(info->monspecs.vfmin-VFMIN) |(info->monspecs.vfmax-VFMAX); - printk("i810fb: invalid video mode%s\n", - default_sync ? "" : ". Specifying " - "vsyncN/hsyncN parameters may help"); + dev_err(&par->dev->dev, "invalid video mode%s\n", + default_sync ? "" : ". Specifying " + "vsyncN/hsyncN parameters may help"); retval = -EINVAL; } } @@ -1674,19 +1674,19 @@ static int i810_alloc_agp_mem(struct fb_info *info) size = par->fb.size + par->iring.size; if (!(bridge = agp_backend_acquire(par->dev))) { - printk("i810fb_alloc_fbmem: cannot acquire agpgart\n"); + dev_warn(&par->dev->dev, "cannot acquire agpgart\n"); return -ENODEV; } if (!(par->i810_gtt.i810_fb_memory = agp_allocate_memory(bridge, size >> 12, AGP_NORMAL_MEMORY))) { - printk("i810fb_alloc_fbmem: can't allocate framebuffer " + dev_warn(&par->dev->dev, "can't allocate framebuffer " "memory\n"); agp_backend_release(bridge); return -ENOMEM; } if (agp_bind_memory(par->i810_gtt.i810_fb_memory, par->fb.offset)) { - printk("i810fb_alloc_fbmem: can't bind framebuffer memory\n"); + dev_warn(&par->dev->dev, "can't bind framebuffer memory\n"); agp_backend_release(bridge); return -EBUSY; } @@ -1694,14 +1694,14 @@ static int i810_alloc_agp_mem(struct fb_info *info) if (!(par->i810_gtt.i810_cursor_memory = agp_allocate_memory(bridge, par->cursor_heap.size >> 12, AGP_PHYSICAL_MEMORY))) { - printk("i810fb_alloc_cursormem: can't allocate " + dev_warn(&par->dev->dev, "can't allocate " "cursor memory\n"); agp_backend_release(bridge); return -ENOMEM; } if (agp_bind_memory(par->i810_gtt.i810_cursor_memory, par->cursor_heap.offset)) { - printk("i810fb_alloc_cursormem: cannot bind cursor memory\n"); + dev_warn(&par->dev->dev, "cannot bind cursor memory\n"); agp_backend_release(bridge); return -EBUSY; } @@ -1844,7 +1844,7 @@ static int i810_allocate_pci_resource(struct i810fb_par *par, int err; if ((err = pci_enable_device(par->dev))) { - printk("i810fb_init: cannot enable device\n"); + dev_err(&par->dev->dev, "cannot enable device\n"); return err; } par->res_flags |= PCI_DEVICE_ENABLED; @@ -1859,14 +1859,14 @@ static int i810_allocate_pci_resource(struct i810fb_par *par, par->mmio_start_phys = pci_resource_start(par->dev, 0); } if (!par->aperture.size) { - printk("i810fb_init: device is disabled\n"); + dev_warn(&par->dev->dev, "device is disabled\n"); return -ENOMEM; } if (!request_mem_region(par->aperture.physical, par->aperture.size, i810_pci_list[entry->driver_data])) { - printk("i810fb_init: cannot request framebuffer region\n"); + dev_warn(&par->dev->dev, "cannot request framebuffer region\n"); return -ENODEV; } par->res_flags |= FRAMEBUFFER_REQ; @@ -1874,14 +1874,14 @@ static int i810_allocate_pci_resource(struct i810fb_par *par, par->aperture.virtual = ioremap_wc(par->aperture.physical, par->aperture.size); if (!par->aperture.virtual) { - printk("i810fb_init: cannot remap framebuffer region\n"); + dev_warn(&par->dev->dev, "cannot remap framebuffer region\n"); return -ENODEV; } if (!request_mem_region(par->mmio_start_phys, MMIO_SIZE, i810_pci_list[entry->driver_data])) { - printk("i810fb_init: cannot request mmio region\n"); + dev_warn(&par->dev->dev, "cannot request mmio region\n"); return -ENODEV; } par->res_flags |= MMIO_REQ; @@ -1889,7 +1889,7 @@ static int i810_allocate_pci_resource(struct i810fb_par *par, par->mmio_start_virtual = ioremap(par->mmio_start_phys, MMIO_SIZE); if (!par->mmio_start_virtual) { - printk("i810fb_init: cannot remap mmio region\n"); + dev_warn(&par->dev->dev, "cannot remap mmio region\n"); return -ENODEV; } @@ -1921,12 +1921,12 @@ static void i810fb_find_init_mode(struct fb_info *info) } if (!err) - printk("i810fb_init_pci: DDC probe successful\n"); + dev_info(&par->dev->dev, "DDC probe successful\n"); fb_edid_to_monspecs(par->edid, specs); if (specs->modedb == NULL) - printk("i810fb_init_pci: Unable to get Mode Database\n"); + dev_info(&par->dev->dev, "Unable to get Mode Database\n"); fb_videomode_to_modelist(specs->modedb, specs->modedb_len, &info->modelist); @@ -2072,7 +2072,7 @@ static int i810fb_init_pci(struct pci_dev *dev, if (err < 0) { i810fb_release_resource(info, par); - printk("i810fb_init: cannot register framebuffer device\n"); + dev_warn(&par->dev->dev, "cannot register framebuffer device\n"); return err; } @@ -2084,10 +2084,10 @@ static int i810fb_init_pci(struct pci_dev *dev, vfreq = hfreq/(info->var.yres + info->var.upper_margin + info->var.vsync_len + info->var.lower_margin); - printk("I810FB: fb%d : %s v%d.%d.%d%s\n" - "I810FB: Video RAM : %dK\n" - "I810FB: Monitor : H: %d-%d KHz V: %d-%d Hz\n" - "I810FB: Mode : %dx%d-%dbpp@%dHz\n", + dev_info(&par->dev->dev, "fb%d : %s v%d.%d.%d%s\n" + "Video RAM : %dK\n" + "Monitor : H: %d-%d KHz V: %d-%d Hz\n" + "Mode : %dx%d-%dbpp@%dHz\n", info->node, i810_pci_list[entry->driver_data], VERSION_MAJOR, VERSION_MINOR, VERSION_TEENIE, BRANCH_VERSION, @@ -2137,7 +2137,7 @@ static void i810fb_remove_pci(struct pci_dev *dev) unregister_framebuffer(info); i810fb_release_resource(info, par); - printk("cleanup_module: unloaded i810 framebuffer device\n"); + dev_info(&par->dev->dev, "unloaded i810 framebuffer device\n"); } #ifndef MODULE diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index f30da32cdaed..a077bf346bdf 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c @@ -996,8 +996,13 @@ static int imxfb_probe(struct platform_device *pdev) info->fix.smem_start = fbi->map_dma; INIT_LIST_HEAD(&info->modelist); - for (i = 0; i < fbi->num_modes; i++) - fb_add_videomode(&fbi->mode[i].mode, &info->modelist); + for (i = 0; i < fbi->num_modes; i++) { + ret = fb_add_videomode(&fbi->mode[i].mode, &info->modelist); + if (ret) { + dev_err(&pdev->dev, "Failed to add videomode\n"); + goto failed_cmap; + } + } /* * This makes sure that our colour bitfield diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index 08ee8baa79f8..c8b1dfa456a3 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c @@ -679,7 +679,8 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - if ((err = pci_enable_device(pdev))) { + err = pcim_enable_device(pdev); + if (err) { printk(KERN_WARNING "kyrofb: Can't enable pdev: %d\n", err); return err; } @@ -688,6 +689,10 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!info) return -ENOMEM; + err = pcim_request_all_regions(pdev, "kyrofb"); + if (err) + goto out_free_fb; + currentpar = info->par; kyro_fix.smem_start = pci_resource_start(pdev, 0); @@ -696,13 +701,15 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) kyro_fix.mmio_len = pci_resource_len(pdev, 1); currentpar->regbase = deviceInfo.pSTGReg = - ioremap(kyro_fix.mmio_start, kyro_fix.mmio_len); + devm_ioremap(&pdev->dev, kyro_fix.mmio_start, + kyro_fix.mmio_len); if (!currentpar->regbase) goto out_free_fb; - info->screen_base = pci_ioremap_wc_bar(pdev, 0); + info->screen_base = devm_ioremap_wc(&pdev->dev, kyro_fix.smem_start, + kyro_fix.smem_len); if (!info->screen_base) - goto out_unmap_regs; + goto out_free_fb; if (!nomtrr) currentpar->wc_cookie = arch_phys_wc_add(kyro_fix.smem_start, @@ -737,7 +744,7 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fb_memset_io(info->screen_base, 0, size); if (register_framebuffer(info) < 0) - goto out_unmap; + goto out_free_fb; fb_info(info, "%s frame buffer device, at %dx%d@%d using %ldk/%ldk of VRAM\n", info->fix.id, @@ -748,10 +755,6 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; -out_unmap: - iounmap(info->screen_base); -out_unmap_regs: - iounmap(currentpar->regbase); out_free_fb: framebuffer_release(info); @@ -773,9 +776,6 @@ static void kyrofb_remove(struct pci_dev *pdev) deviceInfo.ulNextFreeVidMem = 0; deviceInfo.ulOverlayOffset = 0; - iounmap(info->screen_base); - iounmap(par->regbase); - arch_phys_wc_del(par->wc_cookie); unregister_framebuffer(info); diff --git a/drivers/video/fbdev/macfb.c b/drivers/video/fbdev/macfb.c index 887fffdccd24..ef3d2304e2f4 100644 --- a/drivers/video/fbdev/macfb.c +++ b/drivers/video/fbdev/macfb.c @@ -668,19 +668,19 @@ static int __init macfb_init(void) switch(ndev->dr_hw) { case NUBUS_DRHW_APPLE_MDC: - strcpy(macfb_fix.id, "Mac Disp. Card"); + strscpy(macfb_fix.id, "Mac Disp. Card"); macfb_setpalette = mdc_setpalette; break; case NUBUS_DRHW_APPLE_TFB: - strcpy(macfb_fix.id, "Toby"); + strscpy(macfb_fix.id, "Toby"); macfb_setpalette = toby_setpalette; break; case NUBUS_DRHW_APPLE_JET: - strcpy(macfb_fix.id, "Jet"); + strscpy(macfb_fix.id, "Jet"); macfb_setpalette = jet_setpalette; break; default: - strcpy(macfb_fix.id, "Generic NuBus"); + strscpy(macfb_fix.id, "Generic NuBus"); break; } } @@ -707,7 +707,7 @@ static int __init macfb_init(void) case MAC_MODEL_Q700: case MAC_MODEL_Q900: case MAC_MODEL_Q950: - strcpy(macfb_fix.id, "DAFB"); + strscpy(macfb_fix.id, "DAFB"); macfb_setpalette = dafb_setpalette; dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000); break; @@ -716,7 +716,7 @@ static int __init macfb_init(void) * LC II uses the V8 framebuffer */ case MAC_MODEL_LCII: - strcpy(macfb_fix.id, "V8"); + strscpy(macfb_fix.id, "V8"); macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -729,7 +729,7 @@ static int __init macfb_init(void) case MAC_MODEL_IIVI: case MAC_MODEL_IIVX: case MAC_MODEL_P600: - strcpy(macfb_fix.id, "Brazil"); + strscpy(macfb_fix.id, "Brazil"); macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -745,7 +745,7 @@ static int __init macfb_init(void) case MAC_MODEL_P520: case MAC_MODEL_P550: case MAC_MODEL_P460: - strcpy(macfb_fix.id, "Sonora"); + strscpy(macfb_fix.id, "Sonora"); macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -757,7 +757,7 @@ static int __init macfb_init(void) */ case MAC_MODEL_IICI: case MAC_MODEL_IISI: - strcpy(macfb_fix.id, "RBV"); + strscpy(macfb_fix.id, "RBV"); macfb_setpalette = rbv_setpalette; rbv_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -767,7 +767,7 @@ static int __init macfb_init(void) */ case MAC_MODEL_Q840: case MAC_MODEL_C660: - strcpy(macfb_fix.id, "Civic"); + strscpy(macfb_fix.id, "Civic"); macfb_setpalette = civic_setpalette; civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000); break; @@ -778,7 +778,7 @@ static int __init macfb_init(void) * We think this may be like the LC II */ case MAC_MODEL_LC: - strcpy(macfb_fix.id, "LC"); + strscpy(macfb_fix.id, "LC"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = @@ -790,7 +790,7 @@ static int __init macfb_init(void) * We think this may be like the LC II */ case MAC_MODEL_CCL: - strcpy(macfb_fix.id, "Color Classic"); + strscpy(macfb_fix.id, "Color Classic"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = @@ -802,7 +802,7 @@ static int __init macfb_init(void) * And we *do* mean "weirdos" */ case MAC_MODEL_TV: - strcpy(macfb_fix.id, "Mac TV"); + strscpy(macfb_fix.id, "Mac TV"); break; /* @@ -810,7 +810,7 @@ static int __init macfb_init(void) */ case MAC_MODEL_SE30: case MAC_MODEL_CLII: - strcpy(macfb_fix.id, "Monochrome"); + strscpy(macfb_fix.id, "Monochrome"); break; /* @@ -828,7 +828,7 @@ static int __init macfb_init(void) case MAC_MODEL_PB140: case MAC_MODEL_PB145: case MAC_MODEL_PB170: - strcpy(macfb_fix.id, "DDC"); + strscpy(macfb_fix.id, "DDC"); break; /* @@ -840,7 +840,7 @@ static int __init macfb_init(void) case MAC_MODEL_PB180: case MAC_MODEL_PB210: case MAC_MODEL_PB230: - strcpy(macfb_fix.id, "GSC"); + strscpy(macfb_fix.id, "GSC"); break; /* @@ -848,7 +848,7 @@ static int __init macfb_init(void) */ case MAC_MODEL_PB165C: case MAC_MODEL_PB180C: - strcpy(macfb_fix.id, "TIM"); + strscpy(macfb_fix.id, "TIM"); break; /* @@ -860,13 +860,13 @@ static int __init macfb_init(void) case MAC_MODEL_PB270C: case MAC_MODEL_PB280: case MAC_MODEL_PB280C: - strcpy(macfb_fix.id, "CSC"); + strscpy(macfb_fix.id, "CSC"); macfb_setpalette = csc_setpalette; csc_cmap_regs = ioremap(CSC_BASE, 0x1000); break; default: - strcpy(macfb_fix.id, "Unknown"); + strscpy(macfb_fix.id, "Unknown"); break; } diff --git a/drivers/video/fbdev/macmodes.c b/drivers/video/fbdev/macmodes.c index d6be3c67d3df..b16a9d9bef98 100644 --- a/drivers/video/fbdev/macmodes.c +++ b/drivers/video/fbdev/macmodes.c @@ -16,6 +16,7 @@ */ #include <linux/errno.h> +#include <linux/export.h> #include <linux/fb.h> #include <linux/string.h> #include <linux/module.h> @@ -236,7 +237,7 @@ int mac_vmode_to_var(int vmode, int cmode, struct fb_var_screeninfo *var) case CMODE_8: var->bits_per_pixel = 8; var->red.offset = 0; - var->red.length = 8; + var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; diff --git a/drivers/video/fbdev/matrox/g450_pll.c b/drivers/video/fbdev/matrox/g450_pll.c index ff8e321a22ce..6a08f78cd1ac 100644 --- a/drivers/video/fbdev/matrox/g450_pll.c +++ b/drivers/video/fbdev/matrox/g450_pll.c @@ -14,6 +14,8 @@ * */ +#include <linux/export.h> + #include "g450_pll.h" #include "matroxfb_DAC1064.h" @@ -258,13 +260,13 @@ static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo, unsigned int found = 0; unsigned int idx; unsigned int mnpfound = mnparray[0]; - + for (idx = 0; idx < mnpcount; idx++) { unsigned int sarray[3]; unsigned int *sptr; { unsigned int mnp; - + sptr = sarray; mnp = mnparray[idx]; if (mnp & 0x38) { @@ -277,7 +279,7 @@ static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo, } while (sptr >= sarray) { unsigned int mnp = *sptr--; - + if (g450_testpll(minfo, mnp - 0x0300, pll) && g450_testpll(minfo, mnp + 0x0300, pll) && g450_testpll(minfo, mnp - 0x0200, pll) && @@ -310,12 +312,12 @@ static int g450_checkcache(struct matrox_fb_info *minfo, struct matrox_pll_cache *ci, unsigned int mnp_key) { unsigned int i; - + mnp_key &= G450_MNP_FREQBITS; for (i = 0; i < ci->valid; i++) { if (ci->data[i].mnp_key == mnp_key) { unsigned int mnp; - + mnp = ci->data[i].mnp_value; if (i) { memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); @@ -343,7 +345,7 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, { u_int8_t tmp, xpwrctrl; unsigned long flags; - + matroxfb_DAC_lock_irqsave(flags); xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); @@ -375,7 +377,7 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, } { u_int8_t misc; - + misc = mga_inb(M_MISC_REG_READ) & ~0x0C; switch (pll) { case M_PIXEL_PLL_A: @@ -407,15 +409,15 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, case M_VIDEO_PLL: { u_int8_t tmp; - unsigned int mnp; + unsigned int mnp __maybe_unused; unsigned long flags; - + matroxfb_DAC_lock_irqsave(flags); tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); if (!(tmp & 2)) { matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2); } - + mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16; mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8; matroxfb_DAC_unlock_irqrestore(flags); @@ -441,7 +443,7 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); for (idx = mnpcount; idx > 0; idx--) { /* == is important; due to nextpll algorithm we get - sorted equally good frequencies from lower VCO + sorted equally good frequencies from lower VCO frequency to higher - with <= lowest wins, while with < highest one wins */ if (delta <= deltaarray[idx-1]) { @@ -472,7 +474,7 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, { unsigned long flags; unsigned int mnp; - + matroxfb_DAC_lock_irqsave(flags); mnp = g450_checkcache(minfo, ci, mnparray[0]); if (mnp != NO_MORE_MNP) { @@ -495,7 +497,7 @@ int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, unsigned int pll) { unsigned int* arr; - + arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); if (arr) { int r; diff --git a/drivers/video/fbdev/matrox/i2c-matroxfb.c b/drivers/video/fbdev/matrox/i2c-matroxfb.c index bb048e14b2cf..e02dd11b090e 100644 --- a/drivers/video/fbdev/matrox/i2c-matroxfb.c +++ b/drivers/video/fbdev/matrox/i2c-matroxfb.c @@ -144,7 +144,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { unsigned long flags; struct matroxfb_dh_maven_info* m2info; - m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); + m2info = kzalloc_obj(*m2info); if (!m2info) return NULL; diff --git a/drivers/video/fbdev/matrox/matroxfb_DAC1064.c b/drivers/video/fbdev/matrox/matroxfb_DAC1064.c index 398b7035f5a9..9a893b70ab19 100644 --- a/drivers/video/fbdev/matrox/matroxfb_DAC1064.c +++ b/drivers/video/fbdev/matrox/matroxfb_DAC1064.c @@ -13,6 +13,7 @@ * */ +#include <linux/export.h> #include "matroxfb_DAC1064.h" #include "matroxfb_misc.h" @@ -43,11 +44,11 @@ static void DAC1064_calcclock(const struct matrox_fb_info *minfo, unsigned int p; DBG(__func__) - + /* only for devices older than G450 */ fvco = PLL_calcclock(minfo, freq, fmax, in, feed, &p); - + p = (1 << p) - 1; if (fvco <= 100000) ; @@ -169,7 +170,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) struct matrox_hw_state *hw = &minfo->hw; int pixelmnp; int videomnp; - + c2_ctl = hw->crtc2.ctl & ~0x4007; /* Clear PLL + enable for CRTC2 */ c2_ctl |= 0x0001; /* Enable CRTC2 */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x02; /* Stop VIDEO PLL */ @@ -192,7 +193,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) } c2_ctl |= 0x0006; /* Use video PLL */ hw->DACreg[POS1064_XPWRCTRL] |= 0x02; - + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); matroxfb_g450_setpll_cond(minfo, videomnp, M_VIDEO_PLL); } @@ -200,7 +201,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP; if (pixelmnp >= 0) { hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP; - + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); matroxfb_g450_setpll_cond(minfo, pixelmnp, M_PIXEL_PLL_C); } @@ -303,9 +304,9 @@ void DAC1064_global_init(struct matrox_fb_info *minfo) poweroff TMDS. But if we boot with DFP connected, TMDS generated clocks are used instead of ALL pixclocks available... If someone knows which register - handles it, please reveal this secret to me... */ + handles it, please reveal this secret to me... */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x04; /* Poweroff TMDS */ -#endif +#endif break; } /* Now set timming related variables... */ @@ -728,14 +729,14 @@ static void g450_mclk_init(struct matrox_fb_info *minfo) } else { unsigned long flags; unsigned int pwr; - + matroxfb_DAC_lock_irqsave(flags); pwr = inDAC1064(minfo, M1064_XPWRCTRL) & ~0x02; outDAC1064(minfo, M1064_XPWRCTRL, pwr); matroxfb_DAC_unlock_irqrestore(flags); } matroxfb_g450_setclk(minfo, minfo->values.pll.system, M_SYSTEM_PLL); - + /* switch clocks to their real PLL source(s) */ pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3); @@ -748,15 +749,15 @@ static void g450_memory_init(struct matrox_fb_info *minfo) /* disable memory refresh */ minfo->hw.MXoptionReg &= ~0x001F8000; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); - + /* set memory interface parameters */ minfo->hw.MXoptionReg &= ~0x00207E00; minfo->hw.MXoptionReg |= 0x00207E00 & minfo->values.reg.opt; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, minfo->values.reg.opt2); - + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); - + /* first set up memory interface with disabled memory interface clocks */ pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc & ~0x80000000U); mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); @@ -765,25 +766,25 @@ static void g450_memory_init(struct matrox_fb_info *minfo) pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc | 0x80000000U); udelay(200); - + if (minfo->values.memory.ddr && (!minfo->values.memory.emrswen || !minfo->values.memory.dll)) { mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk & ~0x1000); } mga_outl(M_MACCESS, minfo->values.reg.maccess | 0x8000); - + udelay(200); - + minfo->hw.MXoptionReg |= 0x001F8000 & minfo->values.reg.opt; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); - + /* value is written to memory chips only if old != new */ mga_outl(M_PLNWT, 0); mga_outl(M_PLNWT, ~0); - + if (minfo->values.reg.mctlwtst != minfo->values.reg.mctlwtst_core) { mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst_core); } - + } static void g450_preinit(struct matrox_fb_info *minfo) @@ -791,7 +792,7 @@ static void g450_preinit(struct matrox_fb_info *minfo) u_int32_t c2ctl; u_int8_t curctl; u_int8_t c1ctl; - + /* minfo->hw.MXoptionReg = minfo->values.reg.opt; */ minfo->hw.MXoptionReg &= 0xC0000100; minfo->hw.MXoptionReg |= 0x00000020; @@ -805,7 +806,7 @@ static void g450_preinit(struct matrox_fb_info *minfo) pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); /* Init system clocks */ - + /* stop crtc2 */ c2ctl = mga_inl(M_C2CTL); mga_outl(M_C2CTL, c2ctl & ~1); @@ -818,20 +819,20 @@ static void g450_preinit(struct matrox_fb_info *minfo) g450_mclk_init(minfo); g450_memory_init(minfo); - + /* set legacy VGA clock sources for DOSEmu or VMware... */ matroxfb_g450_setclk(minfo, 25175, M_PIXEL_PLL_A); matroxfb_g450_setclk(minfo, 28322, M_PIXEL_PLL_B); /* restore crtc1 */ mga_setr(M_SEQ_INDEX, 1, c1ctl); - + /* restore cursor */ outDAC1064(minfo, M1064_XCURCTRL, curctl); /* restore crtc2 */ mga_outl(M_C2CTL, c2ctl); - + return; } diff --git a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c index f53b8066e8a5..4eb636cd1f89 100644 --- a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c +++ b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c @@ -79,6 +79,7 @@ * */ +#include <linux/export.h> #include "matroxfb_Ti3026.h" #include "matroxfb_misc.h" diff --git a/drivers/video/fbdev/matrox/matroxfb_accel.c b/drivers/video/fbdev/matrox/matroxfb_accel.c index 52e15dc6f45b..2670db392da2 100644 --- a/drivers/video/fbdev/matrox/matroxfb_accel.c +++ b/drivers/video/fbdev/matrox/matroxfb_accel.c @@ -77,6 +77,8 @@ * */ +#include <linux/export.h> + #include "matroxfb_accel.h" #include "matroxfb_DAC1064.h" #include "matroxfb_Ti3026.h" diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c index 81603ce05a22..e1a4bc7c2318 100644 --- a/drivers/video/fbdev/matrox/matroxfb_base.c +++ b/drivers/video/fbdev/matrox/matroxfb_base.c @@ -101,6 +101,7 @@ */ #include <linux/aperture.h> +#include <linux/export.h> #include <linux/version.h> #include "matroxfb_base.h" @@ -2072,7 +2073,7 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm return -1; } - minfo = kzalloc(sizeof(*minfo), GFP_KERNEL); + minfo = kzalloc_obj(*minfo); if (!minfo) return -ENOMEM; diff --git a/drivers/video/fbdev/matrox/matroxfb_crtc2.c b/drivers/video/fbdev/matrox/matroxfb_crtc2.c index 417fc692468d..8de882b09a24 100644 --- a/drivers/video/fbdev/matrox/matroxfb_crtc2.c +++ b/drivers/video/fbdev/matrox/matroxfb_crtc2.c @@ -693,7 +693,7 @@ static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { /* hardware is CRTC2 incapable... */ if (!minfo->devflags.crtc2) return NULL; - m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); + m2info = kzalloc_obj(*m2info); if (!m2info) return NULL; diff --git a/drivers/video/fbdev/matrox/matroxfb_g450.c b/drivers/video/fbdev/matrox/matroxfb_g450.c index df3309fd14f3..800c05b70ee3 100644 --- a/drivers/video/fbdev/matrox/matroxfb_g450.c +++ b/drivers/video/fbdev/matrox/matroxfb_g450.c @@ -13,6 +13,8 @@ * */ +#include <linux/export.h> + #include "matroxfb_base.h" #include "matroxfb_misc.h" #include "matroxfb_DAC1064.h" @@ -32,29 +34,29 @@ struct mctl { #define WLMAX 0x3FF static const struct mctl g450_controls[] = -{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, +{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, "brightness", - 0, WLMAX-BLMIN, 1, 370-BLMIN, + 0, WLMAX-BLMIN, 1, 370-BLMIN, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) }, - { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, + { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, "contrast", - 0, 1023, 1, 127, + 0, 1023, 1, 127, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) }, { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, "saturation", - 0, 255, 1, 165, + 0, 255, 1, 165, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) }, { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, "hue", - 0, 255, 1, 0, + 0, 255, 1, 0, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, "test output", - 0, 1, 1, 0, + 0, 1, 1, 0, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, }; @@ -89,7 +91,7 @@ static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx) static void tvo_fill_defaults(struct matrox_fb_info *minfo) { unsigned int i; - + for (i = 0; i < G450CTRLS; i++) { *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value; } @@ -99,7 +101,7 @@ static int cve2_get_reg(struct matrox_fb_info *minfo, int reg) { unsigned long flags; int val; - + matroxfb_DAC_lock_irqsave(flags); matroxfb_DAC_out(minfo, 0x87, reg); val = matroxfb_DAC_in(minfo, 0x88); @@ -141,16 +143,16 @@ static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl, static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) { int i; - + i = get_ctrl_id(p->id); if (i >= 0) { *p = g450_controls[i].desc; return 0; } if (i == -ENOENT) { - static const struct v4l2_queryctrl disctrl = + static const struct v4l2_queryctrl disctrl = { .flags = V4L2_CTRL_FLAG_DISABLED }; - + i = p->id; *p = disctrl; p->id = i; @@ -163,7 +165,7 @@ static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) { static int g450_set_ctrl(void* md, struct v4l2_control *p) { int i; struct matrox_fb_info *minfo = md; - + i = get_ctrl_id(p->id); if (i < 0) return -EINVAL; @@ -209,7 +211,7 @@ static int g450_set_ctrl(void* md, struct v4l2_control *p) { } break; } - + return 0; } @@ -217,7 +219,7 @@ static int g450_set_ctrl(void* md, struct v4l2_control *p) { static int g450_get_ctrl(void* md, struct v4l2_control *p) { int i; struct matrox_fb_info *minfo = md; - + i = get_ctrl_id(p->id); if (i < 0) return -EINVAL; p->value = *get_ctrl_ptr(minfo, i); @@ -247,22 +249,22 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, unsigned long long piic; int mnp; int over; - + r->regs[0x80] = 0x03; /* | 0x40 for SCART */ hvis = ((mt->HDisplay << 1) + 3) & ~3; - + if (hvis >= 2048) { hvis = 2044; } - + piic = 1000000000ULL * hvis; do_div(piic, outd->h_vis); dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic); - + mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL); - + mt->mnp = mnp; mt->pixclock = g450_mnp2f(minfo, mnp); @@ -275,7 +277,7 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, piic = outd->chromasc; do_div(piic, mt->pixclock); chromasc = piic; - + dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc); r->regs[0] = piic >> 24; @@ -287,7 +289,7 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1; hlen = hvis + hfp + hsl + hbp; over = hlen & 0x0F; - + dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen); if (over) { @@ -310,14 +312,14 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, r->regs[0x2C] = hfp; r->regs[0x31] = hvis / 8; r->regs[0x32] = hvis & 7; - + dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen); r->regs[0x84] = 1; /* x sync point */ r->regs[0x85] = 0; hvis = hvis >> 1; hlen = hlen >> 1; - + dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis); mt->interlaced = 1; @@ -332,13 +334,13 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, unsigned int vtotal; unsigned int vsyncend; unsigned int vdisplay; - + vtotal = mt->VTotal; vsyncend = mt->VSyncEnd; vdisplay = mt->VDisplay; if (vtotal < outd->v_total) { unsigned int yovr = outd->v_total - vtotal; - + vsyncend += yovr >> 1; } else if (vtotal > outd->v_total) { vdisplay = outd->v_total - 4; @@ -350,7 +352,7 @@ static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, r->regs[0x33] = upper - 1; /* upper blanking */ r->regs[0x82] = upper; /* y sync point */ r->regs[0x83] = upper >> 8; - + mt->VDisplay = vdisplay; mt->VSyncStart = outd->v_total - 2; mt->VSyncEnd = outd->v_total; @@ -509,9 +511,9 @@ static void cve2_init_TV(struct matrox_fb_info *minfo, LR(0x80); LR(0x82); LR(0x83); LR(0x84); LR(0x85); - + cve2_set_reg(minfo, 0x3E, 0x01); - + for (i = 0; i < 0x3E; i++) { LR(i); } @@ -558,7 +560,7 @@ static int matroxfb_g450_compute(void* md, struct my_timming* mt) { static int matroxfb_g450_program(void* md) { struct matrox_fb_info *minfo = md; - + if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { cve2_init_TV(minfo, &minfo->hw.maven); } diff --git a/drivers/video/fbdev/matrox/matroxfb_maven.c b/drivers/video/fbdev/matrox/matroxfb_maven.c index dcfae770b42d..2ea65da6075c 100644 --- a/drivers/video/fbdev/matrox/matroxfb_maven.c +++ b/drivers/video/fbdev/matrox/matroxfb_maven.c @@ -1260,7 +1260,7 @@ static int maven_probe(struct i2c_client *client) I2C_FUNC_NOSTART | I2C_FUNC_PROTOCOL_MANGLING)) goto ERROR0; - if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) { + if (!(data = kzalloc_obj(*data))) { err = -ENOMEM; goto ERROR0; } diff --git a/drivers/video/fbdev/matrox/matroxfb_misc.c b/drivers/video/fbdev/matrox/matroxfb_misc.c index 8f159a2ad8d0..2c5f0099532b 100644 --- a/drivers/video/fbdev/matrox/matroxfb_misc.c +++ b/drivers/video/fbdev/matrox/matroxfb_misc.c @@ -85,6 +85,7 @@ * */ +#include <linux/export.h> #include "matroxfb_misc.h" #include <linux/interrupt.h> @@ -390,7 +391,7 @@ void matroxfb_vgaHWrestore(struct matrox_fb_info *minfo) static void get_pins(unsigned char __iomem* pins, struct matrox_bios* bd) { unsigned int b0 = readb(pins); - + if (b0 == 0x2E && readb(pins+1) == 0x41) { unsigned int pins_len = readb(pins+2); unsigned int i; @@ -426,7 +427,7 @@ static void get_pins(unsigned char __iomem* pins, struct matrox_bios* bd) { static void get_bios_version(unsigned char __iomem * vbios, struct matrox_bios* bd) { unsigned int pcir_offset; - + pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8); if (pcir_offset >= 26 && pcir_offset < 0xFFE0 && readb(vbios + pcir_offset ) == 'P' && @@ -451,7 +452,7 @@ static void get_bios_version(unsigned char __iomem * vbios, struct matrox_bios* static void get_bios_output(unsigned char __iomem* vbios, struct matrox_bios* bd) { unsigned char b; - + b = readb(vbios + 0x7FF1); if (b == 0xFF) { b = 0; @@ -461,7 +462,7 @@ static void get_bios_output(unsigned char __iomem* vbios, struct matrox_bios* bd static void get_bios_tvout(unsigned char __iomem* vbios, struct matrox_bios* bd) { unsigned int i; - + /* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */ bd->output.tvout = 0; if (readb(vbios + 0x1D) != 'I' || @@ -472,7 +473,7 @@ static void get_bios_tvout(unsigned char __iomem* vbios, struct matrox_bios* bd) } for (i = 0x2D; i < 0x2D + 128; i++) { unsigned char b = readb(vbios + i); - + if (b == '(' && readb(vbios + i + 1) == 'V') { if (readb(vbios + i + 6) == 'T' && readb(vbios + i + 7) == 'V' && @@ -488,7 +489,7 @@ static void get_bios_tvout(unsigned char __iomem* vbios, struct matrox_bios* bd) static void parse_bios(unsigned char __iomem* vbios, struct matrox_bios* bd) { unsigned int pins_offset; - + if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) { return; } @@ -648,9 +649,9 @@ static int parse_pins5(struct matrox_fb_info *minfo, const struct matrox_bios *bd) { unsigned int mult; - + mult = bd->pins[4]?8000:6000; - + minfo->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; minfo->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? minfo->limits.pixel.vcomax : bd->pins[ 36] * mult; minfo->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? minfo->limits.system.vcomax : bd->pins[ 37] * mult; @@ -770,7 +771,7 @@ void matroxfb_read_pins(struct matrox_fb_info *minfo) u32 biosbase; u32 fbbase; struct pci_dev *pdev = minfo->pcidev; - + memset(&minfo->bios, 0, sizeof(minfo->bios)); pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); @@ -790,7 +791,7 @@ void matroxfb_read_pins(struct matrox_fb_info *minfo) } else { unsigned int ven = readb(b+0x64+0) | (readb(b+0x64+1) << 8); unsigned int dev = readb(b+0x64+2) | (readb(b+0x64+3) << 8); - + if (ven != pdev->vendor || dev != pdev->device) { printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n", ven, dev, pdev->vendor, pdev->device); diff --git a/drivers/video/fbdev/mb862xx/mb862xx-i2c.c b/drivers/video/fbdev/mb862xx/mb862xx-i2c.c index ffdb1597d303..08b7f19a67a5 100644 --- a/drivers/video/fbdev/mb862xx/mb862xx-i2c.c +++ b/drivers/video/fbdev/mb862xx/mb862xx-i2c.c @@ -9,7 +9,6 @@ #include <linux/i2c.h> #include <linux/io.h> #include <linux/delay.h> -#include <linux/export.h> #include "mb862xxfb.h" #include "mb862xx_reg.h" diff --git a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c index ade88e7bc760..676c6d3ccc12 100644 --- a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c @@ -674,7 +674,7 @@ static int of_platform_mb862xx_probe(struct platform_device *ofdev) struct fb_info *info; struct resource res; resource_size_t res_size; - unsigned long ret = -ENODEV; + int ret = -ENODEV; if (of_address_to_resource(np, 0, &res)) { dev_err(dev, "Invalid address\n"); diff --git a/drivers/video/fbdev/mmp/core.c b/drivers/video/fbdev/mmp/core.c index 03707461eced..3c35b0a3ea09 100644 --- a/drivers/video/fbdev/mmp/core.c +++ b/drivers/video/fbdev/mmp/core.c @@ -155,8 +155,7 @@ struct mmp_path *mmp_register_path(struct mmp_path_info *info) struct mmp_path *path = NULL; struct mmp_panel *panel; - path = kzalloc(struct_size(path, overlays, info->overlay_num), - GFP_KERNEL); + path = kzalloc_flex(*path, overlays, info->overlay_num); if (!path) return NULL; diff --git a/drivers/video/fbdev/mmp/fb/mmpfb.c b/drivers/video/fbdev/mmp/fb/mmpfb.c index 2d9797c6fb3e..694587256e06 100644 --- a/drivers/video/fbdev/mmp/fb/mmpfb.c +++ b/drivers/video/fbdev/mmp/fb/mmpfb.c @@ -476,8 +476,7 @@ static int modes_setup(struct mmpfb_info *fbi) return 0; } /* put videomode list to info structure */ - videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode), - GFP_KERNEL); + videomodes = kzalloc_objs(struct fb_videomode, videomode_num); if (!videomodes) return -ENOMEM; diff --git a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c index 03e23173198c..75bbdc0b4aa6 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c +++ b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c @@ -402,7 +402,7 @@ static int path_init(struct mmphw_path_plat *path_plat, dev_info(ctrl->dev, "%s: %s\n", __func__, config->name); /* init driver data */ - path_info = kzalloc(sizeof(*path_info), GFP_KERNEL); + path_info = kzalloc_obj(*path_info); if (!path_info) return 0; diff --git a/drivers/video/fbdev/mmp/panel/tpo_tj032md01bw.c b/drivers/video/fbdev/mmp/panel/tpo_tj032md01bw.c index 34fae588e202..d111ec2a4703 100644 --- a/drivers/video/fbdev/mmp/panel/tpo_tj032md01bw.c +++ b/drivers/video/fbdev/mmp/panel/tpo_tj032md01bw.c @@ -144,7 +144,7 @@ static int tpohvga_probe(struct spi_device *spi) return ret; } - plat_data = kzalloc(sizeof(*plat_data), GFP_KERNEL); + plat_data = kzalloc_obj(*plat_data); if (plat_data == NULL) return -ENOMEM; diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c index 632ba2455913..c1cd028b8991 100644 --- a/drivers/video/fbdev/neofb.c +++ b/drivers/video/fbdev/neofb.c @@ -1768,7 +1768,7 @@ static int neo_scan_monitor(struct fb_info *info) int w; // Eventually we will have i2c support. - info->monspecs.modedb = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL); + info->monspecs.modedb = kmalloc_obj(struct fb_videomode); if (!info->monspecs.modedb) return -ENOMEM; info->monspecs.modedb_len = 1; diff --git a/drivers/video/fbdev/nvidia/nv_local.h b/drivers/video/fbdev/nvidia/nv_local.h index 68e508daa417..93aff35305a9 100644 --- a/drivers/video/fbdev/nvidia/nv_local.h +++ b/drivers/video/fbdev/nvidia/nv_local.h @@ -80,7 +80,7 @@ (par)->dmaFree -= ((size) + 1); \ } -#if defined(__i386__) +#if defined(__i386__) && !defined(CONFIG_UML) #define _NV_FENCE() outb(0, 0x3D0); #else #define _NV_FENCE() mb(); diff --git a/drivers/video/fbdev/nvidia/nv_setup.c b/drivers/video/fbdev/nvidia/nv_setup.c index 5404017e6957..281c7f2ee650 100644 --- a/drivers/video/fbdev/nvidia/nv_setup.c +++ b/drivers/video/fbdev/nvidia/nv_setup.c @@ -283,9 +283,9 @@ int NVCommonSetup(struct fb_info *info) int Television = 0; int err = 0; - var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); - monitorA = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); - monitorB = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); + var = kzalloc_obj(struct fb_var_screeninfo); + monitorA = kzalloc_obj(struct fb_monspecs); + monitorB = kzalloc_obj(struct fb_monspecs); if (!var || !monitorA || !monitorB) { err = -ENOMEM; diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index 8900f181f195..72b85f475605 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/console.h> #include <linux/backlight.h> +#include <linux/string_choices.h> #ifdef CONFIG_BOOTX_TEXT #include <asm/btext.h> #endif @@ -622,7 +623,7 @@ static int nvidiafb_set_par(struct fb_info *info) else par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1); printk(KERN_INFO PFX "Flat panel dithering %s\n", - par->FPDither ? "enabled" : "disabled"); + str_enabled_disabled(par->FPDither)); } info->fix.visual = (info->var.bits_per_pixel == 8) ? @@ -1484,7 +1485,7 @@ static int nvidiafb_setup(char *options) flatpanel = 1; } else if (!strncmp(this_opt, "hwcur", 5)) { hwcur = 1; - } else if (!strncmp(this_opt, "noaccel", 6)) { + } else if (!strncmp(this_opt, "noaccel", 7)) { noaccel = 1; } else if (!strncmp(this_opt, "noscale", 7)) { noscale = 1; diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index f85428e13996..166b2dff36f5 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -640,8 +640,13 @@ static void offb_init_nodriver(struct platform_device *parent, struct device_nod vid = be32_to_cpup(vidp); did = be32_to_cpup(didp); pdev = pci_get_device(vid, did, NULL); - if (!pdev || pci_enable_device(pdev)) + if (!pdev) return; + + if (pci_enable_device(pdev)) { + pci_dev_put(pdev); + return; + } } #endif /* kludge for valkyrie */ diff --git a/drivers/video/fbdev/omap/hwa742.c b/drivers/video/fbdev/omap/hwa742.c index 161fc65d6b57..68a677f824e9 100644 --- a/drivers/video/fbdev/omap/hwa742.c +++ b/drivers/video/fbdev/omap/hwa742.c @@ -597,7 +597,7 @@ static int hwa742_set_update_mode(enum omapfb_update_mode mode) break; case OMAPFB_AUTO_UPDATE: hwa742.stop_auto_update = 1; - del_timer_sync(&hwa742.auto_update_timer); + timer_delete_sync(&hwa742.auto_update_timer); break; case OMAPFB_UPDATE_DISABLED: break; @@ -950,6 +950,8 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, omapfb_conf = dev_get_platdata(fbdev->dev); hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck"); + if (IS_ERR(hwa742.sys_ck)) + return PTR_ERR(hwa742.sys_ck); spin_lock_init(&hwa742.req_lock); @@ -1028,6 +1030,7 @@ err3: err2: hwa742.int_ctrl->cleanup(); err1: + clk_put(hwa742.sys_ck); return r; } @@ -1037,6 +1040,7 @@ static void hwa742_cleanup(void) hwa742.extif->cleanup(); hwa742.int_ctrl->cleanup(); clk_disable_unprepare(hwa742.sys_ck); + clk_put(hwa742.sys_ck); } struct lcd_ctrl hwa742_ctrl = { diff --git a/drivers/video/fbdev/omap/lcd_dma.c b/drivers/video/fbdev/omap/lcd_dma.c index 0da23c57e475..56300be71c57 100644 --- a/drivers/video/fbdev/omap/lcd_dma.c +++ b/drivers/video/fbdev/omap/lcd_dma.c @@ -18,6 +18,7 @@ * Support functions for the OMAP internal DMA channels. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/interrupt.h> diff --git a/drivers/video/fbdev/omap/lcd_mipid.c b/drivers/video/fbdev/omap/lcd_mipid.c index a0fc4570403b..72c2a56943c2 100644 --- a/drivers/video/fbdev/omap/lcd_mipid.c +++ b/drivers/video/fbdev/omap/lcd_mipid.c @@ -552,7 +552,7 @@ static int mipid_spi_probe(struct spi_device *spi) struct mipid_device *md; int r; - md = kzalloc(sizeof(*md), GFP_KERNEL); + md = kzalloc_obj(*md); if (md == NULL) { dev_err(&spi->dev, "out of memory\n"); return -ENOMEM; diff --git a/drivers/video/fbdev/omap/lcdc.c b/drivers/video/fbdev/omap/lcdc.c index abb8b11464e8..53ca58ec5eed 100644 --- a/drivers/video/fbdev/omap/lcdc.c +++ b/drivers/video/fbdev/omap/lcdc.c @@ -5,8 +5,10 @@ * Copyright (C) 2004 Nokia Corporation * Author: Imre Deak <imre.deak@nokia.com> */ + #include <linux/module.h> #include <linux/device.h> +#include <linux/export.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/err.h> diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 2682b20d184a..cafe859d6e5a 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -11,6 +11,8 @@ * Dirk Behme <dirk.behme@de.bosch.com> - changes for 2.6 kernel API * Texas Instruments - H3 support */ + +#include <linux/export.h> #include <linux/platform_device.h> #include <linux/mm.h> #include <linux/slab.h> @@ -844,12 +846,10 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) * be reenabled unless its size is > 0. */ if (old_size != size && size) { - if (size) { - memcpy(new_var, &fbi->var, sizeof(*new_var)); - r = set_fb_var(fbi, new_var); - if (r < 0) - goto out; - } + memcpy(new_var, &fbi->var, sizeof(*new_var)); + r = set_fb_var(fbi, new_var); + if (r < 0) + goto out; } if (fbdev->ctrl->sync) @@ -1630,7 +1630,7 @@ static int omapfb_do_probe(struct platform_device *pdev, goto cleanup; } - fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + fbdev = kzalloc_obj(*fbdev); if (fbdev == NULL) { dev_err(&pdev->dev, "unable to allocate memory for device info\n"); diff --git a/drivers/video/fbdev/omap2/omapfb/Kconfig b/drivers/video/fbdev/omap2/omapfb/Kconfig index f4cdf999a080..2d20e79adefc 100644 --- a/drivers/video/fbdev/omap2/omapfb/Kconfig +++ b/drivers/video/fbdev/omap2/omapfb/Kconfig @@ -5,7 +5,6 @@ config OMAP2_VRFB menuconfig FB_OMAP2 tristate "OMAP2+ frame buffer support" depends on FB - depends on FB_DEVICE depends on DRM_OMAP = n depends on GPIOLIB select FB_OMAP2_DSS @@ -13,6 +12,8 @@ menuconfig FB_OMAP2 select FB_IOMEM_HELPERS help Frame buffer driver for OMAP2+ based boards. + FB_DEVICE is not required, but if enabled, provides sysfs interface + for framebuffer configuration and debugging. if FB_OMAP2 diff --git a/drivers/video/fbdev/omap2/omapfb/dss/apply.c b/drivers/video/fbdev/omap2/omapfb/dss/apply.c index acca991c7540..39947e569a54 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/apply.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/apply.c @@ -6,6 +6,7 @@ #define DSS_SUBSYS_NAME "APPLY" +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index 55b640f2f245..02ea41f6c8f4 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/platform_device.h> #include <linux/seq_file.h> #include <linux/debugfs.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c index cc2ad787d493..7831c6a2eedb 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c @@ -6,6 +6,7 @@ #define DSS_SUBSYS_NAME "APPLY" +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> 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/omap2/omapfb/dss/display.c b/drivers/video/fbdev/omap2/omapfb/dss/display.c index f91db94c9905..16543425bd84 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/display.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/display.c @@ -11,6 +11,7 @@ #define DSS_SUBSYS_NAME "DISPLAY" +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/jiffies.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dpi.c b/drivers/video/fbdev/omap2/omapfb/dss/dpi.c index 86ed4c077c30..ad8ae1727966 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dpi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dpi.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/delay.h> -#include <linux/export.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/platform_device.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c index 1f3434c040c1..330d9fb7d2b0 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c @@ -835,7 +835,7 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) #ifdef DSI_CATCH_MISSING_TE if (irqstatus & DSI_IRQ_TE_TRIGGER) - del_timer(&dsi->te_timer); + timer_delete(&dsi->te_timer); #endif /* make a copy and unlock, so that isrs can unregister @@ -1539,7 +1539,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, unsigned long flags; struct dsi_irq_stats *stats; - stats = kzalloc(sizeof(*stats), GFP_KERNEL); + stats = kzalloc_obj(*stats); if (!stats) { seq_printf(s, "out of memory\n"); return; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c index 7c636db79882..f90a8eff7259 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c @@ -6,6 +6,7 @@ #include <linux/device.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c b/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c index 62c2d48d9e09..38be57ba8c28 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c @@ -6,6 +6,7 @@ * Author: Archit Taneja <archit@ti.com> */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/manager.c b/drivers/video/fbdev/omap2/omapfb/dss/manager.c index 2c2da35345d0..c81fe5e68e42 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/manager.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/manager.c @@ -11,6 +11,7 @@ #define DSS_SUBSYS_NAME "MANAGER" +#include <linux/export.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> @@ -31,8 +32,7 @@ int dss_init_overlay_managers(void) num_managers = dss_feat_get_num_mgrs(); - managers = kcalloc(num_managers, sizeof(struct omap_overlay_manager), - GFP_KERNEL); + managers = kzalloc_objs(struct omap_overlay_manager, num_managers); BUG_ON(managers == NULL); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/omapdss-boot-init.c b/drivers/video/fbdev/omap2/omapfb/dss/omapdss-boot-init.c index d80720c84323..154347e5ddaf 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/omapdss-boot-init.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/omapdss-boot-init.c @@ -46,7 +46,7 @@ static void __init omapdss_update_prop(struct device_node *node, char *compat, { struct property *prop; - prop = kzalloc(sizeof(*prop), GFP_KERNEL); + prop = kzalloc_obj(*prop); if (!prop) return; @@ -110,8 +110,7 @@ static void __init omapdss_omapify_node(struct device_node *node) static void __init omapdss_add_to_list(struct device_node *node, bool root) { - struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node), - GFP_KERNEL); + struct dss_conv_node *n = kmalloc_obj(struct dss_conv_node); if (n) { n->node = node; n->root = root; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/output.c b/drivers/video/fbdev/omap2/omapfb/dss/output.c index 4e2992a0ce50..48cbfb75443f 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/output.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/output.c @@ -4,6 +4,7 @@ * Author: Archit Taneja <archit@ti.com> */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/overlay.c b/drivers/video/fbdev/omap2/omapfb/dss/overlay.c index 8c8e627da13d..6b04162aa9fc 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/overlay.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/overlay.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/sysfs.h> #include <linux/platform_device.h> #include <linux/delay.h> @@ -48,8 +49,7 @@ void dss_init_overlays(struct platform_device *pdev) num_overlays = dss_feat_get_num_ovls(); - overlays = kcalloc(num_overlays, sizeof(struct omap_overlay), - GFP_KERNEL); + overlays = kzalloc_objs(struct omap_overlay, num_overlays); BUG_ON(overlays == NULL); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/sdi.c b/drivers/video/fbdev/omap2/omapfb/dss/sdi.c index 2d3e5d4467c5..68e569ec0f83 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/sdi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/sdi.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/regulator/consumer.h> -#include <linux/export.h> #include <linux/platform_device.h> #include <linux/string.h> #include <linux/of.h> diff --git a/drivers/video/fbdev/omap2/omapfb/dss/venc.c b/drivers/video/fbdev/omap2/omapfb/dss/venc.c index f99dda9e55a5..ed283029ad95 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/venc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/venc.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/io.h> #include <linux/mutex.h> #include <linux/completion.h> diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c index ea8c88aa4477..152dbeaa6451 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c @@ -16,7 +16,6 @@ #include <linux/mm.h> #include <linux/omapfb.h> #include <linux/vmalloc.h> -#include <linux/export.h> #include <linux/sizes.h> #include <video/omapfb_dss.h> diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index 211f23648686..d70deb6a9150 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -1121,7 +1121,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return 0; error: - omapfb_put_mem_region(ofbi->region); + omapfb_put_mem_region(rg); return r; } @@ -2023,19 +2023,19 @@ static int omapfb_mode_to_timings(const char *mode_str, var = NULL; fbops = NULL; - fbi = kzalloc(sizeof(*fbi), GFP_KERNEL); + fbi = kzalloc_obj(*fbi); if (fbi == NULL) { r = -ENOMEM; goto err; } - var = kzalloc(sizeof(*var), GFP_KERNEL); + var = kzalloc_obj(*var); if (var == NULL) { r = -ENOMEM; goto err; } - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); + fbops = kzalloc_obj(*fbops); if (fbops == NULL) { r = -ENOMEM; goto err; @@ -2244,7 +2244,7 @@ static int omapfb_find_best_mode(struct omap_dss_device *display, if (r < 0) goto err1; - specs = kzalloc(sizeof(*specs), GFP_KERNEL); + specs = kzalloc_obj(*specs); if (specs == NULL) { r = -ENOMEM; goto err1; diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c index 831b2c2fbdf9..38a635d38d58 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c @@ -558,10 +558,15 @@ int omapfb_create_sysfs(struct omapfb2_device *fbdev) DBG("create sysfs for fbs\n"); for (i = 0; i < fbdev->num_fbs; i++) { + struct device *dev; int t; + + dev = dev_of_fbinfo(fbdev->fbs[i]); + if (!dev) + continue; + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) { - r = device_create_file(fbdev->fbs[i]->dev, - &omapfb_attrs[t]); + r = device_create_file(dev, &omapfb_attrs[t]); if (r) { dev_err(fbdev->dev, "failed to create sysfs " @@ -580,9 +585,14 @@ void omapfb_remove_sysfs(struct omapfb2_device *fbdev) DBG("remove sysfs for fbs\n"); for (i = 0; i < fbdev->num_fbs; i++) { + struct device *dev; + + dev = dev_of_fbinfo(fbdev->fbs[i]); + if (!dev) + continue; + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) - device_remove_file(fbdev->fbs[i]->dev, - &omapfb_attrs[t]); + device_remove_file(dev, &omapfb_attrs[t]); } } diff --git a/drivers/video/fbdev/omap2/omapfb/vrfb.c b/drivers/video/fbdev/omap2/omapfb/vrfb.c index 568e6e1eca62..675482cde519 100644 --- a/drivers/video/fbdev/omap2/omapfb/vrfb.c +++ b/drivers/video/fbdev/omap2/omapfb/vrfb.c @@ -9,6 +9,7 @@ /*#define DEBUG*/ #include <linux/err.h> +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/ioport.h> diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index cbdb1caf61bd..3f6384e631b1 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -192,7 +192,7 @@ static unsigned long pvr2fb_map; #ifdef CONFIG_PVR2_DMA static unsigned int shdma = PVR2_CASCADE_CHAN; -static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS; +static unsigned int pvr2dma = CONFIG_NR_ONCHIP_DMA_CHANNELS; #endif static struct fb_videomode pvr2_modedb[] = { @@ -652,7 +652,7 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, nr_pages = (count + PAGE_SIZE - 1) >> PAGE_SHIFT; - pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); + pages = kmalloc_objs(struct page *, nr_pages); if (!pages) return -ENOMEM; diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index 4a78b387b343..a2320e2fb8f2 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c @@ -530,7 +530,7 @@ pxa3xx_gcu_add_buffer(struct device *dev, { struct pxa3xx_gcu_batch *buffer; - buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); + buffer = kzalloc_obj(struct pxa3xx_gcu_batch); if (!buffer) return -ENOMEM; diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index 4aa84853e31a..e418eee825fb 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -60,6 +60,7 @@ #include <linux/soc/pxa/cpu.h> #include <video/of_display_timing.h> #include <video/videomode.h> +#include <linux/string_choices.h> #include <asm/io.h> #include <asm/irq.h> @@ -418,12 +419,12 @@ static int pxafb_adjust_timing(struct pxafb_info *fbi, var->yres = max_t(int, var->yres, MIN_YRES); if (!(fbi->lccr0 & LCCR0_LCDT)) { - clamp_val(var->hsync_len, 1, 64); - clamp_val(var->vsync_len, 1, 64); - clamp_val(var->left_margin, 1, 255); - clamp_val(var->right_margin, 1, 255); - clamp_val(var->upper_margin, 1, 255); - clamp_val(var->lower_margin, 1, 255); + var->hsync_len = clamp(var->hsync_len, 1, 64); + var->vsync_len = clamp(var->vsync_len, 1, 64); + var->left_margin = clamp(var->left_margin, 1, 255); + var->right_margin = clamp(var->right_margin, 1, 255); + var->upper_margin = clamp(var->upper_margin, 1, 255); + var->lower_margin = clamp(var->lower_margin, 1, 255); } /* make sure each line is aligned on word boundary */ @@ -1030,9 +1031,8 @@ static inline unsigned int get_pcd(struct pxafb_info *fbi, /* * Some touchscreens need hsync information from the video driver to - * function correctly. We export it here. Note that 'hsync_time' and - * the value returned from pxafb_get_hsync_time() is the *reciprocal* - * of the hsync period in seconds. + * function correctly. We export it here. Note that 'hsync_time' is + * the *reciprocal* of the hsync period in seconds. */ static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) { @@ -1048,18 +1048,6 @@ static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) fbi->hsync_time = htime; } -unsigned long pxafb_get_hsync_time(struct device *dev) -{ - struct pxafb_info *fbi = dev_get_drvdata(dev); - - /* If display is blanked/suspended, hsync isn't active */ - if (!fbi || (fbi->state != C_ENABLE)) - return 0; - - return fbi->hsync_time; -} -EXPORT_SYMBOL(pxafb_get_hsync_time); - static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, unsigned long start, size_t size) { @@ -1432,7 +1420,7 @@ static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) if (ret < 0) pr_warn("Unable to %s LCD supply regulator: %d\n", - on ? "enable" : "disable", ret); + str_enable_disable(on), ret); else fbi->lcd_supply_enabled = on; } @@ -2233,32 +2221,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/riva/riva_hw.c b/drivers/video/fbdev/riva/riva_hw.c index 8b829b720064..f292079566cf 100644 --- a/drivers/video/fbdev/riva/riva_hw.c +++ b/drivers/video/fbdev/riva/riva_hw.c @@ -436,6 +436,9 @@ static char nv3_arb(nv3_fifo_info * res_info, nv3_sim_state * state, nv3_arb_in vmisses = 2; eburst_size = state->memory_width * 1; mburst_size = 32; + if (!state->mclk_khz) + return (0); + gns = 1000000 * (gmisses*state->mem_page_miss + state->mem_latency)/state->mclk_khz; ainfo->by_gfacc = gns*ainfo->gdrain_rate/1000000; ainfo->wcmocc = 0; diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index ff84106ecf1c..ba30e5568cab 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -50,10 +50,14 @@ struct s3fb_info { static const struct svga_fb_format s3fb_formats[] = { { 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, FB_TYPE_TEXT, FB_AUX_TEXT_SVGA_STEP4, FB_VISUAL_PSEUDOCOLOR, 8, 16}, - { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 0, - FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 16}, + { 1, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 0}, 2, + FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 32, 64}, + { 2, {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, 2, + FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 16, 32}, { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 1, FB_TYPE_INTERLEAVED_PLANES, 1, FB_VISUAL_PSEUDOCOLOR, 8, 16}, + { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 2, + FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 16}, { 8, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 4, 8}, {16, {10, 5, 0}, {5, 5, 0}, {0, 5, 0}, {0, 0, 0}, 0, @@ -557,7 +561,7 @@ static int s3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) /* 32bpp mode is not supported on VIRGE VX, 24bpp is not supported on others */ - if ((par->chip == CHIP_988_VIRGE_VX) ? (rv == 7) : (rv == 6)) + if ((par->chip == CHIP_988_VIRGE_VX) ? (rv == 9) : (rv == 8)) rv = -EINVAL; if (rv < 0) { @@ -607,7 +611,7 @@ static int s3fb_set_par(struct fb_info *info) struct s3fb_info *par = info->par; u32 value, mode, hmul, offset_value, screen_size, multiplex, dbytes; u32 bpp = info->var.bits_per_pixel; - u32 htotal, hsstart; + u32 htotal, hsstart, pel_msk; if (bpp != 0) { info->fix.ypanstep = 1; @@ -617,9 +621,11 @@ static int s3fb_set_par(struct fb_info *info) info->tileops = NULL; /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ - if (bpp == 4) { + if (bpp == 4 && (info->var.nonstd & 1) != 0) { + int i; bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); - set_bit(8 - 1, info->pixmap.blit_x); + for (i = 8; i <= FB_MAX_BLIT_WIDTH; i += 8) + set_bit(i - 1, info->pixmap.blit_x); } else { bitmap_fill(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); } @@ -730,7 +736,7 @@ static int s3fb_set_par(struct fb_info *info) vga_wcrt(par->state.vgabase, 0x50, 0x00); vga_wcrt(par->state.vgabase, 0x67, 0x50); msleep(10); /* screen remains blank sometimes without this */ - vga_wcrt(par->state.vgabase, 0x63, (mode <= 2) ? 0x90 : 0x09); + vga_wcrt(par->state.vgabase, 0x63, (mode <= 4) ? 0x90 : 0x09); vga_wcrt(par->state.vgabase, 0x66, 0x90); } @@ -763,12 +769,17 @@ static int s3fb_set_par(struct fb_info *info) svga_wcrt_mask(par->state.vgabase, 0x31, 0x00, 0x40); multiplex = 0; hmul = 1; + pel_msk = 0xff; + + svga_wcrt_mask(par->state.vgabase, 0x08, 0x00, 0x60); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x00, 0x60); /* Set mode-specific register values */ switch (mode) { case 0: fb_dbg(info, "text mode\n"); svga_set_textmode_vga_regs(par->state.vgabase); + pel_msk = 0x0f; /* Set additional registers like in 8-bit mode */ svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); @@ -783,8 +794,11 @@ static int s3fb_set_par(struct fb_info *info) } break; case 1: - fb_dbg(info, "4 bit pseudocolor\n"); - vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); + fb_dbg(info, "1 bit pseudocolor\n"); + svga_wseq_mask(par->state.vgabase, 0x01, 0x10, 0x14); + svga_wcrt_mask(par->state.vgabase, 0x08, 0x60, 0x60); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x40, 0x60); + pel_msk = 0x01; /* Set additional registers like in 8-bit mode */ svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); @@ -794,7 +808,13 @@ static int s3fb_set_par(struct fb_info *info) svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); break; case 2: - fb_dbg(info, "4 bit pseudocolor, planar\n"); + fb_dbg(info, "2 bit pseudocolor\n"); + svga_wseq_mask(par->state.vgabase, 0x01, 0x04, 0x14); + svga_wseq_mask(par->state.vgabase, 0x04, 0x08, 0x08); + vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x20); + svga_wcrt_mask(par->state.vgabase, 0x08, 0x20, 0x60); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x40, 0x60); + pel_msk = 0x03; /* Set additional registers like in 8-bit mode */ svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); @@ -804,8 +824,35 @@ static int s3fb_set_par(struct fb_info *info) svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); break; case 3: + fb_dbg(info, "4 bit pseudocolor, planar\n"); + pel_msk = 0x0f; + + /* Set additional registers like in 8-bit mode */ + svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); + svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x40, 0x60); + + /* disable enhanced mode */ + svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); + break; + case 4: + fb_dbg(info, "4 bit pseudocolor\n"); + vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); + svga_wattr(par->state.vgabase, 0x33, 0x01); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x40, 0x60); + pel_msk = 0xf0; + + /* Set additional registers like in 8-bit mode */ + svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); + svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); + + /* disable enhanced mode */ + svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); + break; + case 5: fb_dbg(info, "8 bit pseudocolor\n"); svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); + svga_wcrt_mask(par->state.vgabase, 0x05, 0x20, 0x60); if (info->var.pixclock > 20000 || par->chip == CHIP_357_VIRGE_GX2 || par->chip == CHIP_359_VIRGE_GX2P || @@ -819,7 +866,7 @@ static int s3fb_set_par(struct fb_info *info) multiplex = 1; } break; - case 4: + case 6: fb_dbg(info, "5/5/5 truecolor\n"); if (par->chip == CHIP_988_VIRGE_VX) { if (info->var.pixclock > 20000) @@ -847,7 +894,7 @@ static int s3fb_set_par(struct fb_info *info) hmul = 2; } break; - case 5: + case 7: fb_dbg(info, "5/6/5 truecolor\n"); if (par->chip == CHIP_988_VIRGE_VX) { if (info->var.pixclock > 20000) @@ -875,12 +922,12 @@ static int s3fb_set_par(struct fb_info *info) hmul = 2; } break; - case 6: + case 8: /* VIRGE VX case */ fb_dbg(info, "8/8/8 truecolor\n"); svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); break; - case 7: + case 9: fb_dbg(info, "8/8/8/8 truecolor\n"); svga_wcrt_mask(par->state.vgabase, 0x50, 0x30, 0x30); svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); @@ -889,6 +936,7 @@ static int s3fb_set_par(struct fb_info *info) fb_err(info, "unsupported mode - bug\n"); return -EINVAL; } + vga_w(par->state.vgabase, VGA_PEL_MSK, pel_msk); if (par->chip != CHIP_988_VIRGE_VX) { svga_wseq_mask(par->state.vgabase, 0x15, multiplex ? 0x10 : 0x00, 0x10); @@ -927,33 +975,26 @@ static int s3fb_set_par(struct fb_info *info) static int s3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *fb) { + struct s3fb_info *par = fb->par; + int cols; + switch (fb->var.bits_per_pixel) { case 0: + case 1: + case 2: case 4: - if (regno >= 16) - return -EINVAL; - - if ((fb->var.bits_per_pixel == 4) && - (fb->var.nonstd == 0)) { - outb(0xF0, VGA_PEL_MSK); - outb(regno*16, VGA_PEL_IW); - } else { - outb(0x0F, VGA_PEL_MSK); - outb(regno, VGA_PEL_IW); - } - outb(red >> 10, VGA_PEL_D); - outb(green >> 10, VGA_PEL_D); - outb(blue >> 10, VGA_PEL_D); - break; case 8: - if (regno >= 256) + cols = 1 << (fb->var.bits_per_pixel ? fb->var.bits_per_pixel : 4); + if (regno >= cols) return -EINVAL; - outb(0xFF, VGA_PEL_MSK); - outb(regno, VGA_PEL_IW); - outb(red >> 10, VGA_PEL_D); - outb(green >> 10, VGA_PEL_D); - outb(blue >> 10, VGA_PEL_D); + if ((fb->var.bits_per_pixel == 4) && ((fb->var.nonstd & 1) == 0)) + regno <<= 4; + + vga_w(par->state.vgabase, VGA_PEL_IW, regno); + vga_w(par->state.vgabase, VGA_PEL_D, red >> 10); + vga_w(par->state.vgabase, VGA_PEL_D, green >> 10); + vga_w(par->state.vgabase, VGA_PEL_D, blue >> 10); break; case 16: if (regno >= 16) @@ -988,34 +1029,30 @@ static int s3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static int s3fb_blank(int blank_mode, struct fb_info *info) { struct s3fb_info *par = info->par; + u8 data; + + data = (blank_mode == FB_BLANK_UNBLANK) ? 0x00 : 0x20; + svga_wseq_mask(par->state.vgabase, 0x01, data, 0x20); + svga_wseq_mask(par->state.vgabase, 0x18, data, 0x20); switch (blank_mode) { - case FB_BLANK_UNBLANK: - fb_dbg(info, "unblank\n"); - svga_wcrt_mask(par->state.vgabase, 0x56, 0x00, 0x06); - svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); - break; - case FB_BLANK_NORMAL: - fb_dbg(info, "blank\n"); - svga_wcrt_mask(par->state.vgabase, 0x56, 0x00, 0x06); - svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); + default: + data = 0x00; break; case FB_BLANK_HSYNC_SUSPEND: - fb_dbg(info, "hsync\n"); - svga_wcrt_mask(par->state.vgabase, 0x56, 0x02, 0x06); - svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); + data = 0x02; break; case FB_BLANK_VSYNC_SUSPEND: - fb_dbg(info, "vsync\n"); - svga_wcrt_mask(par->state.vgabase, 0x56, 0x04, 0x06); - svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); + data = 0x04; break; case FB_BLANK_POWERDOWN: - fb_dbg(info, "sync down\n"); - svga_wcrt_mask(par->state.vgabase, 0x56, 0x06, 0x06); - svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); + data = 0x06; break; } + svga_wcrt_mask(par->state.vgabase, 0x56, data, 0x06); + + data = (blank_mode == FB_BLANK_POWERDOWN) ? 0x01 : 0x00; + svga_wseq_mask(par->state.vgabase, 0x14, data, 0x01); return 0; } @@ -1045,6 +1082,33 @@ static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } +/* Get capabilities of accelerator based on the mode */ + +static void s3fb_get_caps(struct fb_info *info, struct fb_blit_caps *caps, + struct fb_var_screeninfo *var) +{ + int i; + + if (var->bits_per_pixel == 0) { + /* can only support 256 8x16 bitmap */ + bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, caps->x); + bitmap_zero(caps->y, FB_MAX_BLIT_HEIGHT); + set_bit(16 - 1, caps->y); + caps->len = 256; + } else { + if (var->bits_per_pixel == 4 && (var->nonstd & 1) != 0) { + bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH); + for (i = 8; i <= FB_MAX_BLIT_WIDTH; i += 8) + set_bit(i - 1, caps->x); + } else { + bitmap_fill(caps->x, FB_MAX_BLIT_WIDTH); + } + bitmap_fill(caps->y, FB_MAX_BLIT_HEIGHT); + caps->len = ~(u32)0; + } +} + /* ------------------------------------------------------------------------- */ /* Frame buffer operations */ @@ -1063,7 +1127,7 @@ static const struct fb_ops s3fb_ops = { .fb_copyarea = cfb_copyarea, .fb_imageblit = s3fb_imageblit, __FB_DEFAULT_IOMEM_OPS_MMAP, - .fb_get_caps = svga_get_caps, + .fb_get_caps = s3fb_get_caps, }; /* ------------------------------------------------------------------------- */ @@ -1445,6 +1509,8 @@ static int __maybe_unused s3_pci_suspend(struct device *dev) } fb_set_suspend(info, 1); + svga_wseq_mask(par->state.vgabase, 0x18, 0x20, 0x20); + svga_wseq_mask(par->state.vgabase, 0x14, 0x01, 0x01); mutex_unlock(&(par->open_lock)); console_unlock(); @@ -1471,6 +1537,9 @@ static int __maybe_unused s3_pci_resume(struct device *dev) return 0; } + vga_wseq(par->state.vgabase, 0x08, 0x06); + svga_wseq_mask(par->state.vgabase, 0x18, 0x00, 0x20); + svga_wseq_mask(par->state.vgabase, 0x14, 0x00, 0x01); s3fb_set_par(info); fb_set_suspend(info, 0); diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index ac41f8f37589..c2f79357c8da 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -2322,6 +2322,8 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) failed: #ifdef CONFIG_FB_SAVAGE_I2C savagefb_delete_i2c_busses(info); + fb_destroy_modelist(&info->modelist); + fb_destroy_modedb(info->monspecs.modedb); #endif fb_alloc_cmap(&info->cmap, 0, 0); savage_unmap_video(info); diff --git a/drivers/video/fbdev/sbuslib.c b/drivers/video/fbdev/sbuslib.c index 4c79654bda30..dd2002d0810f 100644 --- a/drivers/video/fbdev/sbuslib.c +++ b/drivers/video/fbdev/sbuslib.c @@ -5,6 +5,7 @@ */ #include <linux/compat.h> +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c index 4715dcb59811..72969fe8e513 100644 --- a/drivers/video/fbdev/sh_mobile_lcdcfb.c +++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c @@ -1338,17 +1338,23 @@ 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[] __maybe_unused = { + &dev_attr_overlay_alpha.attr, + &dev_attr_overlay_mode.attr, + &dev_attr_overlay_position.attr, + &dev_attr_overlay_rop3.attr, + NULL, }; +#ifdef CONFIG_FB_DEVICE +ATTRIBUTE_GROUPS(overlay_sysfs); +#endif + static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { .id = "SH Mobile LCDC", .type = FB_TYPE_PACKED_PIXELS, @@ -1516,7 +1522,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 +1535,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; } @@ -2513,7 +2512,7 @@ static int sh_mobile_lcdc_probe(struct platform_device *pdev) return -ENOENT; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -2641,6 +2640,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/simplefb.c b/drivers/video/fbdev/simplefb.c index be95fcddce4c..60e5dcec201f 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -13,18 +13,18 @@ */ #include <linux/aperture.h> +#include <linux/clk.h> #include <linux/errno.h> #include <linux/fb.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/platform_data/simplefb.h> -#include <linux/platform_device.h> -#include <linux/clk.h> #include <linux/of.h> -#include <linux/of_address.h> #include <linux/of_clk.h> #include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> #include <linux/parser.h> +#include <linux/platform_data/simplefb.h> +#include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/regulator/consumer.h> @@ -93,6 +93,7 @@ struct simplefb_par { static void simplefb_clocks_destroy(struct simplefb_par *par); static void simplefb_regulators_destroy(struct simplefb_par *par); +static void simplefb_detach_genpds(void *res); /* * fb_ops.fb_destroy is called by the last put_fb_info() call at the end @@ -105,6 +106,7 @@ static void simplefb_destroy(struct fb_info *info) simplefb_regulators_destroy(info->par); simplefb_clocks_destroy(info->par); + simplefb_detach_genpds(info->par); if (info->screen_base) iounmap(info->screen_base); @@ -134,7 +136,7 @@ struct simplefb_params { static int simplefb_parse_dt(struct platform_device *pdev, struct simplefb_params *params) { - struct device_node *np = pdev->dev.of_node, *mem; + struct device_node *np = pdev->dev.of_node; int ret; const char *format; int i; @@ -174,19 +176,10 @@ static int simplefb_parse_dt(struct platform_device *pdev, return -EINVAL; } - mem = of_parse_phandle(np, "memory-region", 0); - if (mem) { - ret = of_address_to_resource(mem, 0, ¶ms->memory); - if (ret < 0) { - dev_err(&pdev->dev, "failed to parse memory-region\n"); - of_node_put(mem); - return ret; - } - + ret = of_reserved_mem_region_to_resource(np, 0, ¶ms->memory); + if (!ret) { if (of_property_present(np, "reg")) dev_warn(&pdev->dev, "preferring \"memory-region\" over \"reg\" property\n"); - - of_node_put(mem); } else { memset(¶ms->memory, 0, sizeof(params->memory)); } @@ -256,7 +249,7 @@ static int simplefb_clocks_get(struct simplefb_par *par, if (!par->clk_count) return 0; - par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); + par->clks = kzalloc_objs(struct clk *, par->clk_count); if (!par->clks) return -ENOMEM; @@ -454,13 +447,14 @@ static void simplefb_detach_genpds(void *res) if (!IS_ERR_OR_NULL(par->genpds[i])) dev_pm_domain_detach(par->genpds[i], true); } + par->num_genpds = 0; } static int simplefb_attach_genpds(struct simplefb_par *par, struct platform_device *pdev) { struct device *dev = &pdev->dev; - unsigned int i; + unsigned int i, num_genpds; int err; err = of_count_phandle_with_args(dev->of_node, "power-domains", @@ -474,26 +468,35 @@ static int simplefb_attach_genpds(struct simplefb_par *par, return err; } - par->num_genpds = err; + num_genpds = err; /* * Single power-domain devices are handled by the driver core, so * nothing to do here. */ - if (par->num_genpds <= 1) + if (num_genpds <= 1) { + par->num_genpds = num_genpds; return 0; + } - par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds), + par->genpds = devm_kcalloc(dev, num_genpds, sizeof(*par->genpds), GFP_KERNEL); if (!par->genpds) return -ENOMEM; - par->genpd_links = devm_kcalloc(dev, par->num_genpds, + par->genpd_links = devm_kcalloc(dev, num_genpds, sizeof(*par->genpd_links), GFP_KERNEL); if (!par->genpd_links) return -ENOMEM; + /* + * Set par->num_genpds only after genpds and genpd_links are allocated + * to exit early from simplefb_detach_genpds() without full + * initialisation. + */ + par->num_genpds = num_genpds; + for (i = 0; i < par->num_genpds; i++) { par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); if (IS_ERR(par->genpds[i])) { @@ -515,9 +518,10 @@ static int simplefb_attach_genpds(struct simplefb_par *par, dev_warn(dev, "failed to link power-domain %u\n", i); } - return devm_add_action_or_reset(dev, simplefb_detach_genpds, par); + return 0; } #else +static void simplefb_detach_genpds(void *res) { } static int simplefb_attach_genpds(struct simplefb_par *par, struct platform_device *pdev) { @@ -631,18 +635,20 @@ static int simplefb_probe(struct platform_device *pdev) ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); if (ret) { dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); - goto error_regulators; + goto error_genpds; } ret = register_framebuffer(info); if (ret < 0) { dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); - goto error_regulators; + goto error_genpds; } dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); return 0; +error_genpds: + simplefb_detach_genpds(par); error_regulators: simplefb_regulators_destroy(par); error_clocks: diff --git a/drivers/video/fbdev/sis/sis.h b/drivers/video/fbdev/sis/sis.h index d632f096083b..3d658482c69d 100644 --- a/drivers/video/fbdev/sis/sis.h +++ b/drivers/video/fbdev/sis/sis.h @@ -673,9 +673,7 @@ unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg); /* SiS-specific exported functions */ void sis_malloc(struct sis_memreq *req); -void sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req); void sis_free(u32 base); -void sis_free_new(struct pci_dev *pdev, u32 base); /* Routines from init.c/init301.c */ extern unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay, diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c index 75033e6be15a..84567d67f71d 100644 --- a/drivers/video/fbdev/sis/sis_main.c +++ b/drivers/video/fbdev/sis/sis_main.c @@ -3421,14 +3421,6 @@ sis_malloc(struct sis_memreq *req) req->offset = req->size = 0; } -void -sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req) -{ - struct sis_video_info *ivideo = pci_get_drvdata(pdev); - - sis_int_malloc(ivideo, req); -} - /* sis_free: u32 because "base" is offset inside video ram, can never be >4GB */ static void @@ -3455,14 +3447,6 @@ sis_free(u32 base) sis_int_free(ivideo, base); } -void -sis_free_new(struct pci_dev *pdev, u32 base) -{ - struct sis_video_info *ivideo = pci_get_drvdata(pdev); - - sis_int_free(ivideo, base); -} - /* --------------------- SetMode routines ------------------------- */ static void @@ -6832,12 +6816,3 @@ MODULE_PARM_DESC(videoram, #endif #endif /* /MODULE */ - -/* _GPL only for new symbols. */ -EXPORT_SYMBOL(sis_malloc); -EXPORT_SYMBOL(sis_free); -EXPORT_SYMBOL_GPL(sis_malloc_new); -EXPORT_SYMBOL_GPL(sis_free_new); - - - diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index 7734377b2d87..fee4b9f84592 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) @@ -1940,7 +1947,7 @@ static int sm501fb_probe(struct platform_device *pdev) int ret; /* allocate our framebuffers */ - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) { dev_err(dev, "failed to allocate state\n"); return -ENOMEM; diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c index 5f0dd01fd834..5704f60e1741 100644 --- a/drivers/video/fbdev/smscufx.c +++ b/drivers/video/fbdev/smscufx.c @@ -932,7 +932,6 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { struct ufx_data *dev = info->par; - struct dloarea *area = NULL; if (!atomic_read(&dev->usb_active)) return 0; @@ -947,6 +946,10 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ if (cmd == UFX_IOCTL_REPORT_DAMAGE) { + struct dloarea *area __free(kfree) = kmalloc_obj(*area); + if (!area) + return -ENOMEM; + /* If we have a damage-aware client, turn fb_defio "off" * To avoid perf imact of unnecessary page fault handling. * Done by resetting the delay for this fb_info to a very @@ -956,7 +959,8 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, if (info->fbdefio) info->fbdefio->delay = UFX_DEFIO_WRITE_DISABLE; - area = (struct dloarea *)arg; + if (copy_from_user(area, (u8 __user *)arg, sizeof(*area))) + return -EFAULT; if (area->x < 0) area->x = 0; @@ -1033,7 +1037,7 @@ static int ufx_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL); + fbdefio = kzalloc_obj(*fbdefio); if (fbdefio) { fbdefio->delay = UFX_DEFIO_WRITE_DELAY; fbdefio->deferred_io = ufx_dpy_deferred_io; @@ -1590,7 +1594,7 @@ static int ufx_usb_probe(struct usb_interface *interface, usbdev = interface_to_usbdev(interface); BUG_ON(!usbdev); - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (dev == NULL) { dev_err(&usbdev->dev, "ufx_usb_probe: failed alloc of dev struct\n"); return -ENOMEM; @@ -1847,7 +1851,7 @@ static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size) INIT_LIST_HEAD(&dev->urbs.list); while (i < count) { - unode = kzalloc(sizeof(*unode), GFP_KERNEL); + unode = kzalloc_obj(*unode); if (!unode) break; unode->dev = dev; diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index aa6cc0a8151a..83dd31fa1fab 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -680,7 +680,7 @@ static int ssd1307fb_probe(struct i2c_client *client) if (!ssd1307fb_defio) { dev_err(dev, "Couldn't allocate deferred io.\n"); ret = -ENOMEM; - goto fb_alloc_error; + goto fb_defio_error; } ssd1307fb_defio->delay = HZ / refreshrate; @@ -757,6 +757,8 @@ regulator_enable_error: regulator_disable(par->vbat_reg); reset_oled_error: fb_deferred_io_cleanup(info); +fb_defio_error: + __free_pages(vmem, get_order(vmem_size)); fb_alloc_error: framebuffer_release(info); return ret; diff --git a/drivers/video/fbdev/tcx.c b/drivers/video/fbdev/tcx.c index f9a0085ad72b..ca9e84e8d860 100644 --- a/drivers/video/fbdev/tcx.c +++ b/drivers/video/fbdev/tcx.c @@ -428,7 +428,7 @@ static int tcx_probe(struct platform_device *op) j = i; break; } - par->mmap_map[i].poff = op->resource[j].start; + par->mmap_map[i].poff = op->resource[j].start - info->fix.smem_start; } info->fbops = &tcx_ops; diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 51ebe78359ec..4c4e53aaea3a 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -77,6 +77,7 @@ #include <asm/io.h> #include <video/tdfx.h> +#include <video/vga.h> #define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b) @@ -496,6 +497,9 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) } } + if (!var->pixclock) + return -EINVAL; + if (PICOS2KHZ(var->pixclock) > par->max_pixclock) { DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock)); @@ -591,7 +595,7 @@ static int tdfxfb_set_par(struct fb_info *info) vt = ve + (info->var.upper_margin << 1) - 1; reg.screensize = info->var.xres | (info->var.yres << 13); reg.vidcfg |= VIDCFG_HALF_MODE; - reg.crt[0x09] = 0x80; + reg.crt[VGA_CRTC_MAX_SCAN] = 0x80; } else { vd = info->var.yres - 1; vs = vd + info->var.lower_margin; @@ -609,59 +613,59 @@ static int tdfxfb_set_par(struct fb_info *info) info->var.xres < 480 ? 0x60 : info->var.xres < 768 ? 0xe0 : 0x20); - reg.gra[0x05] = 0x40; - reg.gra[0x06] = 0x05; - reg.gra[0x07] = 0x0f; - reg.gra[0x08] = 0xff; - - reg.att[0x00] = 0x00; - reg.att[0x01] = 0x01; - reg.att[0x02] = 0x02; - reg.att[0x03] = 0x03; - reg.att[0x04] = 0x04; - reg.att[0x05] = 0x05; - reg.att[0x06] = 0x06; - reg.att[0x07] = 0x07; - reg.att[0x08] = 0x08; - reg.att[0x09] = 0x09; - reg.att[0x0a] = 0x0a; - reg.att[0x0b] = 0x0b; - reg.att[0x0c] = 0x0c; - reg.att[0x0d] = 0x0d; - reg.att[0x0e] = 0x0e; - reg.att[0x0f] = 0x0f; - reg.att[0x10] = 0x41; - reg.att[0x12] = 0x0f; - - reg.seq[0x00] = 0x03; - reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */ - reg.seq[0x02] = 0x0f; - reg.seq[0x03] = 0x00; - reg.seq[0x04] = 0x0e; - - reg.crt[0x00] = ht - 4; - reg.crt[0x01] = hd; - reg.crt[0x02] = hbs; - reg.crt[0x03] = 0x80 | (hbe & 0x1f); - reg.crt[0x04] = hs; - reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); - reg.crt[0x06] = vt; - reg.crt[0x07] = ((vs & 0x200) >> 2) | - ((vd & 0x200) >> 3) | - ((vt & 0x200) >> 4) | 0x10 | - ((vbs & 0x100) >> 5) | - ((vs & 0x100) >> 6) | - ((vd & 0x100) >> 7) | - ((vt & 0x100) >> 8); - reg.crt[0x09] |= 0x40 | ((vbs & 0x200) >> 4); - reg.crt[0x10] = vs; - reg.crt[0x11] = (ve & 0x0f) | 0x20; - reg.crt[0x12] = vd; - reg.crt[0x13] = wd; - reg.crt[0x15] = vbs; - reg.crt[0x16] = vbe + 1; - reg.crt[0x17] = 0xc3; - reg.crt[0x18] = 0xff; + reg.gra[VGA_GFX_MODE] = 0x40; + reg.gra[VGA_GFX_MISC] = 0x05; + reg.gra[VGA_GFX_COMPARE_MASK] = 0x0f; + reg.gra[VGA_GFX_BIT_MASK] = 0xff; + + reg.att[VGA_ATC_PALETTE0] = 0x00; + reg.att[VGA_ATC_PALETTE1] = 0x01; + reg.att[VGA_ATC_PALETTE2] = 0x02; + reg.att[VGA_ATC_PALETTE3] = 0x03; + reg.att[VGA_ATC_PALETTE4] = 0x04; + reg.att[VGA_ATC_PALETTE5] = 0x05; + reg.att[VGA_ATC_PALETTE6] = 0x06; + reg.att[VGA_ATC_PALETTE7] = 0x07; + reg.att[VGA_ATC_PALETTE8] = 0x08; + reg.att[VGA_ATC_PALETTE9] = 0x09; + reg.att[VGA_ATC_PALETTEA] = 0x0a; + reg.att[VGA_ATC_PALETTEB] = 0x0b; + reg.att[VGA_ATC_PALETTEC] = 0x0c; + reg.att[VGA_ATC_PALETTED] = 0x0d; + reg.att[VGA_ATC_PALETTEE] = 0x0e; + reg.att[VGA_ATC_PALETTEF] = 0x0f; + reg.att[VGA_ATC_MODE] = 0x41; + reg.att[VGA_ATC_PLANE_ENABLE] = 0x0f; + + reg.seq[VGA_SEQ_RESET] = 0x03; + reg.seq[VGA_SEQ_CLOCK_MODE] = 0x01; /* fixme: clkdiv2? */ + reg.seq[VGA_SEQ_PLANE_WRITE] = 0x0f; + reg.seq[VGA_SEQ_CHARACTER_MAP] = 0x00; + reg.seq[VGA_SEQ_MEMORY_MODE] = 0x0e; + + reg.crt[VGA_CRTC_H_TOTAL] = ht - 4; + reg.crt[VGA_CRTC_H_DISP] = hd; + reg.crt[VGA_CRTC_H_BLANK_START] = hbs; + reg.crt[VGA_CRTC_H_BLANK_END] = 0x80 | (hbe & 0x1f); + reg.crt[VGA_CRTC_H_SYNC_START] = hs; + reg.crt[VGA_CRTC_H_SYNC_END] = ((hbe & 0x20) << 2) | (he & 0x1f); + reg.crt[VGA_CRTC_V_TOTAL] = vt; + reg.crt[VGA_CRTC_OVERFLOW] = ((vs & 0x200) >> 2) | + ((vd & 0x200) >> 3) | + ((vt & 0x200) >> 4) | 0x10 | + ((vbs & 0x100) >> 5) | + ((vs & 0x100) >> 6) | + ((vd & 0x100) >> 7) | + ((vt & 0x100) >> 8); + reg.crt[VGA_CRTC_MAX_SCAN] |= 0x40 | ((vbs & 0x200) >> 4); + reg.crt[VGA_CRTC_V_SYNC_START] = vs; + reg.crt[VGA_CRTC_V_SYNC_END] = (ve & 0x0f) | 0x20; + reg.crt[VGA_CRTC_V_DISP_END] = vd; + reg.crt[VGA_CRTC_OFFSET] = wd; + reg.crt[VGA_CRTC_V_BLANK_START] = vbs; + reg.crt[VGA_CRTC_V_BLANK_END] = vbe + 1; + reg.crt[VGA_CRTC_MODE] = 0xc3; + reg.crt[VGA_CRTC_LINE_COMPARE] = 0xff; /* Banshee's nonvga stuff */ reg.ext[0x00] = (((ht & 0x100) >> 8) | diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 516cf2a18757..17b7253b8fbe 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -1631,7 +1631,7 @@ static int trident_pci_probe(struct pci_dev *dev, } if (noaccel) { - printk(KERN_DEBUG "disabling acceleration\n"); + dev_dbg(&dev->dev, "disabling acceleration\n"); info->flags |= FBINFO_HWACCEL_DISABLED; info->pixmap.scan_align = 1; } @@ -1693,7 +1693,7 @@ static int trident_pci_probe(struct pci_dev *dev, info->var.activate |= FB_ACTIVATE_NOW; info->device = &dev->dev; if (register_framebuffer(info) < 0) { - printk(KERN_ERR "tridentfb: could not register framebuffer\n"); + dev_err(&dev->dev, "could not register framebuffer\n"); fb_dealloc_cmap(&info->cmap); err = -EINVAL; goto out_unmap2; diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index acadf0eb450c..fdbb8671a810 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -321,12 +321,32 @@ static int dlfb_set_video_mode(struct dlfb_data *dlfb, return retval; } +static void dlfb_vm_open(struct vm_area_struct *vma) +{ + struct dlfb_data *dlfb = vma->vm_private_data; + + atomic_inc(&dlfb->mmap_count); +} + +static void dlfb_vm_close(struct vm_area_struct *vma) +{ + struct dlfb_data *dlfb = vma->vm_private_data; + + atomic_dec(&dlfb->mmap_count); +} + +static const struct vm_operations_struct dlfb_vm_ops = { + .open = dlfb_vm_open, + .close = dlfb_vm_close, +}; + static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) { unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; + struct dlfb_data *dlfb = info->par; if (info->fbdefio) return fb_deferred_io_mmap(info, vma); @@ -358,6 +378,9 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) size = 0; } + vma->vm_ops = &dlfb_vm_ops; + vma->vm_private_data = dlfb; + atomic_inc(&dlfb->mmap_count); return 0; } @@ -918,7 +941,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc_obj(struct fb_deferred_io); if (fbdefio) { fbdefio->delay = DL_DEFIO_WRITE_DELAY; @@ -1018,6 +1041,9 @@ static int dlfb_ops_check_var(struct fb_var_screeninfo *var, struct fb_videomode mode; struct dlfb_data *dlfb = info->par; + if (!var->pixclock) + return -EINVAL; + /* set device-specific elements of var unrelated to mode */ dlfb_var_color_format(var); @@ -1164,7 +1190,7 @@ static const struct fb_ops dlfb_ops = { static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) { - struct dlfb_deferred_free *d = kmalloc(sizeof(struct dlfb_deferred_free), GFP_KERNEL); + struct dlfb_deferred_free *d = kmalloc_obj(struct dlfb_deferred_free); if (!d) return; d->mem = mem; @@ -1173,7 +1199,6 @@ static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) /* * Assumes &info->lock held by caller - * Assumes no active clients have framebuffer open */ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) { @@ -1185,6 +1210,13 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info new_len = PAGE_ALIGN(new_len); if (new_len > old_len) { + if (atomic_read(&dlfb->mmap_count) > 0) { + dev_warn(info->dev, + "refusing realloc: %d active mmaps\n", + atomic_read(&dlfb->mmap_count)); + return -EBUSY; + } + /* * Alloc system memory for virtual framebuffer */ @@ -1482,8 +1514,8 @@ static const struct bin_attribute edid_attr = { .attr.name = "edid", .attr.mode = 0666, .size = EDID_LENGTH, - .read_new = edid_show, - .write_new = edid_store + .read = edid_show, + .write = edid_store }; static const struct device_attribute fb_device_attrs[] = { @@ -1606,7 +1638,7 @@ static int dlfb_usb_probe(struct usb_interface *intf, static u8 out_ep[] = {OUT_EP_NUM + USB_DIR_OUT, 0}; /* usb initialization */ - dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); + dlfb = kzalloc_obj(*dlfb); if (!dlfb) { dev_err(&intf->dev, "%s: failed to allocate dlfb\n", __func__); return -ENOMEM; @@ -1852,7 +1884,7 @@ retry: dlfb->urbs.available = 0; while (dlfb->urbs.count * size < wanted_size) { - unode = kzalloc(sizeof(*unode), GFP_KERNEL); + unode = kzalloc_obj(*unode); if (!unode) break; unode->dlfb = dlfb; diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 5d52fd00806e..88667fccc27b 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -258,9 +258,9 @@ static struct uvesafb_ktask *uvesafb_prep(void) { struct uvesafb_ktask *task; - task = kzalloc(sizeof(*task), GFP_KERNEL); + task = kzalloc_obj(*task); if (task) { - task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); + task->done = kzalloc_obj(*task->done); if (!task->done) { kfree(task); task = NULL; @@ -487,9 +487,7 @@ static int uvesafb_vbe_getmodes(struct uvesafb_ktask *task, mode++; } - par->vbe_modes = kcalloc(par->vbe_modes_cnt, - sizeof(struct vbe_mode_ib), - GFP_KERNEL); + par->vbe_modes = kzalloc_objs(struct vbe_mode_ib, par->vbe_modes_cnt); if (!par->vbe_modes) return -ENOMEM; @@ -862,7 +860,7 @@ static int uvesafb_vbe_init_mode(struct fb_info *info) * Convert the modelist into a modedb so that we can use it with * fb_find_mode(). */ - mode = kcalloc(i, sizeof(*mode), GFP_KERNEL); + mode = kzalloc_objs(*mode, i); if (mode) { i = 0; list_for_each(pos, &info->modelist) { @@ -1048,8 +1046,7 @@ static int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) info->cmap.len || cmap->start < info->cmap.start) return -EINVAL; - entries = kmalloc_array(cmap->len, sizeof(*entries), - GFP_KERNEL); + entries = kmalloc_objs(*entries, cmap->len); if (!entries) return -ENOMEM; @@ -1243,7 +1240,7 @@ setmode: info->var.pixclock != 0) { task->t.regs.ebx |= 0x0800; /* use CRTC data */ task->t.flags = TF_BUF_ESDI; - crtc = kzalloc(sizeof(struct vbe_crtc_ib), GFP_KERNEL); + crtc = kzalloc_obj(struct vbe_crtc_ib); if (!crtc) { err = -ENOMEM; goto out; diff --git a/drivers/video/fbdev/valkyriefb.c b/drivers/video/fbdev/valkyriefb.c index 91d070ef6989..ec3cf4589056 100644 --- a/drivers/video/fbdev/valkyriefb.c +++ b/drivers/video/fbdev/valkyriefb.c @@ -329,15 +329,17 @@ static int __init valkyriefb_init(void) if (of_address_to_resource(dp, 0, &r)) { printk(KERN_ERR "can't find address for valkyrie\n"); + of_node_put(dp); return 0; } frame_buffer_phys = r.start; cmap_regs_phys = r.start + 0x304000; + of_node_put(dp); } #endif /* ppc (!CONFIG_MAC) */ - p = kzalloc(sizeof(*p), GFP_ATOMIC); + p = kzalloc_obj(*p, GFP_ATOMIC); if (!p) return -ENOMEM; diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c index a81df8865143..f84f4db244bf 100644 --- a/drivers/video/fbdev/vesafb.c +++ b/drivers/video/fbdev/vesafb.c @@ -20,7 +20,7 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/screen_info.h> +#include <linux/sysfb.h> #include <linux/io.h> #include <video/vga.h> @@ -243,6 +243,7 @@ static int vesafb_setup(char *options) static int vesafb_probe(struct platform_device *dev) { + struct sysfb_display_info *dpy; struct screen_info *si; struct fb_info *info; struct vesafb_par *par; @@ -257,10 +258,11 @@ static int vesafb_probe(struct platform_device *dev) * driver. We get a copy of the attached screen_info, so that we can * modify its values without affecting later drivers. */ - si = dev_get_platdata(&dev->dev); - if (!si) + dpy = dev_get_platdata(&dev->dev); + if (!dpy) return -ENODEV; - si = devm_kmemdup(&dev->dev, si, sizeof(*si), GFP_KERNEL); + + si = devm_kmemdup(&dev->dev, &dpy->screen, sizeof(*si), GFP_KERNEL); if (!si) return -ENOMEM; @@ -314,8 +316,8 @@ static int vesafb_probe(struct platform_device *dev) #endif if (!request_mem_region(vesafb_fix.smem_start, size_total, "vesafb")) { - printk(KERN_WARNING - "vesafb: cannot reserve video memory at 0x%lx\n", + dev_warn(&dev->dev, + "cannot reserve video memory at 0x%lx\n", vesafb_fix.smem_start); /* We cannot make this fatal. Sometimes this comes from magic spaces our resource handlers simply don't know about */ @@ -333,12 +335,12 @@ static int vesafb_probe(struct platform_device *dev) par->base = si->lfb_base; par->size = size_total; - printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n", + dev_info(&dev->dev, "mode is %dx%dx%d, linelength=%d, pages=%d\n", vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_fix.line_length, si->pages); if (si->vesapm_seg) { - printk(KERN_INFO "vesafb: protected mode interface info at %04x:%04x\n", + dev_info(&dev->dev, "protected mode interface info at %04x:%04x\n", si->vesapm_seg, si->vesapm_off); } @@ -352,9 +354,10 @@ static int vesafb_probe(struct platform_device *dev) pmi_base = (unsigned short *)phys_to_virt(pmi_phys); pmi_start = (void*)((char*)pmi_base + pmi_base[1]); pmi_pal = (void*)((char*)pmi_base + pmi_base[2]); - printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal); + dev_info(&dev->dev, "pmi: set display start = %p, set palette = %p\n", + pmi_start, pmi_pal); if (pmi_base[3]) { - printk(KERN_INFO "vesafb: pmi: ports = "); + dev_info(&dev->dev, "pmi: ports = "); for (i = pmi_base[3]/2; pmi_base[i] != 0xffff; i++) printk("%x ", pmi_base[i]); printk("\n"); @@ -365,14 +368,14 @@ static int vesafb_probe(struct platform_device *dev) * Rules are: we have to set up a descriptor for the requested * memory area and pass it in the ES register to the BIOS function. */ - printk(KERN_INFO "vesafb: can't handle memory requests, pmi disabled\n"); + dev_info(&dev->dev, "can't handle memory requests, pmi disabled\n"); ypan = pmi_setpal = 0; } } } if (vesafb_defined.bits_per_pixel == 8 && !pmi_setpal && !vga_compat) { - printk(KERN_WARNING "vesafb: hardware palette is unchangeable,\n" + dev_warn(&dev->dev, "hardware palette is unchangeable,\n" " colors may be incorrect\n"); vesafb_fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; } @@ -380,10 +383,10 @@ static int vesafb_probe(struct platform_device *dev) vesafb_defined.xres_virtual = vesafb_defined.xres; vesafb_defined.yres_virtual = vesafb_fix.smem_len / vesafb_fix.line_length; if (ypan && vesafb_defined.yres_virtual > vesafb_defined.yres) { - printk(KERN_INFO "vesafb: scrolling: %s using protected mode interface, yres_virtual=%d\n", + dev_info(&dev->dev, "scrolling: %s using protected mode interface, yres_virtual=%d\n", (ypan > 1) ? "ywrap" : "ypan",vesafb_defined.yres_virtual); } else { - printk(KERN_INFO "vesafb: scrolling: redraw\n"); + dev_info(&dev->dev, "scrolling: redraw\n"); vesafb_defined.yres_virtual = vesafb_defined.yres; ypan = 0; } @@ -410,7 +413,7 @@ static int vesafb_probe(struct platform_device *dev) vesafb_defined.bits_per_pixel; } - printk(KERN_INFO "vesafb: %s: " + dev_info(&dev->dev, "%s: " "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", (vesafb_defined.bits_per_pixel > 8) ? "Truecolor" : (vga_compat || pmi_setpal) ? @@ -453,14 +456,14 @@ static int vesafb_probe(struct platform_device *dev) } if (!info->screen_base) { - printk(KERN_ERR - "vesafb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n", + dev_err(&dev->dev, + "abort, cannot ioremap video memory 0x%x @ 0x%lx\n", vesafb_fix.smem_len, vesafb_fix.smem_start); err = -EIO; goto err_release_region; } - printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, " + dev_info(&dev->dev, "framebuffer at 0x%lx, mapped to 0x%p, " "using %dk, total %dk\n", vesafb_fix.smem_start, info->screen_base, size_remap/1024, size_total/1024); diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index eedab14c7d51..22085d3668e8 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -21,7 +21,7 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/screen_info.h> +#include <linux/sysfb.h> #include <asm/io.h> #include <video/vga.h> @@ -1305,21 +1305,28 @@ static const struct fb_ops vga16fb_ops = { static int vga16fb_probe(struct platform_device *dev) { + struct sysfb_display_info *dpy; struct screen_info *si; struct fb_info *info; struct vga16fb_par *par; int i; int ret = 0; - si = dev_get_platdata(&dev->dev); - if (!si) + dpy = dev_get_platdata(&dev->dev); + if (!dpy) return -ENODEV; + si = &dpy->screen; ret = check_mode_supported(si); if (ret) return ret; - printk(KERN_DEBUG "vga16fb: initializing\n"); + dev_dbg(&dev->dev, "initializing\n"); + if (!request_mem_region(vga16fb_fix.smem_start, vga16fb_fix.smem_len, + "vga16b")) { + dev_err(&dev->dev, "cannot reserve video memory at 0x%lx\n", + vga16fb_fix.smem_start); + } info = framebuffer_alloc(sizeof(struct vga16fb_par), &dev->dev); if (!info) { @@ -1331,12 +1338,12 @@ static int vga16fb_probe(struct platform_device *dev) info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS_BASE, 0); if (!info->screen_base) { - printk(KERN_ERR "vga16fb: unable to map device\n"); + dev_err(&dev->dev, "unable to map device\n"); ret = -ENOMEM; goto err_ioremap; } - printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base); + dev_info(&dev->dev, "mapped to 0x%p\n", info->screen_base); par = info->par; par->isVGA = screen_info_video_type(si) == VIDEO_TYPE_VGAC; @@ -1364,13 +1371,13 @@ static int vga16fb_probe(struct platform_device *dev) i = (info->var.bits_per_pixel == 8) ? 256 : 16; ret = fb_alloc_cmap(&info->cmap, i, 0); if (ret) { - printk(KERN_ERR "vga16fb: unable to allocate colormap\n"); + dev_err(&dev->dev, "unable to allocate colormap\n"); ret = -ENOMEM; goto err_alloc_cmap; } if (vga16fb_check_var(&info->var, info)) { - printk(KERN_ERR "vga16fb: unable to validate variable\n"); + dev_err(&dev->dev, "unable to validate variable\n"); ret = -EINVAL; goto err_check_var; } @@ -1381,7 +1388,7 @@ static int vga16fb_probe(struct platform_device *dev) if (ret) goto err_check_var; if (register_framebuffer(info) < 0) { - printk(KERN_ERR "vga16fb: unable to register framebuffer\n"); + dev_err(&dev->dev, "unable to register framebuffer\n"); ret = -EINVAL; goto err_check_var; } @@ -1398,6 +1405,8 @@ static int vga16fb_probe(struct platform_device *dev) err_ioremap: framebuffer_release(info); err_fb_alloc: + release_mem_region(vga16fb_fix.smem_start, + vga16fb_fix.smem_len); return ret; } @@ -1407,6 +1416,8 @@ static void vga16fb_remove(struct platform_device *dev) if (info) unregister_framebuffer(info); + release_mem_region(vga16fb_fix.smem_start, + vga16fb_fix.smem_len); } static const struct platform_device_id vga16fb_driver_id_table[] = { diff --git a/drivers/video/fbdev/via/lcd.c b/drivers/video/fbdev/via/lcd.c index 8673fced8749..3fa2304fbda7 100644 --- a/drivers/video/fbdev/via/lcd.c +++ b/drivers/video/fbdev/via/lcd.c @@ -954,6 +954,9 @@ bool viafb_lcd_get_mobile_state(bool *mobile) u16 start_pattern; biosptr = ioremap(romaddr, 0x10000); + if (!biosptr) + return false; + start_pattern = readw(biosptr); /* Compare pattern */ diff --git a/drivers/video/fbdev/via/via-core.c b/drivers/video/fbdev/via/via-core.c index 908524a74a38..a8d4a3e2c65e 100644 --- a/drivers/video/fbdev/via/via-core.c +++ b/drivers/video/fbdev/via/via-core.c @@ -9,6 +9,7 @@ * Core code for the Via multifunction framebuffer device. */ #include <linux/aperture.h> +#include <linux/export.h> #include <linux/via-core.h> #include <linux/via_i2c.h> #include "via-gpio.h" diff --git a/drivers/video/fbdev/via/via-gpio.c b/drivers/video/fbdev/via/via-gpio.c index 9577c2cd52c7..45c0a4a6f85c 100644 --- a/drivers/video/fbdev/via/via-gpio.c +++ b/drivers/video/fbdev/via/via-gpio.c @@ -10,7 +10,6 @@ #include <linux/gpio/machine.h> #include <linux/platform_device.h> #include <linux/via-core.h> -#include <linux/export.h> #include "via-gpio.h" /* @@ -81,8 +80,7 @@ struct viafb_gpio_cfg { /* * GPIO access functions */ -static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, - int value) +static int via_gpio_set(struct gpio_chip *chip, unsigned int nr, int value) { struct viafb_gpio_cfg *cfg = gpiochip_get_data(chip); u8 reg; @@ -99,13 +97,14 @@ static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, reg &= ~(0x10 << gpio->vg_mask_shift); via_write_reg(VIASR, gpio->vg_port_index, reg); spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); + + return 0; } static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr, int value) { - via_gpio_set(chip, nr, value); - return 0; + return via_gpio_set(chip, nr, value); } /* diff --git a/drivers/video/fbdev/via/via_aux.c b/drivers/video/fbdev/via/via_aux.c index 902191dddbeb..47f88a2c4524 100644 --- a/drivers/video/fbdev/via/via_aux.c +++ b/drivers/video/fbdev/via/via_aux.c @@ -17,7 +17,7 @@ struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap) if (!adap) return NULL; - bus = kmalloc(sizeof(*bus), GFP_KERNEL); + bus = kmalloc_obj(*bus); if (!bus) return NULL; diff --git a/drivers/video/fbdev/via/via_aux.h b/drivers/video/fbdev/via/via_aux.h index 464723fd514c..6453d6a4fb4e 100644 --- a/drivers/video/fbdev/via/via_aux.h +++ b/drivers/video/fbdev/via/via_aux.h @@ -42,7 +42,7 @@ const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus); static inline bool via_aux_add(struct via_aux_drv *drv) { - struct via_aux_drv *data = kmalloc(sizeof(*data), GFP_KERNEL); + struct via_aux_drv *data = kmalloc_obj(*data); if (!data) return false; diff --git a/drivers/video/fbdev/via/via_aux_edid.c b/drivers/video/fbdev/via/via_aux_edid.c index 9791381a9865..c4abfb1bcf52 100644 --- a/drivers/video/fbdev/via/via_aux_edid.c +++ b/drivers/video/fbdev/via/via_aux_edid.c @@ -24,7 +24,7 @@ static void query_edid(struct via_aux_drv *drv) if (spec) { fb_destroy_modedb(spec->modedb); } else { - spec = kmalloc(sizeof(*spec), GFP_KERNEL); + spec = kmalloc_obj(*spec); if (!spec) return; } diff --git a/drivers/video/fbdev/via/via_i2c.c b/drivers/video/fbdev/via/via_i2c.c index cdbd7a9b8817..42c378425586 100644 --- a/drivers/video/fbdev/via/via_i2c.c +++ b/drivers/video/fbdev/via/via_i2c.c @@ -7,6 +7,7 @@ #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/spinlock.h> #include <linux/module.h> #include <linux/via-core.h> diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c index 6da5ae7d229a..80f95dac32c8 100644 --- a/drivers/video/fbdev/via/viafbdev.c +++ b/drivers/video/fbdev/via/viafbdev.c @@ -840,7 +840,7 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) struct { u8 data[CURSOR_SIZE]; u32 bak[CURSOR_SIZE / 4]; - } *cr_data = kzalloc(sizeof(*cr_data), GFP_ATOMIC); + } *cr_data = kzalloc_obj(*cr_data, GFP_ATOMIC); int size = ((cursor->image.width + 7) >> 3) * cursor->image.height; diff --git a/drivers/video/fbdev/vt8500lcdfb.c b/drivers/video/fbdev/vt8500lcdfb.c index b08a6fdc53fd..85c7a99a7d64 100644 --- a/drivers/video/fbdev/vt8500lcdfb.c +++ b/drivers/video/fbdev/vt8500lcdfb.c @@ -369,7 +369,7 @@ static int vt8500lcd_probe(struct platform_device *pdev) if (fbi->palette_cpu == NULL) { dev_err(&pdev->dev, "Failed to allocate palette buffer\n"); ret = -ENOMEM; - goto failed_free_io; + goto failed_free_mem_virt; } irq = platform_get_irq(pdev, 0); @@ -432,6 +432,9 @@ failed_free_irq: failed_free_palette: dma_free_coherent(&pdev->dev, fbi->palette_size, fbi->palette_cpu, fbi->palette_phys); +failed_free_mem_virt: + dma_free_coherent(&pdev->dev, fbi->fb.fix.smem_len, + fbi->fb.screen_buffer, fbi->fb.fix.smem_start); failed_free_io: iounmap(fbi->regbase); failed_free_res: diff --git a/drivers/video/fbdev/wmt_ge_rops.c b/drivers/video/fbdev/wmt_ge_rops.c index 69106299ab47..0cf78bcadfa6 100644 --- a/drivers/video/fbdev/wmt_ge_rops.c +++ b/drivers/video/fbdev/wmt_ge_rops.c @@ -7,12 +7,12 @@ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> */ +#include <linux/export.h> #include <linux/module.h> #include <linux/fb.h> #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 +41,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 +81,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); @@ -121,25 +148,15 @@ EXPORT_SYMBOL_GPL(wmt_ge_sync); static int wmt_ge_rops_probe(struct platform_device *pdev) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - return -ENODEV; - } - /* Only one ROP engine is presently supported. */ if (unlikely(regbase)) { WARN_ON(1); return -EBUSY; } - regbase = ioremap(res->start, resource_size(res)); - if (regbase == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - return -EBUSY; - } + regbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regbase)) + return PTR_ERR(regbase); writel(1, regbase + GE_ENABLE_OFF); printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n"); @@ -149,7 +166,7 @@ static int wmt_ge_rops_probe(struct platform_device *pdev) static void wmt_ge_rops_remove(struct platform_device *pdev) { - iounmap(regbase); + regbase = NULL; } static const struct of_device_id wmt_dt_ids[] = { diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c index c90f48ebb15e..4385976277ac 100644 --- a/drivers/video/fbdev/xen-fbfront.c +++ b/drivers/video/fbdev/xen-fbfront.c @@ -348,7 +348,7 @@ static int xenfb_probe(struct xenbus_device *dev, int val; int ret = 0; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info); if (info == NULL) { xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); return -ENOMEM; @@ -390,7 +390,7 @@ static int xenfb_probe(struct xenbus_device *dev, info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; - info->gfns = vmalloc(array_size(sizeof(unsigned long), info->nr_pages)); + info->gfns = vmalloc_array(info->nr_pages, sizeof(unsigned long)); if (!info->gfns) goto error_nomem; diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index ce6bb753522d..cda15b95891e 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -20,54 +20,60 @@ config FB_LOGO_EXTRA config LOGO_LINUX_MONO bool "Standard black and white Linux logo" - default y -config LOGO_LINUX_VGA16 - bool "Standard 16-color Linux logo" - default y +config LOGO_LINUX_MONO_FILE + string "Monochrome logo .pbm file" + depends on LOGO_LINUX_MONO + default "drivers/video/logo/logo_superh_mono.pbm" if SUPERH + default "drivers/video/logo/logo_linux_mono.pbm" + help + Takes a path to a monochromatic logo in the portable pixmap file + format (.pbm). This defaults to the Tux penguin. -config LOGO_LINUX_CLUT224 - bool "Standard 224-color Linux logo" - default y + For example, the below ImageMagick command can be used to reduce + an image to black and white and convert it into a pbm file: -config LOGO_DEC_CLUT224 - bool "224-color Digital Equipment Corporation Linux logo" - depends on MACH_DECSTATION || ALPHA - default y + magick source_image -compress none destination.pbm -config LOGO_MAC_CLUT224 - bool "224-color Macintosh Linux logo" - depends on MAC - default y +config LOGO_LINUX_VGA16 + bool "Standard 16-color Linux logo" -config LOGO_PARISC_CLUT224 - bool "224-color PA-RISC Linux logo" - depends on PARISC - default y +config LOGO_LINUX_VGA16_FILE + string "16-color logo .ppm file" + depends on LOGO_LINUX_VGA16 + default "drivers/video/logo/logo_superh_vga16.ppm" if SUPERH + default "drivers/video/logo/logo_linux_vga16.ppm" + help + Takes a path to a logo in the portable pixmap file format (.ppm), + using the 16 colors from the drivers/video/logo/clut_vga16.ppm + palette. This defaults to the Tux penguin. -config LOGO_SGI_CLUT224 - bool "224-color SGI Linux logo" - depends on SGI_IP22 || SGI_IP27 || SGI_IP32 - default y + For example, the below ImageMagick command can be used to reduce an + image to the VGA 16 colors palette and convert into a ppm file: -config LOGO_SUN_CLUT224 - bool "224-color Sun Linux logo" - depends on SPARC - default y + magick source_image -compress none \ + -remap drivers/video/logo/clut_vga16.ppm destination.ppm -config LOGO_SUPERH_MONO - bool "Black and white SuperH Linux logo" - depends on SUPERH +config LOGO_LINUX_CLUT224 + bool "Standard 224-color Linux logo" default y -config LOGO_SUPERH_VGA16 - bool "16-color SuperH Linux logo" - depends on SUPERH - default y +config LOGO_LINUX_CLUT224_FILE + string "224-color logo .ppm file" + depends on LOGO_LINUX_CLUT224 + default "drivers/video/logo/logo_dec_clut224.ppm" if MACH_DECSTATION || ALPHA + default "drivers/video/logo/logo_parisc_clut224.ppm" if PARISC + default "drivers/video/logo/logo_sgi_clut224.ppm" if SGI_IP22 || SGI_IP27 || SGI_IP32 + default "drivers/video/logo/logo_sun_clut224.ppm" if SPARC + default "drivers/video/logo/logo_superh_clut224.ppm" if SUPERH + default "drivers/video/logo/logo_linux_clut224.ppm" + help + Takes a path to a 224-color logo in the portable pixmap file + format (.ppm). This defaults to the Tux penguin. -config LOGO_SUPERH_CLUT224 - bool "224-color SuperH Linux logo" - depends on SUPERH - default y + For example, the below ImageMagick command can be used to reduce + an image palette to 224 colors and convert it into a ppm file: + + magick source_image -compress none -colors 224 destination.ppm endif # LOGO diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index 895c60b8402e..937b37d3b6c7 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -5,14 +5,6 @@ obj-$(CONFIG_LOGO) += logo.o obj-$(CONFIG_LOGO_LINUX_MONO) += logo_linux_mono.o obj-$(CONFIG_LOGO_LINUX_VGA16) += logo_linux_vga16.o obj-$(CONFIG_LOGO_LINUX_CLUT224) += logo_linux_clut224.o -obj-$(CONFIG_LOGO_DEC_CLUT224) += logo_dec_clut224.o -obj-$(CONFIG_LOGO_MAC_CLUT224) += logo_mac_clut224.o -obj-$(CONFIG_LOGO_PARISC_CLUT224) += logo_parisc_clut224.o -obj-$(CONFIG_LOGO_SGI_CLUT224) += logo_sgi_clut224.o -obj-$(CONFIG_LOGO_SUN_CLUT224) += logo_sun_clut224.o -obj-$(CONFIG_LOGO_SUPERH_MONO) += logo_superh_mono.o -obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o -obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o @@ -20,18 +12,21 @@ obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o hostprogs := pnmtologo -# Create commands like "pnmtologo -t mono -n logo_mac_mono -o ..." +# Create commands like "pnmtologo -t mono -n logo_linux_mono -o ..." quiet_cmd_logo = LOGO $@ - cmd_logo = $(obj)/pnmtologo -t $(lastword $(subst _, ,$*)) -n $* -o $@ $< + cmd_logo = $(obj)/pnmtologo -t $2 -n $(basename $(notdir $@)) -o $@ $< -$(obj)/%.c: $(src)/%.pbm $(obj)/pnmtologo FORCE - $(call if_changed,logo) +$(obj)/logo_linux_mono.c: $(CONFIG_LOGO_LINUX_MONO_FILE) $(obj)/pnmtologo FORCE + $(call if_changed,logo,mono) -$(obj)/%.c: $(src)/%.ppm $(obj)/pnmtologo FORCE - $(call if_changed,logo) +$(obj)/logo_linux_vga16.c: $(CONFIG_LOGO_LINUX_VGA16_FILE) $(obj)/pnmtologo FORCE + $(call if_changed,logo,vga16) -$(obj)/%.c: $(src)/%.pgm $(obj)/pnmtologo FORCE - $(call if_changed,logo) +$(obj)/logo_linux_clut224.c: $(CONFIG_LOGO_LINUX_CLUT224_FILE) $(obj)/pnmtologo FORCE + $(call if_changed,logo,clut224) + +$(obj)/%_clut224.c: $(src)/%_clut224.ppm $(obj)/pnmtologo FORCE + $(call if_changed,logo,clut224) # generated C files -targets += *_mono.c *_vga16.c *_clut224.c *_gray256.c +targets += *_mono.c *_vga16.c *_clut224.c diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 141f15a9a459..91535f8848da 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -48,59 +48,21 @@ const struct linux_logo * __ref fb_find_logo(int depth) if (nologo || logos_freed) return NULL; - if (depth >= 1) { #ifdef CONFIG_LOGO_LINUX_MONO - /* Generic Linux logo */ + if (depth >= 1) logo = &logo_linux_mono; #endif -#ifdef CONFIG_LOGO_SUPERH_MONO - /* SuperH Linux logo */ - logo = &logo_superh_mono; -#endif - } - if (depth >= 4) { #ifdef CONFIG_LOGO_LINUX_VGA16 - /* Generic Linux logo */ + if (depth >= 4) logo = &logo_linux_vga16; #endif -#ifdef CONFIG_LOGO_SUPERH_VGA16 - /* SuperH Linux logo */ - logo = &logo_superh_vga16; -#endif - } - if (depth >= 8) { #ifdef CONFIG_LOGO_LINUX_CLUT224 - /* Generic Linux logo */ + if (depth >= 8) logo = &logo_linux_clut224; #endif -#ifdef CONFIG_LOGO_DEC_CLUT224 - /* DEC Linux logo on MIPS/MIPS64 or ALPHA */ - logo = &logo_dec_clut224; -#endif -#ifdef CONFIG_LOGO_MAC_CLUT224 - /* Macintosh Linux logo on m68k */ - if (MACH_IS_MAC) - logo = &logo_mac_clut224; -#endif -#ifdef CONFIG_LOGO_PARISC_CLUT224 - /* PA-RISC Linux logo */ - logo = &logo_parisc_clut224; -#endif -#ifdef CONFIG_LOGO_SGI_CLUT224 - /* SGI Linux logo on MIPS/MIPS64 */ - logo = &logo_sgi_clut224; -#endif -#ifdef CONFIG_LOGO_SUN_CLUT224 - /* Sun Linux logo */ - logo = &logo_sun_clut224; -#endif -#ifdef CONFIG_LOGO_SUPERH_CLUT224 - /* SuperH Linux logo */ - logo = &logo_superh_clut224; -#endif - } + return logo; } EXPORT_SYMBOL_GPL(fb_find_logo); diff --git a/drivers/video/logo/logo_mac_clut224.ppm b/drivers/video/logo/logo_mac_clut224.ppm deleted file mode 100644 index 4dad34baea89..000000000000 --- a/drivers/video/logo/logo_mac_clut224.ppm +++ /dev/null @@ -1,1604 +0,0 @@ -P3 -# 224-color Macintosh Linux logo -80 80 -255 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 6 6 6 10 10 10 10 10 10 - 10 10 10 6 6 6 6 6 6 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 10 10 10 14 14 14 - 22 22 22 26 26 26 30 30 30 34 34 34 - 30 30 30 30 30 30 26 26 26 18 18 18 - 14 14 14 10 10 10 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 26 26 26 42 42 42 - 54 54 54 66 66 66 78 78 78 78 78 78 - 78 78 78 74 74 74 66 66 66 54 54 54 - 42 42 42 26 26 26 18 18 18 10 10 10 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 22 22 22 42 42 42 66 66 66 86 86 86 - 66 66 66 38 38 38 38 38 38 22 22 22 - 26 26 26 34 34 34 54 54 54 66 66 66 - 86 86 86 70 70 70 46 46 46 26 26 26 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 10 10 10 26 26 26 - 50 50 50 82 82 82 58 58 58 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 6 6 6 54 54 54 86 86 86 66 66 66 - 38 38 38 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 22 22 22 50 50 50 - 78 78 78 34 34 34 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 6 6 6 70 70 70 - 78 78 78 46 46 46 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 250 250 250 -250 250 250 250 250 250 250 250 250 242 242 242 -250 250 250 250 250 250 246 246 246 250 250 250 -246 246 246 242 242 242 246 246 246 231 231 231 - 46 46 46 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 14 14 14 - 46 46 46 34 34 34 6 6 6 2 2 6 - 82 82 82 242 242 242 242 242 242 246 246 246 -242 242 242 250 250 250 242 242 242 246 246 246 -242 242 242 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 250 250 250 250 250 250 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 242 242 242 -250 250 250 250 250 250 250 250 250 250 250 250 -242 242 242 246 246 246 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 116 116 116 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 26 26 26 - 86 86 86 101 101 101 46 46 46 10 10 10 - 2 2 6 123 123 123 242 242 242 250 250 250 -246 246 246 250 250 250 242 242 242 250 250 250 -246 246 246 250 250 250 242 242 242 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 123 123 123 -234 234 234 231 231 231 234 234 234 234 234 234 -234 234 234 221 221 221 234 234 234 231 231 231 -234 234 234 234 234 234 214 214 214 10 10 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 30 30 30 - 94 94 94 94 94 94 58 58 58 26 26 26 - 2 2 6 10 10 10 190 190 190 242 242 242 -242 242 242 250 250 250 250 250 250 242 242 242 -246 246 246 250 250 250 250 250 250 242 242 242 -250 250 250 242 242 242 242 242 242 231 231 231 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 90 90 90 -234 234 234 226 226 226 226 226 226 218 218 218 -226 226 226 214 214 214 231 231 231 221 221 221 -231 231 231 221 221 221 116 116 116 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 26 26 26 - 54 54 54 38 38 38 18 18 18 10 10 10 - 2 2 6 2 2 6 58 58 58 242 242 242 -242 242 242 242 242 242 242 242 242 242 242 242 -242 242 242 242 242 242 242 242 242 242 242 242 -242 242 242 242 242 242 250 250 250 226 226 226 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -234 234 234 231 231 231 242 242 242 242 242 242 -234 234 234 234 234 234 238 238 238 234 234 234 -238 238 238 238 238 238 50 50 50 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 10 10 10 - 10 10 10 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 182 182 182 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 242 242 242 250 250 250 206 206 206 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -250 250 250 226 226 226 234 234 234 10 10 10 - 78 78 78 66 66 66 101 98 89 90 90 90 -110 110 110 106 106 106 10 10 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 101 98 89 -210 210 210 238 238 238 226 226 226 238 238 238 -210 210 210 242 242 242 226 226 226 242 242 242 -242 242 242 234 234 234 250 250 250 198 198 198 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -234 234 234 234 234 234 231 231 231 2 2 6 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 82 82 82 2 2 6 2 2 6 - 2 2 6 6 6 6 10 10 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 6 6 6 - 14 14 14 10 10 10 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 18 18 18 - 82 82 82 34 34 34 10 10 10 0 0 0 - 6 6 6 0 0 0 0 0 0 0 0 0 -144 144 144 250 250 250 242 242 242 202 202 202 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -226 226 226 231 231 231 234 234 234 90 90 90 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 2 2 6 - 6 6 6 6 6 6 22 22 22 34 34 34 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 18 18 18 34 34 34 - 10 10 10 50 50 50 22 22 22 2 2 6 - 2 2 6 2 2 6 2 2 6 10 10 10 - 86 86 86 42 42 42 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 -158 158 158 242 242 242 234 234 234 187 187 187 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -231 231 231 226 226 226 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 2 2 6 - 38 38 38 116 116 116 94 94 94 22 22 22 - 22 22 22 2 2 6 2 2 6 2 2 6 - 14 14 14 86 86 86 138 138 138 162 162 162 -154 154 154 38 38 38 26 26 26 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 86 86 86 46 46 46 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 -187 187 187 234 234 234 250 250 250 190 190 190 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -226 226 226 218 218 218 234 234 234 218 218 218 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 14 14 14 -134 134 134 198 198 198 195 195 195 116 116 116 - 10 10 10 2 2 6 2 2 6 6 6 6 -101 98 89 187 187 187 210 210 210 218 218 218 -214 214 214 134 134 134 14 14 14 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 86 86 86 50 50 50 18 18 18 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -214 214 214 226 226 226 242 242 242 187 187 187 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 74 74 74 -226 226 226 214 214 214 226 226 226 190 190 190 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 54 54 54 -218 218 218 195 195 195 226 226 226 246 246 246 - 58 58 58 2 2 6 2 2 6 30 30 30 -210 210 210 253 253 253 174 174 174 123 123 123 -221 221 221 234 234 234 74 74 74 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -195 195 195 226 226 226 234 234 234 206 206 206 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -226 226 226 218 218 218 234 234 234 226 226 226 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 82 82 82 2 2 6 106 106 106 -170 170 170 26 26 26 86 86 86 226 226 226 -123 123 123 10 10 10 14 14 14 46 46 46 -231 231 231 190 190 190 6 6 6 70 70 70 - 90 90 90 238 238 238 158 158 158 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -182 182 182 234 234 234 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 101 98 89 -214 214 214 214 214 214 226 226 226 242 242 242 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 86 86 86 6 6 6 116 116 116 -106 106 106 6 6 6 70 70 70 149 149 149 -128 128 128 18 18 18 38 38 38 54 54 54 -221 221 221 106 106 106 2 2 6 14 14 14 - 46 46 46 190 190 190 198 198 198 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 74 74 74 62 62 62 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -190 190 190 226 226 226 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -210 210 210 214 214 214 210 210 210 250 250 250 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 94 94 94 14 14 14 101 101 101 -128 128 128 2 2 6 18 18 18 116 116 116 -118 98 46 121 92 8 121 92 8 98 78 10 -162 162 162 106 106 106 2 2 6 2 2 6 - 2 2 6 195 195 195 195 195 195 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 74 74 74 62 62 62 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -214 214 214 226 226 226 231 231 231 149 149 149 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 1 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -226 226 226 218 218 218 210 210 210 231 231 231 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 90 90 90 14 14 14 58 58 58 -210 210 210 26 26 26 54 38 6 154 114 10 -226 170 11 236 186 11 225 175 15 184 144 12 -215 174 15 175 146 61 37 26 9 2 2 6 - 70 70 70 246 246 246 138 138 138 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 66 66 66 26 26 26 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -178 178 178 218 218 218 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -214 214 214 198 198 198 210 210 210 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 86 86 86 14 14 14 10 10 10 -195 195 195 188 164 115 192 133 9 225 175 15 -239 182 13 234 190 10 232 195 16 232 200 30 -245 207 45 241 208 19 232 195 16 184 144 12 -218 194 134 211 206 186 42 42 42 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 50 50 50 74 74 74 30 30 30 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -187 187 187 231 231 231 226 226 226 182 182 182 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -218 218 218 210 210 210 210 210 210 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 86 86 86 14 14 14 2 2 6 -121 87 25 192 133 9 219 162 10 239 182 13 -236 186 11 232 195 16 241 208 19 244 214 54 -246 218 60 246 218 38 246 215 20 241 208 19 -241 208 19 226 184 13 121 87 25 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 50 50 50 82 82 82 34 34 34 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 -162 162 162 226 226 226 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -206 206 206 206 206 206 202 202 202 250 250 250 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 82 82 82 30 30 30 61 42 6 -180 123 7 206 145 10 230 174 11 239 182 13 -234 190 10 238 202 15 241 208 19 246 218 74 -246 218 38 246 215 20 246 215 20 246 215 20 -226 184 13 215 174 15 184 144 12 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 26 26 26 94 94 94 42 42 42 14 14 14 - 0 0 0 0 0 0 0 0 0 0 0 0 -166 166 166 226 226 226 214 214 214 170 170 170 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -226 226 226 218 218 218 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 50 50 50 104 69 6 -192 133 9 216 158 10 236 178 12 236 186 11 -232 195 16 241 208 19 244 214 54 245 215 43 -246 215 20 246 215 20 241 208 19 198 155 10 -200 144 11 216 158 10 156 118 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 6 6 6 90 90 90 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 -178 178 178 226 226 226 226 226 226 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -214 214 214 198 198 198 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 46 46 46 22 22 22 -137 92 6 210 162 10 239 182 13 238 190 10 -238 202 15 241 208 19 246 215 20 246 215 20 -241 208 19 203 166 17 185 133 11 210 150 10 -216 158 10 210 150 10 102 78 10 2 2 6 - 6 6 6 54 54 54 14 14 14 2 2 6 - 2 2 6 62 62 62 74 74 74 30 30 30 - 10 10 10 0 0 0 0 0 0 0 0 0 -190 190 190 214 214 214 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -210 210 210 202 202 202 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 78 78 78 50 50 50 6 6 6 - 94 70 30 139 102 15 190 146 13 226 184 13 -232 200 30 232 195 16 215 174 15 190 146 13 -168 122 10 192 133 9 210 150 10 213 154 11 -202 150 34 182 157 106 101 98 89 2 2 6 - 2 2 6 78 78 78 116 116 116 58 58 58 - 2 2 6 22 22 22 90 90 90 46 46 46 - 18 18 18 6 6 6 0 0 0 0 0 0 -195 195 195 214 214 214 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -198 198 198 190 190 190 187 187 187 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 86 86 86 50 50 50 6 6 6 -128 128 128 174 154 114 156 107 11 168 122 10 -198 155 10 184 144 12 197 138 11 200 144 11 -206 145 10 206 145 10 197 138 11 188 164 115 -195 195 195 198 198 198 174 174 174 14 14 14 - 2 2 6 22 22 22 116 116 116 116 116 116 - 22 22 22 2 2 6 74 74 74 70 70 70 - 30 30 30 10 10 10 0 0 0 0 0 0 -178 178 178 226 226 226 226 226 226 141 141 141 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -195 195 195 195 195 195 195 195 195 250 250 250 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 101 101 101 26 26 26 10 10 10 -138 138 138 190 190 190 174 154 114 156 107 11 -197 138 11 200 144 11 197 138 11 192 133 9 -180 123 7 190 142 34 190 178 144 187 187 187 -202 202 202 221 221 221 214 214 214 66 66 66 - 2 2 6 2 2 6 50 50 50 62 62 62 - 6 6 6 2 2 6 10 10 10 90 90 90 - 50 50 50 18 18 18 6 6 6 0 0 0 -190 190 190 226 226 226 250 250 250 202 202 202 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -187 187 187 190 190 190 187 187 187 250 250 250 - 0 0 0 0 0 0 10 10 10 34 34 34 - 74 74 74 74 74 74 2 2 6 6 6 6 -144 144 144 198 198 198 190 190 190 178 166 146 -154 121 60 156 107 11 156 107 11 168 124 44 -174 154 114 187 187 187 190 190 190 210 210 210 -246 246 246 253 253 253 253 253 253 182 182 182 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 62 62 62 - 74 74 74 34 34 34 14 14 14 0 0 0 -174 174 174 206 206 206 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -190 190 190 187 187 187 190 190 190 234 234 234 - 0 0 0 10 10 10 22 22 22 54 54 54 - 94 94 94 18 18 18 2 2 6 46 46 46 -234 234 234 221 221 221 190 190 190 190 190 190 -190 190 190 187 187 187 187 187 187 190 190 190 -190 190 190 195 195 195 214 214 214 242 242 242 -253 253 253 253 253 253 253 253 253 253 253 253 - 82 82 82 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 14 14 14 - 86 86 86 54 54 54 22 22 22 6 6 6 -195 195 195 202 202 202 234 234 234 138 138 138 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -182 182 182 187 187 187 178 178 178 242 242 242 - 6 6 6 18 18 18 46 46 46 90 90 90 - 46 46 46 18 18 18 6 6 6 182 182 182 -253 253 253 246 246 246 206 206 206 190 190 190 -190 190 190 190 190 190 190 190 190 190 190 190 -206 206 206 231 231 231 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -202 202 202 14 14 14 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 42 42 42 86 86 86 42 42 42 18 18 18 -190 190 190 202 202 202 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -195 195 195 182 182 182 187 187 187 250 250 250 - 14 14 14 38 38 38 74 74 74 66 66 66 - 2 2 6 6 6 6 90 90 90 250 250 250 -253 253 253 253 253 253 238 238 238 198 198 198 -190 190 190 190 190 190 195 195 195 221 221 221 -246 246 246 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 82 82 82 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 78 78 78 70 70 70 34 34 34 -202 202 202 182 182 182 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 26 26 26 -195 195 195 182 182 182 178 178 178 242 242 242 - 34 34 34 66 66 66 78 78 78 6 6 6 - 2 2 6 18 18 18 218 218 218 253 253 253 -253 253 253 253 253 253 253 253 253 246 246 246 -226 226 226 231 231 231 246 246 246 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 178 178 178 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 18 18 18 90 90 90 62 62 62 -218 218 218 198 198 198 250 250 250 141 141 141 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -182 182 182 178 178 178 174 174 174 250 250 250 - 58 58 58 90 90 90 18 18 18 2 2 6 - 2 2 6 110 110 110 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 231 231 231 18 18 18 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 18 18 18 94 94 94 -206 206 206 198 198 198 242 242 242 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -166 166 166 170 170 170 187 187 187 242 242 242 - 90 90 90 26 26 26 2 2 6 2 2 6 - 14 14 14 195 195 195 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 242 242 242 54 54 54 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 38 38 38 -187 187 187 214 214 214 231 231 231 134 134 134 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -187 187 187 182 182 182 166 166 166 234 234 234 - 34 34 34 2 2 6 2 2 6 2 2 6 - 42 42 42 195 195 195 246 246 246 253 253 253 -253 253 253 253 253 253 253 253 253 250 250 250 -242 242 242 242 242 242 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 246 246 246 238 238 238 -226 226 226 231 231 231 101 101 101 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 -206 206 206 174 174 174 250 250 250 128 128 128 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 34 34 34 -178 178 178 144 144 144 170 170 170 226 226 226 - 2 2 6 2 2 6 2 2 6 6 6 6 - 70 70 70 170 170 170 206 206 206 234 234 234 -246 246 246 250 250 250 250 250 250 238 238 238 -226 226 226 231 231 231 238 238 238 250 250 250 -250 250 250 250 250 250 246 246 246 231 231 231 -214 214 214 206 206 206 202 202 202 202 202 202 -198 198 198 202 202 202 182 182 182 18 18 18 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 -202 202 202 166 166 166 214 214 214 128 128 128 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -178 178 178 158 158 158 134 134 134 242 242 242 - 2 2 6 2 2 6 2 2 6 10 10 10 - 94 94 94 182 182 182 218 218 218 242 242 242 -250 250 250 253 253 253 253 253 253 250 250 250 -234 234 234 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 246 246 246 -238 238 238 226 226 226 210 210 210 202 202 202 -195 195 195 195 195 195 210 210 210 158 158 158 - 6 6 6 14 14 14 50 50 50 14 14 14 - 2 2 6 2 2 6 2 2 6 2 2 6 -198 198 198 187 187 187 246 246 246 116 116 116 - 18 18 18 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -195 195 195 154 154 154 154 154 154 250 250 250 -250 250 250 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 250 250 250 246 246 246 -234 234 234 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 242 242 242 242 242 242 -250 250 250 242 242 242 242 242 242 250 250 250 -182 182 182 190 190 190 206 206 206 141 141 141 - 26 26 26 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -170 170 170 166 166 166 128 128 128 250 250 250 -242 242 242 250 250 250 250 250 250 242 242 242 -250 250 250 242 242 242 242 242 242 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -242 242 242 250 250 250 242 242 242 250 250 250 -250 250 250 242 242 242 250 250 250 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 242 242 242 250 250 250 234 234 234 -250 250 250 242 242 242 242 242 242 250 250 250 -195 195 195 195 195 195 206 206 206 128 128 128 - 42 42 42 14 14 14 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -178 178 178 174 174 174 158 158 158 170 170 170 -162 162 162 170 170 170 170 170 170 162 162 162 -166 166 166 170 170 170 154 154 154 154 154 154 -178 178 178 162 162 162 166 166 166 166 166 166 -166 166 166 158 158 158 178 178 178 162 162 162 -170 170 170 174 174 174 178 178 178 178 178 178 -170 170 170 178 178 178 170 170 170 166 166 166 -170 170 170 182 182 182 187 187 187 178 178 178 -195 195 195 195 195 195 195 195 195 195 195 195 -187 187 187 195 195 195 178 178 178 195 195 195 -206 206 206 195 195 195 210 210 210 116 116 116 - 58 58 58 22 22 22 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 50 50 50 -162 162 162 162 162 162 170 170 170 158 158 158 -162 162 162 158 158 158 162 162 162 166 166 166 -158 158 158 174 174 174 162 162 162 158 158 158 -170 170 170 166 166 166 166 166 166 174 174 174 -166 166 166 174 174 174 174 174 174 174 174 174 -174 174 174 182 182 182 166 166 166 187 187 187 -182 182 182 187 187 187 174 174 174 166 166 166 -166 166 166 187 187 187 182 182 182 158 158 158 -174 174 174 174 174 174 174 174 174 182 182 182 -182 182 182 182 182 182 178 178 178 195 195 195 -178 178 178 182 182 182 174 174 174 30 30 30 - 78 78 78 30 30 30 10 10 10 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 10 10 10 34 34 34 -154 154 154 158 158 158 154 154 154 158 158 158 -154 154 154 166 166 166 162 162 162 178 178 178 -178 178 178 166 166 166 170 170 170 158 158 158 -170 170 170 178 178 178 178 178 178 187 187 187 -195 195 195 178 178 178 178 178 178 178 178 178 -162 162 162 187 187 187 166 166 166 178 178 178 -174 174 174 178 178 178 170 170 170 170 170 170 -174 174 174 170 170 170 187 187 187 178 178 178 -178 178 178 202 202 202 170 170 170 187 187 187 -178 178 178 182 182 182 174 174 174 190 190 190 -182 182 182 166 166 166 149 149 149 6 6 6 - 86 86 86 46 46 46 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 50 50 50 -166 166 166 162 162 162 149 149 149 162 162 162 -158 158 158 170 170 170 158 158 158 158 158 158 -166 166 166 170 170 170 149 149 149 170 170 170 -158 158 158 174 174 174 166 166 166 166 166 166 -166 166 166 166 166 166 182 182 182 158 158 158 -158 158 158 174 174 174 170 170 170 158 158 158 -178 178 178 166 166 166 158 158 158 174 174 174 -170 170 170 166 166 166 174 174 174 166 166 166 -174 174 174 182 182 182 174 174 174 182 182 182 -174 174 174 178 178 178 187 187 187 206 206 206 -187 187 187 178 178 178 128 128 128 2 2 6 - 74 74 74 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 10 10 10 26 26 26 42 42 42 -158 158 158 144 144 144 149 149 149 162 162 162 -149 149 149 170 170 170 170 170 170 170 170 170 -174 174 174 170 170 170 158 158 158 162 162 162 -170 170 170 162 162 162 170 170 170 170 170 170 -162 162 162 162 162 162 170 170 170 170 170 170 -170 170 170 166 166 166 154 154 154 166 166 166 -154 154 154 162 162 162 170 170 170 149 149 149 -170 170 170 144 144 144 187 187 187 170 170 170 -170 170 170 195 195 195 187 187 187 202 202 202 -198 198 198 182 182 182 202 202 202 210 210 210 -187 187 187 178 178 178 106 106 106 2 2 6 - 42 42 42 74 74 74 30 30 30 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 42 42 42 42 42 42 -158 158 158 141 141 141 162 162 162 149 149 149 -154 154 154 158 158 158 166 166 166 174 174 174 -162 162 162 158 158 158 162 162 162 158 158 158 -158 158 158 158 158 158 166 166 166 166 166 166 -158 158 158 158 158 158 158 158 158 166 166 166 -166 166 166 170 170 170 182 182 182 187 187 187 -166 166 166 174 174 174 166 166 166 154 154 154 -174 174 174 174 174 174 166 166 166 190 190 190 - 34 34 34 2 2 6 18 18 18 2 2 6 - 34 34 34 2 2 6 18 18 18 78 78 78 -182 182 182 178 178 178 78 78 78 2 2 6 - 10 10 10 86 86 86 38 38 38 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 10 10 10 26 26 26 66 66 66 30 30 30 -138 138 138 144 144 144 154 154 154 149 149 149 -154 154 154 154 154 154 154 154 154 166 166 166 -162 162 162 158 158 158 162 162 162 154 154 154 -170 170 170 154 154 154 178 178 178 162 162 162 -162 162 162 170 170 170 162 162 162 154 154 154 - 2 2 6 2 2 6 34 34 34 42 42 42 - 42 42 42 34 34 34 22 18 6 34 34 34 - 42 42 42 42 42 42 66 66 66 34 34 34 -128 128 128 10 10 10 10 10 10 18 18 18 - 18 18 18 10 10 10 26 26 26 174 174 174 -187 187 187 138 138 138 34 34 34 2 2 6 - 6 6 6 86 86 86 46 46 46 14 14 14 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 18 18 18 46 46 46 86 86 86 6 6 6 -110 110 110 162 162 162 149 149 149 144 144 144 -149 149 149 166 166 166 149 149 149 162 162 162 -149 149 149 162 162 162 149 149 149 158 158 158 -166 166 166 158 158 158 158 158 158 166 166 166 -166 166 166 149 149 149 158 158 158 166 166 166 -128 128 128 18 18 18 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 22 18 6 26 26 26 - 18 18 18 6 6 6 18 18 18 166 166 166 -174 174 174 110 110 110 18 18 18 2 2 6 - 2 2 6 82 82 82 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 26 26 26 66 66 66 62 62 62 2 2 6 - 46 46 46 141 141 141 166 166 166 144 144 144 -154 154 154 170 170 170 158 158 158 162 162 162 -149 149 149 162 162 162 154 154 154 154 154 154 -162 162 162 144 144 144 162 162 162 154 154 154 -170 170 170 144 144 144 154 154 154 170 170 170 -116 116 116 144 144 144 110 110 110 116 116 116 -110 110 110 144 144 144 116 116 116 128 128 128 -134 134 134 116 116 116 134 134 134 149 149 149 -158 158 158 231 231 231 234 234 234 214 214 214 -202 202 202 195 195 195 166 166 166 144 144 144 -144 144 144 34 34 34 2 2 6 2 2 6 - 2 2 6 66 66 66 58 58 58 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 78 78 78 6 6 6 2 2 6 - 14 14 14 123 123 123 138 138 138 90 90 90 -110 110 110 128 128 128 154 154 154 149 149 149 -144 144 144 149 149 149 158 158 158 149 149 149 -166 166 166 158 158 158 158 158 158 166 166 166 -158 158 158 158 158 158 158 158 158 158 158 158 -144 144 144 170 170 170 162 162 162 170 170 170 -187 187 187 174 174 174 170 170 170 170 170 170 -162 162 162 170 170 170 170 170 170 178 178 178 -187 187 187 190 190 190 170 170 170 149 149 149 -149 149 149 138 138 138 170 170 170 116 116 116 - 18 18 18 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 62 62 62 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 74 74 74 2 2 6 2 2 6 - 14 14 14 94 94 94 134 134 134 74 74 74 - 50 50 50 158 158 158 154 154 154 166 166 166 -162 162 162 170 170 170 162 162 162 178 178 178 -170 170 170 154 154 154 162 162 162 154 154 154 -154 154 154 154 154 154 170 170 170 141 141 141 -149 149 149 166 166 166 166 166 166 166 166 166 -178 178 178 174 174 174 158 158 158 174 174 174 -174 174 174 174 174 174 174 174 174 158 158 158 -166 166 166 166 166 166 170 170 170 170 170 170 -170 170 170 162 162 162 82 82 82 10 10 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 62 62 62 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 54 54 54 62 62 62 2 2 6 2 2 6 - 2 2 6 34 34 34 123 123 123 18 18 18 - 22 18 6 128 128 128 149 149 149 154 154 154 -158 158 158 158 158 158 149 149 149 166 166 166 -166 166 166 158 158 158 158 158 158 182 182 182 -158 158 158 149 149 149 149 149 149 178 178 178 -162 162 162 170 170 170 170 170 170 170 170 170 -174 174 174 178 178 178 170 170 170 178 178 178 -170 170 170 178 178 178 178 178 178 162 162 162 -174 174 174 170 170 170 166 166 166 166 166 166 -141 141 141 50 50 50 30 30 30 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 58 58 58 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 58 58 58 62 62 62 2 2 6 2 2 6 - 2 2 6 2 2 6 38 38 38 144 144 144 -178 178 178 162 162 162 134 134 134 154 154 154 -154 154 154 154 154 154 154 154 154 170 170 170 -154 154 154 154 154 154 162 162 162 170 170 170 -162 162 162 154 154 154 158 158 158 174 174 174 -149 149 149 166 166 166 174 174 174 178 178 178 -174 174 174 174 174 174 166 166 166 174 174 174 -166 166 166 166 166 166 166 166 166 166 166 166 -170 170 170 170 170 170 166 166 166 138 138 138 - 42 42 42 34 34 34 18 14 6 22 22 22 - 26 26 26 18 18 18 6 6 6 2 2 6 - 2 2 6 82 82 82 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 26 26 26 - 62 62 62 106 106 106 74 54 14 185 133 11 -210 162 10 121 92 8 6 6 6 78 78 78 -154 154 154 149 149 149 141 141 141 149 149 149 -149 149 149 149 149 149 158 158 158 141 141 141 -149 149 149 141 141 141 158 158 158 149 149 149 -149 149 149 149 149 149 162 162 162 170 170 170 -154 154 154 170 170 170 162 162 162 166 166 166 -170 170 170 170 170 170 170 170 170 162 162 162 -162 162 162 170 170 170 170 170 170 170 170 170 -170 170 170 162 162 162 162 162 162 38 38 38 - 14 14 14 2 2 6 2 2 6 2 2 6 - 6 6 6 18 18 18 66 66 66 38 38 38 - 6 6 6 94 94 94 50 50 50 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 10 10 10 10 10 10 18 18 18 38 38 38 - 78 78 78 142 134 106 216 158 10 242 186 14 -246 190 14 246 190 14 156 118 10 10 10 10 -116 116 116 182 182 182 138 138 138 154 154 154 -154 154 154 138 138 138 162 162 162 170 170 170 -178 178 178 138 138 138 162 162 162 162 162 162 -162 162 162 158 158 158 149 149 149 174 174 174 -134 134 134 174 174 174 170 170 170 158 158 158 -158 158 158 174 174 174 141 141 141 174 174 174 -149 149 149 166 166 166 158 158 158 174 174 174 -141 141 141 178 178 178 175 146 61 37 26 9 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 38 38 38 46 46 46 - 26 26 26 106 106 106 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 22 22 22 - 30 30 30 38 38 38 50 50 50 70 70 70 -106 106 106 190 142 34 226 170 11 242 186 14 -246 190 14 246 190 14 246 190 14 154 114 10 - 6 6 6 74 74 74 226 226 226 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 231 231 231 250 250 250 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 228 184 62 -241 196 14 241 208 19 232 195 16 38 30 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 30 30 30 26 26 26 -203 166 17 154 142 90 66 66 66 26 26 26 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 18 18 18 38 38 38 58 58 58 - 78 78 78 86 86 86 101 101 101 123 123 123 -175 146 61 210 150 10 234 174 13 246 186 14 -246 190 14 246 190 14 246 190 14 238 190 10 -102 78 10 2 2 6 46 46 46 198 198 198 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 234 234 234 242 242 242 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 224 178 62 -242 186 14 241 196 14 210 166 10 22 18 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 6 6 6 121 92 8 -238 202 15 232 195 16 82 82 82 34 34 34 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 14 14 14 38 38 38 70 70 70 154 122 46 -190 142 34 200 144 11 197 138 11 197 138 11 -213 154 11 226 170 11 242 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -225 175 15 46 32 6 2 2 6 22 22 22 -158 158 158 250 250 250 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 242 242 242 224 178 62 -239 182 13 236 186 11 213 154 11 46 32 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 61 42 6 225 175 15 -238 190 10 236 186 11 112 100 78 42 42 42 - 14 14 14 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 22 22 22 54 54 54 154 122 46 213 154 11 -226 170 11 230 174 11 226 170 11 226 170 11 -236 178 12 242 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -241 196 14 184 144 12 10 10 10 2 2 6 - 6 6 6 116 116 116 242 242 242 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 231 231 231 198 198 198 214 170 54 -236 178 12 236 178 12 210 150 10 137 92 6 - 18 14 6 2 2 6 2 2 6 2 2 6 - 6 6 6 70 47 6 200 144 11 236 178 12 -239 182 13 239 182 13 124 112 88 58 58 58 - 22 22 22 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 70 70 70 180 133 36 226 170 11 -239 182 13 242 186 14 242 186 14 246 186 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 232 195 16 98 70 6 2 2 6 - 2 2 6 2 2 6 66 66 66 221 221 221 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 206 206 206 198 198 198 214 166 58 -230 174 11 230 174 11 216 158 10 192 133 9 -163 110 8 116 81 8 102 78 10 116 81 8 -167 114 7 197 138 11 226 170 11 239 182 13 -242 186 14 242 186 14 162 146 94 78 78 78 - 34 34 34 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 30 30 30 78 78 78 190 142 34 226 170 11 -239 182 13 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 241 196 14 203 166 17 22 18 6 - 2 2 6 2 2 6 2 2 6 38 38 38 -218 218 218 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 206 206 206 198 198 198 202 162 69 -226 170 11 236 178 12 224 166 10 210 150 10 -200 144 11 197 138 11 192 133 9 197 138 11 -210 150 10 226 170 11 242 186 14 246 190 14 -246 190 14 246 186 14 225 175 15 124 112 88 - 62 62 62 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 174 135 50 224 166 10 -239 182 13 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 241 196 14 139 102 15 - 2 2 6 2 2 6 2 2 6 2 2 6 - 78 78 78 250 250 250 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 214 214 214 198 198 198 190 150 46 -219 162 10 236 178 12 234 174 13 224 166 10 -216 158 10 213 154 11 213 154 11 216 158 10 -226 170 11 239 182 13 246 190 14 246 190 14 -246 190 14 246 190 14 242 186 14 206 162 42 -101 101 101 58 58 58 30 30 30 14 14 14 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 74 74 74 174 135 50 216 158 10 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 241 196 14 226 184 13 - 61 42 6 2 2 6 2 2 6 2 2 6 - 22 22 22 238 238 238 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 226 226 226 187 187 187 180 133 36 -216 158 10 236 178 12 239 182 13 236 178 12 -230 174 11 226 170 11 226 170 11 230 174 11 -236 178 12 242 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 186 14 239 182 13 -206 162 42 106 106 106 66 66 66 34 34 34 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 26 26 26 70 70 70 163 133 67 213 154 11 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 241 196 14 -190 146 13 18 14 6 2 2 6 2 2 6 - 46 46 46 246 246 246 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 221 221 221 86 86 86 156 107 11 -216 158 10 236 178 12 242 186 14 246 186 14 -242 186 14 239 182 13 239 182 13 242 186 14 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -242 186 14 225 175 15 142 122 72 66 66 66 - 30 30 30 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 26 26 26 70 70 70 163 133 67 210 150 10 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -232 195 16 121 92 8 34 34 34 106 106 106 -221 221 221 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -242 242 242 82 82 82 18 14 6 163 110 8 -216 158 10 236 178 12 242 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 242 186 14 163 133 67 - 46 46 46 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 163 133 67 210 150 10 -236 178 12 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -241 196 14 215 174 15 190 178 144 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 218 218 218 - 58 58 58 2 2 6 22 18 6 167 114 7 -216 158 10 236 178 12 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 186 14 242 186 14 190 150 46 - 54 54 54 22 22 22 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 14 14 14 - 38 38 38 86 86 86 180 133 36 213 154 11 -236 178 12 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 232 195 16 190 146 13 214 214 214 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 170 170 170 26 26 26 - 2 2 6 2 2 6 37 26 9 163 110 8 -219 162 10 239 182 13 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 236 178 12 224 166 10 142 122 72 - 46 46 46 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 109 106 95 192 133 9 224 166 10 -242 186 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -242 186 14 226 184 13 210 162 10 142 110 46 -226 226 226 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -198 198 198 66 66 66 2 2 6 2 2 6 - 2 2 6 2 2 6 50 34 6 156 107 11 -219 162 10 239 182 13 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 242 186 14 -234 174 13 213 154 11 154 122 46 66 66 66 - 30 30 30 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 58 58 58 154 121 60 206 145 10 234 174 13 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 236 178 12 210 162 10 163 110 8 - 61 42 6 138 138 138 218 218 218 250 250 250 -253 253 253 253 253 253 253 253 253 250 250 250 -242 242 242 210 210 210 144 144 144 66 66 66 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 61 42 6 163 110 8 -216 158 10 236 178 12 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 239 182 13 230 174 11 216 158 10 -190 142 34 124 112 88 70 70 70 38 38 38 - 18 18 18 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 62 62 62 168 124 44 206 145 10 224 166 10 -236 178 12 239 182 13 242 186 14 242 186 14 -246 186 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 236 178 12 216 158 10 175 118 6 - 80 54 7 2 2 6 6 6 6 30 30 30 - 54 54 54 62 62 62 50 50 50 38 38 38 - 14 14 14 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 80 54 7 167 114 7 -213 154 11 236 178 12 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 242 186 14 239 182 13 239 182 13 -230 174 11 210 150 10 174 135 50 124 112 88 - 82 82 82 54 54 54 34 34 34 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 158 118 36 192 133 9 200 144 11 -216 158 10 219 162 10 224 166 10 226 170 11 -230 174 11 236 178 12 239 182 13 239 182 13 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 230 174 11 210 150 10 163 110 8 -104 69 6 10 10 10 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 91 60 6 167 114 7 -206 145 10 230 174 11 242 186 14 246 190 14 -246 190 14 246 190 14 246 186 14 242 186 14 -239 182 13 230 174 11 224 166 10 213 154 11 -180 133 36 124 112 88 86 86 86 58 58 58 - 38 38 38 22 22 22 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 14 14 14 - 34 34 34 70 70 70 138 110 50 158 118 36 -167 114 7 180 123 7 192 133 9 197 138 11 -200 144 11 206 145 10 213 154 11 219 162 10 -224 166 10 230 174 11 239 182 13 242 186 14 -246 186 14 246 186 14 246 186 14 246 186 14 -239 182 13 216 158 10 185 133 11 152 99 6 -104 69 6 18 14 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 80 54 7 152 99 6 -192 133 9 219 162 10 236 178 12 239 182 13 -246 186 14 242 186 14 239 182 13 236 178 12 -224 166 10 206 145 10 192 133 9 154 121 60 - 94 94 94 62 62 62 42 42 42 22 22 22 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 18 18 18 34 34 34 58 58 58 78 78 78 -101 98 89 124 112 88 142 110 46 156 107 11 -163 110 8 167 114 7 175 118 6 180 123 7 -185 133 11 197 138 11 210 150 10 219 162 10 -226 170 11 236 178 12 236 178 12 234 174 13 -219 162 10 197 138 11 163 110 8 130 83 6 - 91 60 6 10 10 10 2 2 6 2 2 6 - 18 18 18 38 38 38 38 38 38 38 38 38 - 38 38 38 38 38 38 38 38 38 38 38 38 - 38 38 38 38 38 38 26 26 26 2 2 6 - 2 2 6 6 6 6 70 47 6 137 92 6 -175 118 6 200 144 11 219 162 10 230 174 11 -234 174 13 230 174 11 219 162 10 210 150 10 -192 133 9 163 110 8 124 112 88 82 82 82 - 50 50 50 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 22 22 22 34 34 34 - 42 42 42 58 58 58 74 74 74 86 86 86 -101 98 89 122 102 70 130 98 46 121 87 25 -137 92 6 152 99 6 163 110 8 180 123 7 -185 133 11 197 138 11 206 145 10 200 144 11 -180 123 7 156 107 11 130 83 6 104 69 6 - 50 34 6 54 54 54 110 110 110 101 98 89 - 86 86 86 82 82 82 78 78 78 78 78 78 - 78 78 78 78 78 78 78 78 78 78 78 78 - 78 78 78 82 82 82 86 86 86 94 94 94 -106 106 106 101 101 101 86 66 34 124 80 6 -156 107 11 180 123 7 192 133 9 200 144 11 -206 145 10 200 144 11 192 133 9 175 118 6 -139 102 15 109 106 95 70 70 70 42 42 42 - 22 22 22 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 10 10 10 - 14 14 14 22 22 22 30 30 30 38 38 38 - 50 50 50 62 62 62 74 74 74 90 90 90 -101 98 89 112 100 78 121 87 25 124 80 6 -137 92 6 152 99 6 152 99 6 152 99 6 -138 86 6 124 80 6 98 70 6 86 66 30 -101 98 89 82 82 82 58 58 58 46 46 46 - 38 38 38 34 34 34 34 34 34 34 34 34 - 34 34 34 34 34 34 34 34 34 34 34 34 - 34 34 34 34 34 34 38 38 38 42 42 42 - 54 54 54 82 82 82 94 86 76 91 60 6 -134 86 6 156 107 11 167 114 7 175 118 6 -175 118 6 167 114 7 152 99 6 121 87 25 -101 98 89 62 62 62 34 34 34 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 6 6 6 10 10 10 - 18 18 18 22 22 22 30 30 30 42 42 42 - 50 50 50 66 66 66 86 86 86 101 98 89 -106 86 58 98 70 6 104 69 6 104 69 6 -104 69 6 91 60 6 82 62 34 90 90 90 - 62 62 62 38 38 38 22 22 22 14 14 14 - 10 10 10 10 10 10 10 10 10 10 10 10 - 10 10 10 10 10 10 6 6 6 10 10 10 - 10 10 10 10 10 10 10 10 10 14 14 14 - 22 22 22 42 42 42 70 70 70 89 81 66 - 80 54 7 104 69 6 124 80 6 137 92 6 -134 86 6 116 81 8 100 82 52 86 86 86 - 58 58 58 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 10 10 10 14 14 14 - 18 18 18 26 26 26 38 38 38 54 54 54 - 70 70 70 86 86 86 94 86 76 89 81 66 - 89 81 66 86 86 86 74 74 74 50 50 50 - 30 30 30 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 18 18 18 34 34 34 58 58 58 - 82 82 82 89 81 66 89 81 66 89 81 66 - 94 86 66 94 86 76 74 74 74 50 50 50 - 26 26 26 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 6 6 6 14 14 14 18 18 18 - 30 30 30 38 38 38 46 46 46 54 54 54 - 50 50 50 42 42 42 30 30 30 18 18 18 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 26 26 26 - 38 38 38 50 50 50 58 58 58 58 58 58 - 54 54 54 42 42 42 30 30 30 18 18 18 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 6 6 6 10 10 10 14 14 14 18 18 18 - 18 18 18 14 14 14 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 14 14 14 18 18 18 22 22 22 22 22 22 - 18 18 18 14 14 14 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index bebd371c6b93..d3a2bbed5fea 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -157,7 +157,7 @@ struct display_timings *of_get_display_timings(const struct device_node *np) return NULL; } - disp = kzalloc(sizeof(*disp), GFP_KERNEL); + disp = kzalloc_obj(*disp); if (!disp) { pr_err("%pOF: could not allocate struct disp'\n", np); goto dispfail; @@ -181,32 +181,30 @@ struct display_timings *of_get_display_timings(const struct device_node *np) if (disp->num_timings == 0) { /* should never happen, as entry was already found above */ pr_err("%pOF: no timings specified\n", np); - goto entryfail; + goto timingfail; } - disp->timings = kcalloc(disp->num_timings, - sizeof(struct display_timing *), - GFP_KERNEL); + disp->timings = kzalloc_objs(struct display_timing *, disp->num_timings); if (!disp->timings) { pr_err("%pOF: could not allocate timings array\n", np); - goto entryfail; + goto timingfail; } disp->num_timings = 0; disp->native_mode = 0; - for_each_child_of_node(timings_np, entry) { + for_each_child_of_node_scoped(timings_np, child) { struct display_timing *dt; int r; - dt = kmalloc(sizeof(*dt), GFP_KERNEL); + dt = kmalloc_obj(*dt); if (!dt) { pr_err("%pOF: could not allocate display_timing struct\n", np); goto timingfail; } - r = of_parse_display_timing(entry, dt); + r = of_parse_display_timing(child, dt); if (r) { /* * to not encourage wrong devicetrees, fail in case of @@ -218,7 +216,7 @@ struct display_timings *of_get_display_timings(const struct device_node *np) goto timingfail; } - if (native_mode == entry) + if (native_mode == child) disp->native_mode = disp->num_timings; disp->timings[disp->num_timings] = dt; diff --git a/drivers/video/screen_info_generic.c b/drivers/video/screen_info_generic.c index 64117c6367ab..763adbba71cb 100644 --- a/drivers/video/screen_info_generic.c +++ b/drivers/video/screen_info_generic.c @@ -5,6 +5,8 @@ #include <linux/screen_info.h> #include <linux/string.h> +#include <video/pixel_format.h> + static void resource_init_named(struct resource *r, resource_size_t start, resource_size_t size, const char *name, unsigned int flags) @@ -144,3 +146,92 @@ ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, return pos - r; } EXPORT_SYMBOL(screen_info_resources); + +/* + * The meaning of depth and bpp for direct-color formats is + * inconsistent: + * + * - DRM format info specifies depth as the number of color + * bits; including alpha, but not including filler bits. + * - Linux' EFI platform code computes lfb_depth from the + * individual color channels, including the reserved bits. + * - VBE 1.1 defines lfb_depth for XRGB1555 as 16, but later + * versions use 15. + * - On the kernel command line, 'bpp' of 32 is usually + * XRGB8888 including the filler bits, but 15 is XRGB1555 + * not including the filler bit. + * + * It is not easily possible to fix this in struct screen_info, + * as this could break UAPI. The best solution is to compute + * bits_per_pixel from the color bits, reserved bits and + * reported lfb_depth, whichever is highest. + */ + +u32 __screen_info_lfb_bits_per_pixel(const struct screen_info *si) +{ + u32 bits_per_pixel = si->lfb_depth; + + if (bits_per_pixel > 8) { + bits_per_pixel = max(max3(si->red_size + si->red_pos, + si->green_size + si->green_pos, + si->blue_size + si->blue_pos), + si->rsvd_size + si->rsvd_pos); + bits_per_pixel = max_t(u32, bits_per_pixel, si->lfb_depth); + } + + return bits_per_pixel; +} +EXPORT_SYMBOL(__screen_info_lfb_bits_per_pixel); + +static int __screen_info_lfb_pixel_format(const struct screen_info *si, struct pixel_format *f) +{ + u32 bits_per_pixel = __screen_info_lfb_bits_per_pixel(si); + + if (bits_per_pixel > U8_MAX) + return -EINVAL; + + f->bits_per_pixel = bits_per_pixel; + + if (si->lfb_depth > 8) { + f->indexed = false; + f->alpha.offset = 0; + f->alpha.length = 0; + f->red.offset = si->red_pos; + f->red.length = si->red_size; + f->green.offset = si->green_pos; + f->green.length = si->green_size; + f->blue.offset = si->blue_pos; + f->blue.length = si->blue_size; + } else { + f->indexed = true; + f->index.offset = 0; + f->index.length = si->lfb_depth; + } + + return 0; +} + +/** + * screen_info_pixel_format - Returns the screen-info format as pixel-format description + * + * @si: the screen_info + * @f: pointer to return pixel-format description + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int screen_info_pixel_format(const struct screen_info *si, struct pixel_format *f) +{ + unsigned int type = screen_info_video_type(si); + + /* TODO: Add support for additional types as needed. */ + switch (type) { + case VIDEO_TYPE_VLFB: + case VIDEO_TYPE_EFI: + return __screen_info_lfb_pixel_format(si, f); + } + + /* not supported */ + return -EINVAL; +} +EXPORT_SYMBOL(screen_info_pixel_format); diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c index 6c5833517141..8f34d8a74f09 100644 --- a/drivers/video/screen_info_pci.c +++ b/drivers/video/screen_info_pci.c @@ -4,11 +4,12 @@ #include <linux/printk.h> #include <linux/screen_info.h> #include <linux/string.h> +#include <linux/sysfb.h> static struct pci_dev *screen_info_lfb_pdev; static size_t screen_info_lfb_bar; -static resource_size_t screen_info_lfb_offset; -static struct resource screen_info_lfb_res = DEFINE_RES_MEM(0, 0); +static resource_size_t screen_info_lfb_res_start; // original start of resource +static resource_size_t screen_info_lfb_offset; // framebuffer offset within resource static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr) { @@ -26,12 +27,12 @@ static bool __screen_info_relocation_is_valid(const struct screen_info *si, stru void screen_info_apply_fixups(void) { - struct screen_info *si = &screen_info; + struct screen_info *si = &sysfb_primary_display.screen; if (screen_info_lfb_pdev) { struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar]; - if (pr->start != screen_info_lfb_res.start) { + if (pr->start != screen_info_lfb_res_start) { if (__screen_info_relocation_is_valid(si, pr)) { /* * Only update base if we have an actual @@ -47,46 +48,67 @@ void screen_info_apply_fixups(void) } } +static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type, + struct pci_bus_region *r) +{ + u64 base, size; + + base = __screen_info_lfb_base(si); + if (!base) + return -EINVAL; + + size = __screen_info_lfb_size(si, type); + if (!size) + return -EINVAL; + + r->start = base; + r->end = base + size - 1; + + return 0; +} + static void screen_info_fixup_lfb(struct pci_dev *pdev) { unsigned int type; - struct resource res[SCREEN_INFO_MAX_RESOURCES]; - size_t i, numres; + struct pci_bus_region bus_region; int ret; - const struct screen_info *si = &screen_info; + struct resource r = { + .flags = IORESOURCE_MEM, + }; + const struct resource *pr; + const struct screen_info *si = &sysfb_primary_display.screen; if (screen_info_lfb_pdev) return; // already found type = screen_info_video_type(si); - if (type != VIDEO_TYPE_EFI) - return; // only applies to EFI + if (!__screen_info_has_lfb(type)) + return; // only applies to EFI; maybe VESA - ret = screen_info_resources(si, res, ARRAY_SIZE(res)); + ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region); if (ret < 0) return; - numres = ret; - for (i = 0; i < numres; ++i) { - struct resource *r = &res[i]; - const struct resource *pr; - - if (!(r->flags & IORESOURCE_MEM)) - continue; - pr = pci_find_resource(pdev, r); - if (!pr) - continue; - - /* - * We've found a PCI device with the framebuffer - * resource. Store away the parameters to track - * relocation of the framebuffer aperture. - */ - screen_info_lfb_pdev = pdev; - screen_info_lfb_bar = pr - pdev->resource; - screen_info_lfb_offset = r->start - pr->start; - memcpy(&screen_info_lfb_res, r, sizeof(screen_info_lfb_res)); - } + /* + * Translate the PCI bus address to resource. Account + * for an offset if the framebuffer is behind a PCI host + * bridge. + */ + pcibios_bus_to_resource(pdev->bus, &r, &bus_region); + + pr = pci_find_resource(pdev, &r); + if (!pr) + return; + + /* + * We've found a PCI device with the framebuffer + * resource. Store away the parameters to track + * relocation of the framebuffer aperture. + */ + screen_info_lfb_pdev = pdev; + screen_info_lfb_bar = pr - pdev->resource; + screen_info_lfb_offset = r.start - pr->start; + screen_info_lfb_res_start = bus_region.start; } DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16, screen_info_fixup_lfb); diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c index 88a1758616e0..0d37e4b10447 100644 --- a/drivers/video/sticore.c +++ b/drivers/video/sticore.c @@ -558,7 +558,7 @@ sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) dest += sizeof(struct sti_rom_font); memcpy(dest, fbfont->data, bpc * fbfont->charcount); - cooked_font = kzalloc(sizeof(*cooked_font), GFP_KERNEL); + cooked_font = kzalloc_obj(*cooked_font); if (!cooked_font) { kfree(nf); return NULL; @@ -678,7 +678,7 @@ static int sti_cook_fonts(struct sti_cooked_rom *cooked_rom, struct sti_rom_font *raw_font, *font_start; struct sti_cooked_font *cooked_font; - cooked_font = kzalloc(sizeof(*cooked_font), GFP_KERNEL); + cooked_font = kzalloc_obj(*cooked_font); if (!cooked_font) return 0; @@ -692,7 +692,7 @@ static int sti_cook_fonts(struct sti_cooked_rom *cooked_rom, while (raw_font->next_font) { raw_font = ((void *)font_start) + (raw_font->next_font); - cooked_font->next_font = kzalloc(sizeof(*cooked_font), GFP_KERNEL); + cooked_font->next_font = kzalloc_obj(*cooked_font); if (!cooked_font->next_font) return 1; @@ -806,7 +806,7 @@ static int sti_read_rom(int wordmode, struct sti_struct *sti, struct sti_rom *raw = NULL; unsigned long revno; - cooked = kmalloc(sizeof *cooked, GFP_KERNEL); + cooked = kmalloc_obj(*cooked); if (!cooked) goto out_err; @@ -915,7 +915,7 @@ static struct sti_struct *sti_try_rom_generic(unsigned long address, return NULL; } - sti = kzalloc(sizeof(*sti), GFP_KERNEL); + sti = kzalloc_obj(*sti); if (!sti) return NULL; diff --git a/drivers/video/vgastate.c b/drivers/video/vgastate.c index 122fb3c3ec9d..6a29d198f62c 100644 --- a/drivers/video/vgastate.c +++ b/drivers/video/vgastate.c @@ -351,7 +351,7 @@ int save_vga(struct vgastate *state) { struct regstate *saved; - saved = kzalloc(sizeof(struct regstate), GFP_KERNEL); + saved = kzalloc_obj(struct regstate); if (saved == NULL) return 1; |
