fbdev fixes & enhancements for 6.18-rc1:

Bug fixes:
 - Add bounds checking in bit_putcs to fix vmalloc-out-of-bounds (Albin Babu Varghese)
 - Fix logic error in "offb" name match (Finn Thain)
 - simplefb: Fix use after free in simplefb_detach_genpds() (Janne Grunau)
 - s3fb: Various fixes and powersave improvements (Zsolt Kajtar)
 
 Enhancements & code cleanups:
 - Various fixes in the documentation (Bagas Sanjaya)
 - Use string choices helpers (Chelsy Ratnawat)
 - xenfb: Use vmalloc_array to simplify code (Qianfeng Rong)
 - mb862xxfb: Use int type to store negative error codes (Qianfeng Rong)
 - Make drivers depend on LCD_CLASS_DEVICE (Thomas Zimmermann)
 - radeonfb: Remove stale product link in Kconfig (Sukrut Heroorkar)
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCaOkH4wAKCRD3ErUQojoP
 X4sPAP9NEDUdf2ZcPUiG30XVKdhHlxEffzAWs0RsSBaYlrAMjwD+Ja6IVI/5ib+e
 xEeDeExWrlC77Y90j8reBw9B+/Xu4Ak=
 =WS3Q
 -----END PGP SIGNATURE-----

Merge tag 'fbdev-for-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev

Pull fbdev updates from Helge Deller:
 "Beside the usual bunch of smaller bug fixes, the majority of changes
  were by Zsolt Kajtar to improve the s3fb driver.

  Bug fixes:
   - Bounds checking to fix vmalloc-out-of-bounds (Albin Babu Varghese)
   - Fix logic error in "offb" name match (Finn Thain)
   - simplefb: Fix use after free in (Janne Grunau)
   - s3fb: Various fixes and powersave improvements (Zsolt Kajtar)

  Enhancements & code cleanups:
   - Various fixes in the documentation (Bagas Sanjaya)
   - Use string choices helpers (Chelsy Ratnawat)
   - xenfb: Use vmalloc_array to simplify code (Qianfeng Rong)
   - mb862xxfb: use signed type for error codes (Qianfeng Rong)
   - Make drivers depend on LCD_CLASS_DEVICE (Thomas Zimmermann)
   - radeonfb: Remove stale product link in Kconfig (Sukrut Heroorkar)"

* tag 'fbdev-for-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev:
  fbdev: Fix logic error in "offb" name match
  fbdev: Add bounds checking in bit_putcs to fix vmalloc-out-of-bounds
  fbdev: Make drivers depend on LCD_CLASS_DEVICE
  fbdev: radeonfb: Remove stale product link in Kconfig
  Documentation: fb: Retitle driver docs
  Documentation: fb: ep93xx: Demote section headings
  Documentation: fb: Split toctree
  fbdev: simplefb: Fix use after free in simplefb_detach_genpds()
  fbdev: s3fb: Revert mclk stop in suspend
  fbdev: mb862xxfb: Use int type to store negative error codes
  fbdev: Use string choices helpers
  fbdev: core: Fix ubsan warning in pixel_to_pat
  fbdev: s3fb: Implement 1 and 2 BPP modes, improve 4 BPP
  fbdev: s3fb: Implement powersave for S3 FB
  fbdev: xenfb: Use vmalloc_array to simplify code
This commit is contained in:
Linus Torvalds 2025-10-10 09:36:23 -07:00
commit 06a88f4799
25 changed files with 259 additions and 165 deletions

View File

@ -1,8 +1,6 @@
================= =========================================
What is aty128fb? aty128fb - ATI Rage128 framebuffer driver
================= =========================================
.. [This file is cloned from VesaFB/matroxfb]
This is a driver for a graphic framebuffer for ATI Rage128 based devices This is a driver for a graphic framebuffer for ATI Rage128 based devices
on Intel and PPC boxes. on Intel and PPC boxes.

View File

@ -1,6 +1,6 @@
============== ===================================
What is efifb? efifb - Generic EFI platform driver
============== ===================================
This is a generic EFI platform driver for systems with UEFI firmware. The This is a generic EFI platform driver for systems with UEFI firmware. The
system must be booted via the EFI stub for this to be usable. efifb supports system must be booted via the EFI stub for this to be usable. efifb supports

View File

@ -41,7 +41,6 @@ your board initialisation function::
ep93xx_register_fb(&some_board_fb_info); ep93xx_register_fb(&some_board_fb_info);
=====================
Video Attribute Flags Video Attribute Flags
===================== =====================
@ -79,7 +78,6 @@ EP93XXFB_USE_SDCSN2 Use SDCSn[2] for the framebuffer.
EP93XXFB_USE_SDCSN3 Use SDCSn[3] for the framebuffer. EP93XXFB_USE_SDCSN3 Use SDCSn[3] for the framebuffer.
=============================== ====================================== =============================== ======================================
==================
Platform callbacks Platform callbacks
================== ==================
@ -101,7 +99,6 @@ obtained as follows::
/* Board specific framebuffer setup */ /* Board specific framebuffer setup */
} }
======================
Setting the video mode Setting the video mode
====================== ======================
@ -119,7 +116,6 @@ set when the module is installed::
modprobe ep93xx-fb video=320x240 modprobe ep93xx-fb video=320x240
==============
Screenpage bug Screenpage bug
============== ==============

View File

@ -1,8 +1,6 @@
============= =======================================
What is gxfb? gxfb - AMD Geode GX2 framebuffer driver
============= =======================================
.. [This file is cloned from VesaFB/aty128fb]
This is a graphics framebuffer driver for AMD Geode GX2 based processors. This is a graphics framebuffer driver for AMD Geode GX2 based processors.

View File

@ -4,26 +4,36 @@
Frame Buffer Frame Buffer
============ ============
General information
===================
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
api api
cmap_xfbdev
deferred_io
fbcon
framebuffer
internals
modedb
Driver documentation
====================
.. toctree::
:maxdepth: 1
arkfb arkfb
aty128fb aty128fb
cirrusfb cirrusfb
cmap_xfbdev
deferred_io
efifb efifb
ep93xx-fb ep93xx-fb
fbcon
framebuffer
gxfb gxfb
intel810 intel810
internals
lxfb lxfb
matroxfb matroxfb
metronomefb metronomefb
modedb
pvr2fb pvr2fb
pxafb pxafb
s3fb s3fb

View File

@ -1,9 +1,6 @@
============= ======================================
What is lxfb? lxfb - AMD Geode LX framebuffer driver
============= ======================================
.. [This file is cloned from VesaFB/aty128fb]
This is a graphics framebuffer driver for AMD Geode LX based processors. This is a graphics framebuffer driver for AMD Geode LX based processors.

View File

@ -1,9 +1,6 @@
================= ================================================
What is matroxfb? matroxfb - Framebuffer driver for Matrox devices
================= ================================================
.. [This file is cloned from VesaFB. Thanks go to Gerd Knorr]
This is a driver for a graphic framebuffer for Matrox devices on This is a driver for a graphic framebuffer for Matrox devices on
Alpha, Intel and PPC boxes. Alpha, Intel and PPC boxes.

View File

@ -1,6 +1,6 @@
=============== ===============================================
What is pvr2fb? pvr2fb - PowerVR 2 graphics frame buffer driver
=============== ===============================================
This is a driver for PowerVR 2 based graphics frame buffers, such as the This is a driver for PowerVR 2 based graphics frame buffers, such as the
one found in the Dreamcast. one found in the Dreamcast.

View File

@ -1,9 +1,6 @@
================= =================================================
What is sa1100fb? sa1100fb - SA-1100 LCD graphic framebuffer driver
================= =================================================
.. [This file is cloned from VesaFB/matroxfb]
This is a driver for a graphic framebuffer for the SA-1100 LCD This is a driver for a graphic framebuffer for the SA-1100 LCD
controller. controller.

View File

@ -1,6 +1,6 @@
============== =====================================
What is sisfb? sisfb - SiS framebuffer device driver
============== =====================================
sisfb is a framebuffer device driver for SiS (Silicon Integrated Systems) sisfb is a framebuffer device driver for SiS (Silicon Integrated Systems)
graphics chips. Supported are: graphics chips. Supported are:

View File

@ -1,6 +1,6 @@
================ ==========================================================
What is sm712fb? sm712fb - Silicon Motion SM712 graphics framebuffer driver
================ ==========================================================
This is a graphics framebuffer driver for Silicon Motion SM712 based processors. This is a graphics framebuffer driver for Silicon Motion SM712 based processors.

View File

@ -1,6 +1,6 @@
============== =======================================
What is tgafb? tgafb - TGA graphics framebuffer driver
============== =======================================
This is a driver for DECChip 21030 based graphics framebuffers, a.k.a. TGA This is a driver for DECChip 21030 based graphics framebuffers, a.k.a. TGA
cards, which are usually found in older Digital Alpha systems. The cards, which are usually found in older Digital Alpha systems. The

View File

@ -1,6 +1,6 @@
============== ==================================
What is udlfb? udlfb - DisplayLink USB 2.0 driver
============== ==================================
This is a driver for DisplayLink USB 2.0 era graphics chips. This is a driver for DisplayLink USB 2.0 era graphics chips.

View File

@ -1,6 +1,6 @@
=============== ===========================================
What is vesafb? vesafb - Generic graphic framebuffer driver
=============== ===========================================
This is a generic driver for a graphic framebuffer on intel boxes. This is a generic driver for a graphic framebuffer on intel boxes.

View File

@ -126,9 +126,9 @@ config FB_ACORN
config FB_CLPS711X config FB_CLPS711X
tristate "CLPS711X LCD support" tristate "CLPS711X LCD support"
depends on FB && (ARCH_CLPS711X || COMPILE_TEST) depends on FB && (ARCH_CLPS711X || COMPILE_TEST)
depends on LCD_CLASS_DEVICE
select FB_IOMEM_HELPERS select FB_IOMEM_HELPERS
select FB_MODE_HELPERS select FB_MODE_HELPERS
select LCD_CLASS_DEVICE
select VIDEOMODE_HELPERS select VIDEOMODE_HELPERS
help help
Say Y to enable the Framebuffer driver for the Cirrus Logic 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" tristate "Freescale i.MX1/21/25/27 LCD support"
depends on FB && HAVE_CLK && HAS_IOMEM depends on FB && HAVE_CLK && HAS_IOMEM
depends on ARCH_MXC || COMPILE_TEST depends on ARCH_MXC || COMPILE_TEST
select LCD_CLASS_DEVICE depends on LCD_CLASS_DEVICE
select FB_IOMEM_HELPERS select FB_IOMEM_HELPERS
select FB_MODE_HELPERS select FB_MODE_HELPERS
select VIDEOMODE_HELPERS select VIDEOMODE_HELPERS
@ -948,9 +948,6 @@ config FB_RADEON
a framebuffer device. There are both PCI and AGP versions. You 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. 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 config FB_RADEON_I2C
bool "DDC/I2C for ATI Radeon support" bool "DDC/I2C for ATI Radeon support"
depends on FB_RADEON depends on FB_RADEON
@ -1060,6 +1057,7 @@ config FB_S3
select FB_TILEBLITTING select FB_TILEBLITTING
select FB_SVGALIB select FB_SVGALIB
select VGASTATE select VGASTATE
select FB_CFB_REV_PIXELS_IN_BYTE
select FONT_8x16 if FRAMEBUFFER_CONSOLE select FONT_8x16 if FRAMEBUFFER_CONSOLE
help help
Driver for graphics boards with S3 Trio / S3 Virge chip. Driver for graphics boards with S3 Trio / S3 Virge chip.

View File

@ -160,6 +160,11 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
image.height = vc->vc_font.height; image.height = vc->vc_font.height;
image.depth = 1; image.depth = 1;
if (image.dy >= info->var.yres)
return;
image.height = min(image.height, info->var.yres - image.dy);
if (attribute) { if (attribute) {
buf = kmalloc(cellsize, GFP_ATOMIC); buf = kmalloc(cellsize, GFP_ATOMIC);
if (!buf) if (!buf)
@ -173,6 +178,18 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
cnt = count; cnt = count;
image.width = vc->vc_font.width * cnt; 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 = DIV_ROUND_UP(image.width, 8) + scan_align;
pitch &= ~scan_align; pitch &= ~scan_align;
size = pitch * image.height + buf_align; size = pitch * image.height + buf_align;

View File

@ -40,7 +40,7 @@ int fb_get_options(const char *name, char **option)
bool enabled; bool enabled;
if (name) if (name)
is_of = strncmp(name, "offb", 4); is_of = !strncmp(name, "offb", 4);
enabled = __video_get_options(name, &options, is_of); enabled = __video_get_options(name, &options, is_of);

View File

@ -92,8 +92,7 @@ static unsigned long pixel_to_pat(int bpp, u32 color)
pattern = pattern | pattern << bpp; pattern = pattern | pattern << bpp;
break; break;
default: default:
pattern = color; return color;
break;
} }
#ifndef __LITTLE_ENDIAN #ifndef __LITTLE_ENDIAN
pattern <<= (BITS_PER_LONG % bpp); pattern <<= (BITS_PER_LONG % bpp);

View File

@ -36,6 +36,7 @@
#include <video/of_videomode.h> #include <video/of_videomode.h>
#include <video/videomode.h> #include <video/videomode.h>
#include "../edid.h" #include "../edid.h"
#include <linux/string_choices.h>
/* /*
* EDID parser * EDID parser
@ -320,9 +321,9 @@ static void get_dpms_capabilities(unsigned char flags,
if (flags & DPMS_STANDBY) if (flags & DPMS_STANDBY)
specs->dpms |= FB_DPMS_STANDBY; specs->dpms |= FB_DPMS_STANDBY;
DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n", DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n",
(flags & DPMS_ACTIVE_OFF) ? "yes" : "no", str_yes_no(flags & DPMS_ACTIVE_OFF),
(flags & DPMS_SUSPEND) ? "yes" : "no", str_yes_no(flags & DPMS_SUSPEND),
(flags & DPMS_STANDBY) ? "yes" : "no"); str_yes_no(flags & DPMS_STANDBY));
} }
static void get_chroma(unsigned char *block, struct fb_monspecs *specs) static void get_chroma(unsigned char *block, struct fb_monspecs *specs)

View File

@ -674,7 +674,7 @@ static int of_platform_mb862xx_probe(struct platform_device *ofdev)
struct fb_info *info; struct fb_info *info;
struct resource res; struct resource res;
resource_size_t res_size; resource_size_t res_size;
unsigned long ret = -ENODEV; int ret = -ENODEV;
if (of_address_to_resource(np, 0, &res)) { if (of_address_to_resource(np, 0, &res)) {
dev_err(dev, "Invalid address\n"); dev_err(dev, "Invalid address\n");

View File

@ -22,6 +22,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/string_choices.h>
#ifdef CONFIG_BOOTX_TEXT #ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h> #include <asm/btext.h>
#endif #endif
@ -622,7 +623,7 @@ static int nvidiafb_set_par(struct fb_info *info)
else else
par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1); par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
printk(KERN_INFO PFX "Flat panel dithering %s\n", 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) ? info->fix.visual = (info->var.bits_per_pixel == 8) ?

View File

@ -60,6 +60,7 @@
#include <linux/soc/pxa/cpu.h> #include <linux/soc/pxa/cpu.h>
#include <video/of_display_timing.h> #include <video/of_display_timing.h>
#include <video/videomode.h> #include <video/videomode.h>
#include <linux/string_choices.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -1419,7 +1420,7 @@ static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on)
if (ret < 0) if (ret < 0)
pr_warn("Unable to %s LCD supply regulator: %d\n", pr_warn("Unable to %s LCD supply regulator: %d\n",
on ? "enable" : "disable", ret); str_enable_disable(on), ret);
else else
fbi->lcd_supply_enabled = on; fbi->lcd_supply_enabled = on;
} }

View File

@ -50,10 +50,14 @@ struct s3fb_info {
static const struct svga_fb_format s3fb_formats[] = { static const struct svga_fb_format s3fb_formats[] = {
{ 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, { 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}, 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, { 1, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 0}, 2,
FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 16}, 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, { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 1,
FB_TYPE_INTERLEAVED_PLANES, 1, FB_VISUAL_PSEUDOCOLOR, 8, 16}, 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, { 8, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0,
FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 4, 8}, FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 4, 8},
{16, {10, 5, 0}, {5, 5, 0}, {0, 5, 0}, {0, 0, 0}, 0, {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, /* 32bpp mode is not supported on VIRGE VX,
24bpp is not supported on others */ 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; rv = -EINVAL;
if (rv < 0) { if (rv < 0) {
@ -607,7 +611,7 @@ static int s3fb_set_par(struct fb_info *info)
struct s3fb_info *par = info->par; struct s3fb_info *par = info->par;
u32 value, mode, hmul, offset_value, screen_size, multiplex, dbytes; u32 value, mode, hmul, offset_value, screen_size, multiplex, dbytes;
u32 bpp = info->var.bits_per_pixel; u32 bpp = info->var.bits_per_pixel;
u32 htotal, hsstart; u32 htotal, hsstart, pel_msk;
if (bpp != 0) { if (bpp != 0) {
info->fix.ypanstep = 1; info->fix.ypanstep = 1;
@ -617,9 +621,11 @@ static int s3fb_set_par(struct fb_info *info)
info->tileops = NULL; info->tileops = NULL;
/* in 4bpp supports 8p wide tiles only, any tiles otherwise */ /* 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); 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 { } else {
bitmap_fill(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); 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, 0x50, 0x00);
vga_wcrt(par->state.vgabase, 0x67, 0x50); vga_wcrt(par->state.vgabase, 0x67, 0x50);
msleep(10); /* screen remains blank sometimes without this */ 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); 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); svga_wcrt_mask(par->state.vgabase, 0x31, 0x00, 0x40);
multiplex = 0; multiplex = 0;
hmul = 1; 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 */ /* Set mode-specific register values */
switch (mode) { switch (mode) {
case 0: case 0:
fb_dbg(info, "text mode\n"); fb_dbg(info, "text mode\n");
svga_set_textmode_vga_regs(par->state.vgabase); svga_set_textmode_vga_regs(par->state.vgabase);
pel_msk = 0x0f;
/* Set additional registers like in 8-bit mode */ /* Set additional registers like in 8-bit mode */
svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30);
@ -783,8 +794,11 @@ static int s3fb_set_par(struct fb_info *info)
} }
break; break;
case 1: case 1:
fb_dbg(info, "4 bit pseudocolor\n"); fb_dbg(info, "1 bit pseudocolor\n");
vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); 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 */ /* Set additional registers like in 8-bit mode */
svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); 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); svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30);
break; break;
case 2: 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 */ /* Set additional registers like in 8-bit mode */
svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); 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); svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30);
break; break;
case 3: 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"); fb_dbg(info, "8 bit pseudocolor\n");
svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30);
svga_wcrt_mask(par->state.vgabase, 0x05, 0x20, 0x60);
if (info->var.pixclock > 20000 || if (info->var.pixclock > 20000 ||
par->chip == CHIP_357_VIRGE_GX2 || par->chip == CHIP_357_VIRGE_GX2 ||
par->chip == CHIP_359_VIRGE_GX2P || par->chip == CHIP_359_VIRGE_GX2P ||
@ -819,7 +866,7 @@ static int s3fb_set_par(struct fb_info *info)
multiplex = 1; multiplex = 1;
} }
break; break;
case 4: case 6:
fb_dbg(info, "5/5/5 truecolor\n"); fb_dbg(info, "5/5/5 truecolor\n");
if (par->chip == CHIP_988_VIRGE_VX) { if (par->chip == CHIP_988_VIRGE_VX) {
if (info->var.pixclock > 20000) if (info->var.pixclock > 20000)
@ -847,7 +894,7 @@ static int s3fb_set_par(struct fb_info *info)
hmul = 2; hmul = 2;
} }
break; break;
case 5: case 7:
fb_dbg(info, "5/6/5 truecolor\n"); fb_dbg(info, "5/6/5 truecolor\n");
if (par->chip == CHIP_988_VIRGE_VX) { if (par->chip == CHIP_988_VIRGE_VX) {
if (info->var.pixclock > 20000) if (info->var.pixclock > 20000)
@ -875,12 +922,12 @@ static int s3fb_set_par(struct fb_info *info)
hmul = 2; hmul = 2;
} }
break; break;
case 6: case 8:
/* VIRGE VX case */ /* VIRGE VX case */
fb_dbg(info, "8/8/8 truecolor\n"); fb_dbg(info, "8/8/8 truecolor\n");
svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0);
break; break;
case 7: case 9:
fb_dbg(info, "8/8/8/8 truecolor\n"); fb_dbg(info, "8/8/8/8 truecolor\n");
svga_wcrt_mask(par->state.vgabase, 0x50, 0x30, 0x30); svga_wcrt_mask(par->state.vgabase, 0x50, 0x30, 0x30);
svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); 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"); fb_err(info, "unsupported mode - bug\n");
return -EINVAL; return -EINVAL;
} }
vga_w(par->state.vgabase, VGA_PEL_MSK, pel_msk);
if (par->chip != CHIP_988_VIRGE_VX) { if (par->chip != CHIP_988_VIRGE_VX) {
svga_wseq_mask(par->state.vgabase, 0x15, multiplex ? 0x10 : 0x00, 0x10); 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, static int s3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *fb) u_int transp, struct fb_info *fb)
{ {
struct s3fb_info *par = fb->par;
int cols;
switch (fb->var.bits_per_pixel) { switch (fb->var.bits_per_pixel) {
case 0: case 0:
case 1:
case 2:
case 4: 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: case 8:
if (regno >= 256) cols = 1 << (fb->var.bits_per_pixel ? fb->var.bits_per_pixel : 4);
if (regno >= cols)
return -EINVAL; return -EINVAL;
outb(0xFF, VGA_PEL_MSK); if ((fb->var.bits_per_pixel == 4) && ((fb->var.nonstd & 1) == 0))
outb(regno, VGA_PEL_IW); regno <<= 4;
outb(red >> 10, VGA_PEL_D);
outb(green >> 10, VGA_PEL_D); vga_w(par->state.vgabase, VGA_PEL_IW, regno);
outb(blue >> 10, VGA_PEL_D); 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; break;
case 16: case 16:
if (regno >= 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) static int s3fb_blank(int blank_mode, struct fb_info *info)
{ {
struct s3fb_info *par = info->par; 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) { switch (blank_mode) {
case FB_BLANK_UNBLANK: default:
fb_dbg(info, "unblank\n"); data = 0x00;
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);
break; break;
case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND:
fb_dbg(info, "hsync\n"); data = 0x02;
svga_wcrt_mask(par->state.vgabase, 0x56, 0x02, 0x06);
svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
break; break;
case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_VSYNC_SUSPEND:
fb_dbg(info, "vsync\n"); data = 0x04;
svga_wcrt_mask(par->state.vgabase, 0x56, 0x04, 0x06);
svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
break; break;
case FB_BLANK_POWERDOWN: case FB_BLANK_POWERDOWN:
fb_dbg(info, "sync down\n"); data = 0x06;
svga_wcrt_mask(par->state.vgabase, 0x56, 0x06, 0x06);
svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
break; 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; return 0;
} }
@ -1045,6 +1082,33 @@ static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
return 0; 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 */ /* Frame buffer operations */
@ -1063,7 +1127,7 @@ static const struct fb_ops s3fb_ops = {
.fb_copyarea = cfb_copyarea, .fb_copyarea = cfb_copyarea,
.fb_imageblit = s3fb_imageblit, .fb_imageblit = s3fb_imageblit,
__FB_DEFAULT_IOMEM_OPS_MMAP, __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); 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)); mutex_unlock(&(par->open_lock));
console_unlock(); console_unlock();
@ -1471,6 +1537,9 @@ static int __maybe_unused s3_pci_resume(struct device *dev)
return 0; 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); s3fb_set_par(info);
fb_set_suspend(info, 0); fb_set_suspend(info, 0);

View File

@ -93,6 +93,7 @@ struct simplefb_par {
static void simplefb_clocks_destroy(struct simplefb_par *par); static void simplefb_clocks_destroy(struct simplefb_par *par);
static void simplefb_regulators_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 * 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_regulators_destroy(info->par);
simplefb_clocks_destroy(info->par); simplefb_clocks_destroy(info->par);
simplefb_detach_genpds(info->par);
if (info->screen_base) if (info->screen_base)
iounmap(info->screen_base); iounmap(info->screen_base);
@ -445,13 +447,14 @@ static void simplefb_detach_genpds(void *res)
if (!IS_ERR_OR_NULL(par->genpds[i])) if (!IS_ERR_OR_NULL(par->genpds[i]))
dev_pm_domain_detach(par->genpds[i], true); dev_pm_domain_detach(par->genpds[i], true);
} }
par->num_genpds = 0;
} }
static int simplefb_attach_genpds(struct simplefb_par *par, static int simplefb_attach_genpds(struct simplefb_par *par,
struct platform_device *pdev) struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
unsigned int i; unsigned int i, num_genpds;
int err; int err;
err = of_count_phandle_with_args(dev->of_node, "power-domains", err = of_count_phandle_with_args(dev->of_node, "power-domains",
@ -465,26 +468,35 @@ static int simplefb_attach_genpds(struct simplefb_par *par,
return err; return err;
} }
par->num_genpds = err; num_genpds = err;
/* /*
* Single power-domain devices are handled by the driver core, so * Single power-domain devices are handled by the driver core, so
* nothing to do here. * nothing to do here.
*/ */
if (par->num_genpds <= 1) if (num_genpds <= 1) {
par->num_genpds = num_genpds;
return 0; 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); GFP_KERNEL);
if (!par->genpds) if (!par->genpds)
return -ENOMEM; return -ENOMEM;
par->genpd_links = devm_kcalloc(dev, par->num_genpds, par->genpd_links = devm_kcalloc(dev, num_genpds,
sizeof(*par->genpd_links), sizeof(*par->genpd_links),
GFP_KERNEL); GFP_KERNEL);
if (!par->genpd_links) if (!par->genpd_links)
return -ENOMEM; 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++) { for (i = 0; i < par->num_genpds; i++) {
par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); par->genpds[i] = dev_pm_domain_attach_by_id(dev, i);
if (IS_ERR(par->genpds[i])) { if (IS_ERR(par->genpds[i])) {
@ -506,9 +518,10 @@ static int simplefb_attach_genpds(struct simplefb_par *par,
dev_warn(dev, "failed to link power-domain %u\n", i); 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 #else
static void simplefb_detach_genpds(void *res) { }
static int simplefb_attach_genpds(struct simplefb_par *par, static int simplefb_attach_genpds(struct simplefb_par *par,
struct platform_device *pdev) struct platform_device *pdev)
{ {
@ -622,18 +635,20 @@ static int simplefb_probe(struct platform_device *pdev)
ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret);
goto error_regulators; goto error_genpds;
} }
ret = register_framebuffer(info); ret = register_framebuffer(info);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 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); dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
return 0; return 0;
error_genpds:
simplefb_detach_genpds(par);
error_regulators: error_regulators:
simplefb_regulators_destroy(par); simplefb_regulators_destroy(par);
error_clocks: error_clocks:

View File

@ -390,7 +390,7 @@ static int xenfb_probe(struct xenbus_device *dev,
info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 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) if (!info->gfns)
goto error_nomem; goto error_nomem;