diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-20 13:40:22 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-20 13:40:24 +0100 |
| commit | e94002ce32ebe2dd0ed9fde82dff4dbc52940bb2 (patch) | |
| tree | 08613dcc1873e321834c16cb0da29cc79ae97608 | |
| parent | 369c440d970ed02b3fe2e0e37d70353c327fdbde (diff) | |
| parent | 8c3c7f132f1464211ebf01d57533462637b82e73 (diff) | |
| download | linux-next-e94002ce32ebe2dd0ed9fde82dff4dbc52940bb2.tar.gz linux-next-e94002ce32ebe2dd0ed9fde82dff4dbc52940bb2.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git
34 files changed, 713 insertions, 177 deletions
diff --git a/drivers/gpu/drm/hyperv/Kconfig b/drivers/gpu/drm/hyperv/Kconfig index 86234f6a73f2..e48e35fb7f8b 100644 --- a/drivers/gpu/drm/hyperv/Kconfig +++ b/drivers/gpu/drm/hyperv/Kconfig @@ -8,7 +8,6 @@ config DRM_HYPERV help This is a KMS driver for Hyper-V synthetic video device. Choose this option if you would like to enable drm driver for Hyper-V virtual - machine. Unselect Hyper-V framebuffer driver (CONFIG_FB_HYPERV) so - that DRM driver is used by default. + machine. If M is selected the module will be called hyperv_drm. diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 085d3a202148..e8cd8cb76874 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1717,8 +1717,7 @@ config FB_MB862XX_PCI_GDC config FB_MB862XX_LIME bool "Lime GDC" depends on OF && PPC - select FB_FOREIGN_ENDIAN - select FB_LITTLE_ENDIAN + depends on FB_LITTLE_ENDIAN || FB_BOTH_ENDIAN help Framebuffer support for Fujitsu Lime GDC on host CPU bus. diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c index b8ed1c537293..dc2d4154e1b6 100644 --- a/drivers/video/fbdev/atafb.c +++ b/drivers/video/fbdev/atafb.c @@ -2081,8 +2081,12 @@ static int ext_encode_fix(struct fb_fix_screeninfo *fix, struct atafb_par *par) external_pmode == FB_TYPE_PACKED_PIXELS) ? FB_VISUAL_MONO10 : FB_VISUAL_MONO01; } else { - /* Use STATIC if we don't know how to access color registers */ - int visual = external_vgaiobase ? + /* Use STATIC if we don't know how to access color registers; + * SuperVidel 8bpp chunky (fb in SV RAM) uses the Falcon palette + */ + int visual = (external_vgaiobase || + (external_depth == 8 && + external_addr >= 0xa0000000)) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; switch (external_pmode) { @@ -2159,6 +2163,35 @@ static int ext_encode_var(struct fb_var_screeninfo *var, struct atafb_par *par) var->transp.offset = 0; var->transp.length = 0; var->transp.msb_right = 0; + if (external_pmode == -1 && external_depth == 16) { + /* RGB565 truecolor (e.g. SuperVidel native mode) */ + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + } else if (external_pmode == -1 && external_depth == 32) { + /* ARGB8888 truecolor (e.g. SuperVidel native mode) */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + } else if (external_pmode == FB_TYPE_PACKED_PIXELS && + external_depth == 8 && external_addr >= 0xa0000000) { + /* SuperVidel 8bpp chunky: palette has 8 bits per channel. + * Without this, fb_get_color_depth() sees length 0 and + * fbcon falls back to its 2-color palette — the console + * text (color 7) stays black on black. + */ + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + } var->yres_virtual = var->yres; var->xoffset = 0; var->yoffset = 0; @@ -2193,6 +2226,38 @@ static int ext_setcolreg(unsigned int regno, unsigned int red, { unsigned char colmask = (1 << external_bitspercol) - 1; + if (external_pmode == -1 && external_depth == 16) { + /* truecolor: only the pseudo palette for fbcon is needed */ + if (regno > 15) + return 1; + ((u32 *)info->pseudo_palette)[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + return 0; + } + if (external_pmode == -1 && external_depth == 32) { + /* ARGB8888, alpha forced opaque */ + if (regno > 15) + return 1; + ((u32 *)info->pseudo_palette)[regno] = 0xff000000 | + ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + return 0; + } + if (external_pmode == FB_TYPE_PACKED_PIXELS && external_depth == 8 && + external_addr >= 0xa0000000) { + /* SuperVidel native 8bpp chunky scans out via the Falcon + * palette registers, honoring all 8 bits per channel + */ + if (regno > 255) + return 1; + f030_col[regno] = ((red & 0xff00) << 16) | + ((green & 0xff00) << 8) | + ((blue & 0xff00) >> 8); + return 0; + } + if (!external_vgaiobase) return 1; @@ -2239,6 +2304,185 @@ static int ext_detect(void) return 1; } +/* ------------------- SuperVidel SuperBlitter ---------------------- */ + +/* + * Hardware blitter in the SuperVidel FPGA, operating within SV DDR2 RAM. + * FW revision >= 9 provides a command FIFO (async operation); older + * revisions are programmed directly with busy-polling. + */ +#define SVBLIT_REGS_PHYS 0x80010000 +#define SVBLIT_SRC1 0x58 /* bits 26:0 */ +#define SVBLIT_SRC2 0x5c +#define SVBLIT_DST 0x60 +#define SVBLIT_COUNT 0x64 /* bytes per line - 1 */ +#define SVBLIT_SRC1_OFFSET 0x68 /* line start to next line start */ +#define SVBLIT_SRC2_OFFSET 0x6c +#define SVBLIT_DST_OFFSET 0x70 +#define SVBLIT_MASK_AND_LINES 0x74 /* bits 11:0: number of lines */ +#define SVBLIT_CONTROL 0x78 /* bit 0: busy/start, bits 4:1: mode */ +#define SVBLIT_VERSION 0x7c /* bits 9:0: FW revision */ +#define SVBLIT_FIFO 0x80 /* wr: data; rd: bit 0 empty, bit 1 full */ + +/* + * SuperBlitter bug: Instead of declared 2048 bytes, 2032 is the real maximum. + */ +#define SVBLIT_MAX_SPAN 2032 + +static void __iomem *svblit_regs; +static int svblit_fw; + +static inline u32 svblit_rd(unsigned int reg) +{ + return __raw_readl(svblit_regs + reg); +} + +static inline void svblit_wr(unsigned int reg, u32 val) +{ + __raw_writel(val, svblit_regs + reg); +} + +/* wait until all queued blits have finished */ +static void svblit_wait(void) +{ + if (svblit_fw >= 9) + /* FIFO empty flag = fewer than 9 longwords queued */ + while (!(svblit_rd(SVBLIT_FIFO) & 1)) + cpu_relax(); + while (svblit_rd(SVBLIT_CONTROL) & 1) + cpu_relax(); +} + +/* + * FW >= 9 queues commands through the 512-longword FIFO: a command is + * 9 longwords (registers 0x58..0x78 in order), executed whenever >= 9 + * words are queued and the blitter is idle. The full flag rises at + * >= 500 queued words, so below it there is always room for a whole + * command — one flag check per command prevents overflow (dropped + * words would desync the 9-word framing until an SV reinit, which is + * exactly what overflowing did before this guard existed). Older FW + * is programmed directly with busy-polling. + * + * The line byte count field is 11 bits but see SVBLIT_MAX_SPAN. + */ +static void svblit_copy(u32 src, u32 dst, u32 nbytes, u32 src_offset, + u32 dst_offset, u32 lines) +{ + while (nbytes) { + u32 chunk = min(nbytes, SVBLIT_MAX_SPAN); + + if (svblit_fw >= 9) { + while (svblit_rd(SVBLIT_FIFO) & 2) + cpu_relax(); + svblit_wr(SVBLIT_FIFO, src); + svblit_wr(SVBLIT_FIFO, 0); + svblit_wr(SVBLIT_FIFO, dst); + svblit_wr(SVBLIT_FIFO, chunk - 1); + svblit_wr(SVBLIT_FIFO, src_offset); + svblit_wr(SVBLIT_FIFO, 0); + svblit_wr(SVBLIT_FIFO, dst_offset); + svblit_wr(SVBLIT_FIFO, lines); + svblit_wr(SVBLIT_FIFO, 0x01); + } else { + while (svblit_rd(SVBLIT_CONTROL) & 1) + cpu_relax(); + svblit_wr(SVBLIT_SRC1, src); + svblit_wr(SVBLIT_SRC2, 0); + svblit_wr(SVBLIT_DST, dst); + svblit_wr(SVBLIT_COUNT, chunk - 1); + svblit_wr(SVBLIT_SRC1_OFFSET, src_offset); + svblit_wr(SVBLIT_SRC2_OFFSET, 0); + svblit_wr(SVBLIT_DST_OFFSET, dst_offset); + svblit_wr(SVBLIT_MASK_AND_LINES, lines); + svblit_wr(SVBLIT_CONTROL, 0x01); + } + + src += chunk; + dst += chunk; + nbytes -= chunk; + } +} + +static int svblit_sync(struct fb_info *info) +{ + svblit_wait(); + return 0; +} + +static void svblit_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + u32 bytespp = info->var.bits_per_pixel / 8; + u32 pitch = info->fix.line_length; + + /* + * The blitter walks lines in ascending order, so overlapping + * moves down/right would read already overwritten data. Those + * are rare for fbcon (scrolling backwards); leave them and + * oversized areas to the CPU. + */ + if (area->height > 4095 || + area->dy > area->sy || + (area->dy == area->sy && area->dx > area->sx)) { + svblit_wait(); + cfb_copyarea(info, area); + return; + } + + svblit_copy(external_addr + area->sy * pitch + area->sx * bytespp, + external_addr + area->dy * pitch + area->dx * bytespp, + area->width * bytespp, pitch, pitch, area->height); + /* async: every CPU access to the fb goes through svblit_wait() */ +} + +static void svblit_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + u32 bytespp = info->var.bits_per_pixel / 8; + u32 pitch = info->fix.line_length; + u8 *line; + u32 pix; + + svblit_wait(); /* the CPU is about to touch the fb */ + + if (rect->rop != ROP_COPY || rect->height <= 1 || + rect->height > 4096) { + cfb_fillrect(info, rect); + return; + } + + pix = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? + ((u32 *)info->pseudo_palette)[rect->color] : rect->color; + + /* draw the first line with the CPU ... */ + line = (u8 *)info->screen_base + rect->dy * pitch + + rect->dx * bytespp; + switch (bytespp) { + case 1: + memset(line, pix, rect->width); + break; + case 2: + memset16((u16 *)line, pix, rect->width); + break; + default: + memset32((u32 *)line, pix, rect->width); + break; + } + + /* ... and let the blitter replicate it into the other lines */ + svblit_copy(external_addr + rect->dy * pitch + rect->dx * bytespp, + external_addr + (rect->dy + 1) * pitch + + rect->dx * bytespp, + rect->width * bytespp, 0, pitch, rect->height - 1); +} + +static void svblit_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + svblit_wait(); /* CPU rendering must not race queued blits */ + cfb_imageblit(info, image); +} + #endif /* ATAFB_EXT */ /* ------ This is the same for most hardware types -------- */ @@ -2422,7 +2666,9 @@ static void atafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) return; #ifdef ATAFB_FALCON - if (info->var.bits_per_pixel == 16) { + /* chunky modes (Falcon hicolor, external packed/truecolor) */ + if (info->fix.type == FB_TYPE_PACKED_PIXELS && + info->var.bits_per_pixel > 1) { cfb_fillrect(info, rect); return; } @@ -2463,7 +2709,9 @@ static void atafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) int rev_copy = 0; #ifdef ATAFB_FALCON - if (info->var.bits_per_pixel == 16) { + /* chunky modes (Falcon hicolor, external packed/truecolor) */ + if (info->fix.type == FB_TYPE_PACKED_PIXELS && + info->var.bits_per_pixel > 1) { cfb_copyarea(info, area); return; } @@ -2517,7 +2765,9 @@ static void atafb_imageblit(struct fb_info *info, const struct fb_image *image) u32 dx, dy, width, height, pitch; #ifdef ATAFB_FALCON - if (info->var.bits_per_pixel == 16) { + /* chunky modes (Falcon hicolor, external packed/truecolor) */ + if (info->fix.type == FB_TYPE_PACKED_PIXELS && + info->var.bits_per_pixel > 1) { cfb_imageblit(info, image); return; } @@ -2753,7 +3003,7 @@ static void __init atafb_setup_ext(char *spec) return; depth = simple_strtoul(p, NULL, 10); if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && - depth != 16 && depth != 24) + depth != 16 && depth != 24 && depth != 32) return; p = strsep(&spec, ";"); @@ -3097,10 +3347,33 @@ static int __init atafb_probe(struct platform_device *pdev) phys_screen_base = external_addr; screen_len = external_len & PAGE_MASK; memset (screen_base, 0, external_len); + + /* framebuffer in SV RAM: enable the SuperBlitter */ + if (external_addr >= 0xa0000000) { + svblit_regs = ioremap(SVBLIT_REGS_PHYS, 0x100); + if (svblit_regs) { + svblit_fw = svblit_rd(SVBLIT_VERSION) & 0x1ff; + atafb_ops.fb_fillrect = svblit_fillrect; + atafb_ops.fb_copyarea = svblit_copyarea; + atafb_ops.fb_imageblit = svblit_imageblit; + atafb_ops.fb_sync = svblit_sync; + fb_info.flags |= FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_FILLRECT; + dev_info(&pdev->dev, + "SuperBlitter enabled, FW revision %d (%s)\n", + svblit_fw, svblit_fw >= 9 ? + "async FIFO" : "sync"); + } + } } #endif /* ATAFB_EXT */ // strcpy(fb_info.mode->name, "Atari Builtin "); + /* Parent the fb device properly: without this fb0 registers as a + * virtual sysfs device with no /sys/class/graphics/fb0/device link, + * which makes Xorg's fbdevhw reject it ("No devices detected"). + */ + fb_info.device = &pdev->dev; fb_info.fbops = &atafb_ops; // try to set default (detected; requested) var do_fb_set_var(&atafb_predefined[default_par - 1], 1); @@ -3126,7 +3399,11 @@ static int __init atafb_probe(struct platform_device *pdev) atafb_set_disp(&fb_info); - fb_alloc_cmap(&(fb_info.cmap), 1 << fb_info.var.bits_per_pixel, 0); + /* truecolor visuals only need the 16-entry console palette; this + * also avoids 1 << 32 overflowing at 32bpp + */ + fb_alloc_cmap(&(fb_info.cmap), fb_info.var.bits_per_pixel > 8 ? + 16 : 1 << fb_info.var.bits_per_pixel, 0); dev_info(&pdev->dev, "Determined %dx%d, depth %d\n", fb_info.var.xres, diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c index bcb10e66221c..c349a1711ff5 100644 --- a/drivers/video/fbdev/aty/aty128fb.c +++ b/drivers/video/fbdev/aty/aty128fb.c @@ -2009,31 +2009,30 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return err; /* Enable device in PCI config */ - if ((err = pci_enable_device(pdev))) { + err = pcim_enable_device(pdev); + if (err) { printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n", err); return -ENODEV; } fb_addr = pci_resource_start(pdev, 0); - if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0), - "aty128fb FB")) { + if (!pcim_request_region(pdev, 0, "aty128fb FB")) { printk(KERN_ERR "aty128fb: cannot reserve frame " "buffer memory\n"); return -ENODEV; } reg_addr = pci_resource_start(pdev, 2); - if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2), - "aty128fb MMIO")) { + if (!pcim_request_region(pdev, 2, "aty128fb MMIO")) { printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n"); - goto err_free_fb; + return -ENODEV; } /* We have the resources. Now virtualize them */ info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev); if (!info) - goto err_free_mmio; + goto err_free_info; par = info->par; @@ -2041,7 +2040,8 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Virtualize mmio region */ info->fix.mmio_start = reg_addr; - par->regbase = pci_ioremap_bar(pdev, 2); + par->regbase = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); if (!par->regbase) goto err_free_info; @@ -2050,9 +2050,9 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF; /* Virtualize the framebuffer */ - info->screen_base = ioremap_wc(fb_addr, par->vram_size); + info->screen_base = devm_ioremap_wc(&pdev->dev, fb_addr, par->vram_size); if (!info->screen_base) - goto err_unmap_out; + goto err_free_info; /* Set up info->fix */ info->fix = aty128fb_fix; @@ -2063,7 +2063,7 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* If we can't test scratch registers, something is seriously wrong */ if (!register_test(par)) { printk(KERN_ERR "aty128fb: Can't write to video register!\n"); - goto err_out; + goto err_free_info; } #ifndef __sparc__ @@ -2085,25 +2085,15 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, info); if (!aty128_init(pdev, ent)) - goto err_out; + goto err_free_info; if (mtrr) par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, par->vram_size); return 0; -err_out: - iounmap(info->screen_base); -err_unmap_out: - iounmap(par->regbase); err_free_info: framebuffer_release(info); -err_free_mmio: - release_mem_region(pci_resource_start(pdev, 2), - pci_resource_len(pdev, 2)); -err_free_fb: - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); return -ENODEV; } @@ -2124,13 +2114,7 @@ static void aty128_remove(struct pci_dev *pdev) unregister_framebuffer(info); arch_phys_wc_del(par->wc_cookie); - iounmap(par->regbase); - iounmap(info->screen_base); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - release_mem_region(pci_resource_start(pdev, 2), - pci_resource_len(pdev, 2)); framebuffer_release(info); } #endif /* CONFIG_PCI */ diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 9fc5af09f86c..97cc8b6a9361 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -3435,7 +3435,7 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, raddr = addr + 0x7ff000UL; rrp = &pdev->resource[2]; if ((rrp->flags & IORESOURCE_MEM) && - request_mem_region(rrp->start, resource_size(rrp), "atyfb")) { + devm_request_mem_region(&pdev->dev, rrp->start, resource_size(rrp), "atyfb")) { par->aux_start = rrp->start; par->aux_size = resource_size(rrp); raddr = rrp->start; @@ -3448,9 +3448,9 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, * By using strong UC we force the MTRR to never have an * effect on the MMIO region on both non-PAT and PAT systems. */ - par->ati_regbase = ioremap_uc(info->fix.mmio_start, 0x1000); + par->ati_regbase = devm_ioremap_uc(&pdev->dev, info->fix.mmio_start, 0x1000); #else - par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000); + par->ati_regbase = devm_ioremap(&pdev->dev, info->fix.mmio_start, 0x1000); #endif if (par->ati_regbase == NULL) return -ENOMEM; @@ -3490,8 +3490,8 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, aty_fudge_framebuffer_len(info); - info->screen_base = ioremap_wc(info->fix.smem_start, - info->fix.smem_len); + info->screen_base = devm_ioremap_wc(&pdev->dev, info->fix.smem_start, + info->fix.smem_len); if (info->screen_base == NULL) { ret = -ENOMEM; goto atyfb_setup_generic_fail; @@ -3511,12 +3511,9 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, return 0; atyfb_setup_generic_fail: - iounmap(par->ati_regbase); + /* devm handles cleanup automatically on probe failure */ par->ati_regbase = NULL; - if (info->screen_base) { - iounmap(info->screen_base); - info->screen_base = NULL; - } + info->screen_base = NULL; return ret; } @@ -3536,7 +3533,7 @@ static int atyfb_pci_probe(struct pci_dev *pdev, return rc; /* Enable device in PCI config */ - if (pci_enable_device(pdev)) { + if (pcim_enable_device(pdev)) { PRINTKE("Cannot enable PCI device\n"); return -ENXIO; } @@ -3552,7 +3549,7 @@ static int atyfb_pci_probe(struct pci_dev *pdev, /* Reserve space */ res_start = rp->start; res_size = resource_size(rp); - if (!request_mem_region(res_start, res_size, "atyfb")) + if (!pcim_request_region(pdev, rp - pdev->resource, "atyfb")) return -EBUSY; /* Allocate framebuffer */ @@ -3612,17 +3609,9 @@ static int atyfb_pci_probe(struct pci_dev *pdev, err_release_io: #ifdef __sparc__ kfree(par->mmap_map); -#else - if (par->ati_regbase) - iounmap(par->ati_regbase); - if (info->screen_base) - iounmap(info->screen_base); #endif + /* devm handles cleanup automatically for non-sparc PCI devices */ err_release_mem: - if (par->aux_start) - release_mem_region(par->aux_start, par->aux_size); - - release_mem_region(par->res_start, par->res_size); framebuffer_release(info); return rc; @@ -3733,10 +3722,13 @@ static void atyfb_remove(struct fb_info *info) arch_phys_wc_del(par->wc_cookie); #ifndef __sparc__ - if (par->ati_regbase) - iounmap(par->ati_regbase); - if (info->screen_base) - iounmap(info->screen_base); + /* For PCI devices, devm handles unmapping automatically */ + if (par->bus_type != PCI) { + if (par->ati_regbase) + iounmap(par->ati_regbase); + if (info->screen_base) + iounmap(info->screen_base); + } #ifdef __BIG_ENDIAN if (info->sprite.addr) iounmap(info->sprite.addr); @@ -3745,10 +3737,7 @@ static void atyfb_remove(struct fb_info *info) #ifdef __sparc__ kfree(par->mmap_map); #endif - if (par->aux_start) - release_mem_region(par->aux_start, par->aux_size); - - if (par->res_start) + if (par->res_start && par->bus_type != PCI) release_mem_region(par->res_start, par->res_size); framebuffer_release(info); diff --git a/drivers/video/fbdev/au1100fb.c b/drivers/video/fbdev/au1100fb.c index c54cfcd832bb..39f86ffb1ae3 100644 --- a/drivers/video/fbdev/au1100fb.c +++ b/drivers/video/fbdev/au1100fb.c @@ -393,7 +393,7 @@ static struct au1100fb_panel known_lcd_panels[] = #define DRIVER_DESC "LCD controller driver for AU1100 processors" #define to_au1100fb_device(_info) \ - (_info ? container_of(_info, struct au1100fb_device, info) : NULL); + (_info ? container_of(_info, struct au1100fb_device, info) : NULL) /* Bitfields format supported by the controller. Note that the order of formats * SHOULD be the same as in the LCD_CONTROL_SBPPF field, so we can retrieve the diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index 7a7db7100499..6789773b22fb 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c @@ -329,8 +329,6 @@ static int clps711x_fb_probe(struct platform_device *pdev) return 0; - unregister_framebuffer(info); - out_fb_dealloc_cmap: regmap_update_bits(cfb->syscon, SYSCON_OFFSET, SYSCON1_LCDEN, 0); fb_dealloc_cmap(&info->cmap); diff --git a/drivers/video/fbdev/core/fb_io_fops.c b/drivers/video/fbdev/core/fb_io_fops.c index 7d16513690c3..545886cb3eeb 100644 --- a/drivers/video/fbdev/core/fb_io_fops.c +++ b/drivers/video/fbdev/core/fb_io_fops.c @@ -24,6 +24,14 @@ ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t if (total_size == 0) total_size = info->fix.smem_len; + /* + * Security Hardening: Defend against buggy legacy drivers that may + * calculate a malformed screen_size. Clamp total_size to the actual + * hardware mapped memory limit (smem_len) to prevent OOB access. + */ + if (info->fix.smem_len && total_size > info->fix.smem_len) + total_size = info->fix.smem_len; + if (p >= total_size) return 0; @@ -96,6 +104,14 @@ ssize_t fb_io_write(struct fb_info *info, const char __user *buf, size_t count, if (total_size == 0) total_size = info->fix.smem_len; + /* + * Security Hardening: Defend against buggy legacy drivers that may + * calculate a malformed screen_size. Clamp total_size to the actual + * hardware mapped memory limit (smem_len) to prevent OOB access. + */ + if (info->fix.smem_len && total_size > info->fix.smem_len) + total_size = info->fix.smem_len; + if (p > total_size) return -EFBIG; diff --git a/drivers/video/fbdev/core/fb_sys_fops.c b/drivers/video/fbdev/core/fb_sys_fops.c index be96b3b3942e..e97cf02f7c70 100644 --- a/drivers/video/fbdev/core/fb_sys_fops.c +++ b/drivers/video/fbdev/core/fb_sys_fops.c @@ -35,6 +35,14 @@ ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count, if (total_size == 0) total_size = info->fix.smem_len; + /* + * Security Hardening: Defend against buggy legacy drivers that may + * calculate a malformed screen_size. Clamp total_size to the actual + * hardware mapped memory limit (smem_len) to prevent OOB access. + */ + if (info->fix.smem_len && total_size > info->fix.smem_len) + total_size = info->fix.smem_len; + if (p >= total_size) return 0; @@ -80,6 +88,14 @@ ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, if (total_size == 0) total_size = info->fix.smem_len; + /* + * Security Hardening: Defend against buggy legacy drivers that may + * calculate a malformed screen_size. Clamp total_size to the actual + * hardware mapped memory limit (smem_len) to prevent OOB access. + */ + if (info->fix.smem_len && total_size > info->fix.smem_len) + total_size = info->fix.smem_len; + if (p > total_size) return -EFBIG; diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index d756b3603fa6..c23738988822 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c @@ -369,6 +369,9 @@ static int kyro_dev_overlay_create(u32 ulWidth, static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight) { + u32 right; + u32 bottom; + if (deviceInfo.ulOverlayOffset == 0) /* probably haven't called CreateOverlay yet */ return -EINVAL; @@ -378,11 +381,30 @@ static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight (x < 2 && ulWidth + 2 == 0)) return -EINVAL; + /* + * SetOverlayViewPort() adjusts X coordinates by +2 (left) and +1 + * (right) before packing them into 16-bit register fields. + */ + if (x > U16_MAX - 2 || y > U16_MAX) + return -EINVAL; + + right = x + ulWidth; + bottom = y + ulHeight; + + if (right < x || bottom < y) + return -EINVAL; + + right--; + bottom--; + + if (right > U16_MAX - 1 || bottom > U16_MAX) + return -EINVAL; + /* Stop Ramdac Output */ DisableRamdacOutput(deviceInfo.pSTGReg); SetOverlayViewPort(deviceInfo.pSTGReg, - x, y, x + ulWidth - 1, y + ulHeight - 1); + x, y, right, bottom); EnableOverlayPlane(deviceInfo.pSTGReg); /* Start Ramdac Output */ diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c index ac04a19b6849..24d312c29fb6 100644 --- a/drivers/video/fbdev/matrox/matroxfb_base.c +++ b/drivers/video/fbdev/matrox/matroxfb_base.c @@ -374,10 +374,6 @@ static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy) unregister_framebuffer(&minfo->fbcon); matroxfb_g450_shutdown(minfo); arch_phys_wc_del(minfo->wc_cookie); - iounmap(minfo->mmio.vbase.vaddr); - iounmap(minfo->video.vbase.vaddr); - release_mem_region(minfo->video.base, minfo->video.len_maximum); - release_mem_region(minfo->mmio.base, 16384); kfree(minfo); } @@ -1712,11 +1708,13 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) goto fail; } memsize = b->base->maxvram; - if (!request_mem_region(ctrlptr_phys, 16384, "matroxfb MMIO")) { + if (!devm_request_mem_region(&minfo->pcidev->dev, ctrlptr_phys, 16384, + "matroxfb MMIO")) { goto fail; } - if (!request_mem_region(video_base_phys, memsize, "matroxfb FB")) { - goto failCtrlMR; + if (!devm_request_mem_region(&minfo->pcidev->dev, video_base_phys, + memsize, "matroxfb FB")) { + goto fail; } minfo->video.len_maximum = memsize; /* convert mem (autodetect k, M) */ @@ -1727,19 +1725,19 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) memsize = mem; err = -ENOMEM; - minfo->mmio.vbase.vaddr = ioremap(ctrlptr_phys, 16384); + minfo->mmio.vbase.vaddr = devm_ioremap(&minfo->pcidev->dev, ctrlptr_phys, 16384); if (!minfo->mmio.vbase.vaddr) { printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys); - goto failVideoMR; + goto fail; } minfo->mmio.base = ctrlptr_phys; minfo->mmio.len = 16384; minfo->video.base = video_base_phys; - minfo->video.vbase.vaddr = ioremap_wc(video_base_phys, memsize); + minfo->video.vbase.vaddr = devm_ioremap_wc(&minfo->pcidev->dev, video_base_phys, memsize); if (!minfo->video.vbase.vaddr) { printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n", video_base_phys, memsize); - goto failCtrlIO; + goto fail; } { u_int32_t cmd; @@ -1954,13 +1952,6 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) return 0; failVideoIO:; matroxfb_g450_shutdown(minfo); - iounmap(minfo->video.vbase.vaddr); -failCtrlIO:; - iounmap(minfo->mmio.vbase.vaddr); -failVideoMR:; - release_mem_region(video_base_phys, minfo->video.len_maximum); -failCtrlMR:; - release_mem_region(ctrlptr_phys, 16384); fail:; return err; } @@ -2069,9 +2060,8 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm return -ENODEV; } pci_read_config_dword(pdev, PCI_COMMAND, &cmd); - if (pci_enable_device(pdev)) { + if (pcim_enable_device(pdev)) return -1; - } minfo = kzalloc_obj(*minfo); if (!minfo) diff --git a/drivers/video/fbdev/maxinefb.c b/drivers/video/fbdev/maxinefb.c index 52528eb4dfb4..67d4bd881041 100644 --- a/drivers/video/fbdev/maxinefb.c +++ b/drivers/video/fbdev/maxinefb.c @@ -61,7 +61,7 @@ static struct fb_fix_screeninfo maxinefb_fix __initdata = { /* Handle the funny Inmos RamDAC/video controller ... */ -void maxinefb_ims332_write_register(int regno, register unsigned int val) +static void maxinefb_ims332_write_register(int regno, register unsigned int val) { register unsigned char *regs = (char *) MAXINEFB_IMS332_ADDRESS; unsigned char *wptr; @@ -71,7 +71,9 @@ void maxinefb_ims332_write_register(int regno, register unsigned int val) *((volatile unsigned short *) (wptr)) = val; } -unsigned int maxinefb_ims332_read_register(int regno) +#if 0 +/* dead code: leave here for hardware interface documentation */ +static unsigned int maxinefb_ims332_read_register(int regno) { register unsigned char *regs = (char *) MAXINEFB_IMS332_ADDRESS; unsigned char *rptr; @@ -83,6 +85,7 @@ unsigned int maxinefb_ims332_read_register(int regno) return (j & 0xffff) | ((k & 0xff00) << 8); } +#endif /* Set the palette */ static int maxinefb_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -111,7 +114,7 @@ static const struct fb_ops maxinefb_ops = { .fb_setcolreg = maxinefb_setcolreg, }; -int __init maxinefb_init(void) +static int __init maxinefb_init(void) { unsigned long fboff; unsigned long fb_start; @@ -157,7 +160,7 @@ int __init maxinefb_init(void) fb_alloc_cmap(&fb_info.cmap, 256, 0); if (register_framebuffer(&fb_info) < 0) - return 1; + return -ENODEV; return 0; } @@ -166,9 +169,7 @@ static void __exit maxinefb_exit(void) unregister_framebuffer(&fb_info); } -#ifdef MODULE -MODULE_LICENSE("GPL"); -#endif module_init(maxinefb_init); module_exit(maxinefb_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c index 676c6d3ccc12..35939175bd00 100644 --- a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c @@ -408,7 +408,7 @@ static struct fb_ops mb862xxfb_ops = { }; /* initialize fb_info data */ -static int mb862xxfb_init_fbinfo(struct fb_info *fbi) +static int __maybe_unused mb862xxfb_init_fbinfo(struct fb_info *fbi) { struct mb862xxfb_par *par = fbi->par; struct mb862xx_gc_mode *mode = par->gc_mode; @@ -568,9 +568,9 @@ static ssize_t dispregs_show(struct device *dev, return ptr - buf; } -static DEVICE_ATTR_RO(dispregs); +static __maybe_unused DEVICE_ATTR_RO(dispregs); -static irqreturn_t mb862xx_intr(int irq, void *dev_id) +static irqreturn_t __maybe_unused mb862xx_intr(int irq, void *dev_id) { struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; unsigned long reg_ist, mask; diff --git a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c index 75bbdc0b4aa6..2c457da67a9f 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c +++ b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c @@ -505,8 +505,6 @@ static int mmphw_probe(struct platform_device *pdev) ret = devm_request_irq(ctrl->dev, ctrl->irq, ctrl_handle_irq, IRQF_SHARED, "lcd_controller", ctrl); if (ret < 0) { - dev_err(ctrl->dev, "%s unable to request IRQ %d\n", - __func__, ctrl->irq); ret = -ENXIO; goto failed; } diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index 7d20c4087aeb..4ef7a5ba43cc 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -1292,7 +1292,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) NVTRACE_ENTER(); assert(pd != NULL); - if (pci_enable_device(pd)) { + if (pcim_enable_device(pd)) { printk(KERN_ERR PFX "cannot enable PCI device\n"); return -ENODEV; } @@ -1305,7 +1305,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) nvidiafb_fix.mmio_start = pci_resource_start(pd, 0); nvidiafb_fix.mmio_len = pci_resource_len(pd, 0); - REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len); + REGS = devm_ioremap(&pd->dev, nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len); if (!REGS) { printk(KERN_ERR PFX "cannot ioremap MMIO base\n"); return -ENODEV; @@ -1333,7 +1333,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) if (info->pixmap.addr == NULL) goto err_out_kfree; - if (pci_request_regions(pd, "nvidiafb")) { + if (pcim_request_all_regions(pd, "nvidiafb")) { printk(KERN_ERR PFX "cannot request PCI regions\n"); goto err_out_enable; } @@ -1358,7 +1358,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4); if (NVCommonSetup(info)) - goto err_out_free_base0; + goto err_out_enable; par->FbAddress = nvidiafb_fix.smem_start; par->FbMapSize = par->RamAmountKBytes * 1024; @@ -1378,8 +1378,8 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize; par->CursorStart = par->FbUsableSize + (32 * 1024); - info->screen_base = ioremap_wc(nvidiafb_fix.smem_start, - par->FbMapSize); + info->screen_base = devm_ioremap_wc(&pd->dev, nvidiafb_fix.smem_start, + par->FbMapSize); info->screen_size = par->FbUsableSize; nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024; @@ -1423,19 +1423,15 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) return 0; err_out_iounmap_fb: - iounmap(info->screen_base); fb_destroy_modelist(&info->modelist); err_out_free_base1: fb_destroy_modedb(info->monspecs.modedb); nvidia_delete_i2c_busses(par); -err_out_free_base0: - pci_release_regions(pd); err_out_enable: kfree(info->pixmap.addr); err_out_kfree: framebuffer_release(info); err_out: - iounmap(REGS); return -ENODEV; } @@ -1450,11 +1446,8 @@ static void nvidiafb_remove(struct pci_dev *pd) unregister_framebuffer(info); arch_phys_wc_del(par->wc_cookie); - iounmap(info->screen_base); fb_destroy_modedb(info->monspecs.modedb); nvidia_delete_i2c_busses(par); - iounmap(par->REGS); - pci_release_regions(pd); kfree(info->pixmap.addr); framebuffer_release(info); NVTRACE_LEAVE(); diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c index 5e7963b4aa93..b2328671e067 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c @@ -1150,14 +1150,14 @@ static int dsicm_probe(struct platform_device *pdev) dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + mutex_init(&ddata->lock); + r = omapdss_register_display(dssdev); if (r) { dev_err(dev, "Failed to register panel\n"); goto err_reg; } - mutex_init(&ddata->lock); - atomic_set(&ddata->do_update, 0); ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); @@ -1185,10 +1185,8 @@ static int dsicm_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING, "taal vsync", ddata); - if (r) { - dev_err(dev, "IRQ request failed\n"); + if (r) return r; - } INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, dsicm_te_timeout_work_callback); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c index 330d9fb7d2b0..d98db01fdd39 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c @@ -326,8 +326,6 @@ struct dsi_data { spinlock_t irq_lock; struct dsi_isr_tables isr_tables; - /* space for a copy used by the interrupt handler */ - struct dsi_isr_tables isr_tables_copy; int update_channel; #ifdef DSI_PERF_MEASURE @@ -838,15 +836,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) timer_delete(&dsi->te_timer); #endif - /* make a copy and unlock, so that isrs can unregister - * themselves */ - memcpy(&dsi->isr_tables_copy, &dsi->isr_tables, - sizeof(dsi->isr_tables)); + dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus); spin_unlock(&dsi->irq_lock); - dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); - dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus); dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus); diff --git a/drivers/video/fbdev/platinumfb.c b/drivers/video/fbdev/platinumfb.c index a08d955d9b43..f74c937c839f 100644 --- a/drivers/video/fbdev/platinumfb.c +++ b/drivers/video/fbdev/platinumfb.c @@ -567,15 +567,30 @@ static int platinumfb_probe(struct platform_device* odev) /* frame buffer - map only 4MB */ pinfo->frame_buffer_phys = pinfo->rsrc_fb.start; pinfo->frame_buffer = ioremap_wt(pinfo->rsrc_fb.start, 0x400000); + if (!pinfo->frame_buffer) { + dev_err(&odev->dev, "failed to ioremap frame buffer\n"); + rc = -ENOMEM; + goto err_release_fb; + } pinfo->base_frame_buffer = pinfo->frame_buffer; /* registers */ pinfo->platinum_regs_phys = pinfo->rsrc_reg.start; pinfo->platinum_regs = ioremap(pinfo->rsrc_reg.start, 0x1000); + if (!pinfo->platinum_regs) { + dev_err(&odev->dev, "failed to ioremap registers\n"); + rc = -ENOMEM; + goto err_unmap_fb; + } pinfo->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */ request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap"); pinfo->cmap_regs = ioremap(pinfo->cmap_regs_phys, 0x1000); + if (!pinfo->cmap_regs) { + dev_err(&odev->dev, "failed to ioremap cmap registers\n"); + rc = -ENOMEM; + goto err_release_cmap; + } /* Grok total video ram */ out_be32(&pinfo->platinum_regs->reg[16].r, (unsigned)pinfo->frame_buffer_phys); @@ -623,13 +638,22 @@ static int platinumfb_probe(struct platform_device* odev) dev_set_drvdata(&odev->dev, info); rc = platinum_init_fb(info); - if (rc != 0) { - iounmap(pinfo->frame_buffer); - iounmap(pinfo->platinum_regs); - iounmap(pinfo->cmap_regs); - framebuffer_release(info); - } + if (rc != 0) + goto err_unmap_cmap; + + return 0; +err_unmap_cmap: + iounmap(pinfo->cmap_regs); +err_release_cmap: + release_mem_region(pinfo->cmap_regs_phys, 0x1000); +err_unmap_regs: + iounmap(pinfo->platinum_regs); +err_unmap_fb: + iounmap(pinfo->frame_buffer); +err_release_fb: + release_mem_region(pinfo->rsrc_fb.start, resource_size(&pinfo->rsrc_fb)); + framebuffer_release(info); return rc; } diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index 9428716e2dc4..a6e7abca7a06 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -639,7 +639,7 @@ static irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id) } #ifdef CONFIG_PVR2_DMA -static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, +static ssize_t pvr2fb_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos) { unsigned long dst, start, end, len; @@ -1077,7 +1077,7 @@ static struct pvr2_board { #ifdef CONFIG_PCI { pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" }, #endif - { 0, }, + { }, }; static int __init pvr2fb_init(void) diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index 6784888d93c9..ce8a823d7128 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -725,7 +725,6 @@ static int pxa168fb_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq, IRQF_SHARED, info->fix.id, fbi); if (ret < 0) { - dev_err(&pdev->dev, "unable to request IRQ\n"); ret = -ENXIO; goto failed_free_cmap; } diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index a2320e2fb8f2..fc5bd7e7aae6 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c @@ -615,10 +615,8 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, pxa3xx_gcu_handle_irq, 0, DRV_NAME, priv); - if (ret < 0) { - dev_err(dev, "request_irq failed\n"); + if (ret < 0) return ret; - } /* allocate dma memory */ priv->shared = dma_alloc_coherent(dev, SHARED_SIZE, diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index e418eee825fb..1682345fb9b0 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -2312,7 +2312,6 @@ static int pxafb_probe(struct platform_device *dev) ret = devm_request_irq(&dev->dev, irq, pxafb_handle_irq, 0, "LCD", fbi); if (ret) { - dev_err(&dev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; goto failed_free_mem; } diff --git a/drivers/video/fbdev/s3c-fb.c b/drivers/video/fbdev/s3c-fb.c index 2f4d707e2e09..82a3bc9de433 100644 --- a/drivers/video/fbdev/s3c-fb.c +++ b/drivers/video/fbdev/s3c-fb.c @@ -1421,10 +1421,8 @@ static int s3c_fb_probe(struct platform_device *pdev) ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq, 0, "s3c_fb", sfb); - if (ret) { - dev_err(dev, "irq request failed\n"); + if (ret) goto err_lcd_clk; - } dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index 0d362d2bf0e3..fea0172c45b5 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -1167,10 +1167,8 @@ static int sa1100fb_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, sa1100fb_handle_irq, 0, "LCD", fbi); - if (ret) { - dev_err(&pdev->dev, "request_irq failed: %d\n", ret); + if (ret) return ret; - } fbi->shannon_lcden = gpiod_get_optional(&pdev->dev, "shannon-lcden", GPIOD_OUT_LOW); diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index 7789196d2eb5..56ff3c50cd29 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -1728,7 +1728,7 @@ static int savage_map_mmio(struct fb_info *info) par->mmio.len = SAVAGE_NEWMMIO_REGSIZE; - par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len); + par->mmio.vbase = devm_ioremap(&par->pcidev->dev, par->mmio.pbase, par->mmio.len); if (!par->mmio.vbase) { printk("savagefb: unable to map memory mapped IO\n"); return -ENOMEM; @@ -1755,7 +1755,6 @@ static void savage_unmap_mmio(struct fb_info *info) savage_disable_mmio(par); if (par->mmio.vbase) { - iounmap(par->mmio.vbase); par->mmio.vbase = NULL; } } @@ -1774,7 +1773,7 @@ static int savage_map_video(struct fb_info *info, int video_len) par->video.pbase = pci_resource_start(par->pcidev, resource); par->video.len = video_len; - par->video.vbase = ioremap_wc(par->video.pbase, par->video.len); + par->video.vbase = devm_ioremap_wc(&par->pcidev->dev, par->video.pbase, par->video.len); if (!par->video.vbase) { printk("savagefb: unable to map screen memory\n"); @@ -1802,7 +1801,6 @@ static void savage_unmap_video(struct fb_info *info) if (par->video.vbase) { arch_phys_wc_del(par->video.wc_cookie); - iounmap(par->video.vbase); par->video.vbase = NULL; info->screen_base = NULL; } @@ -2188,11 +2186,12 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENOMEM; par = info->par; mutex_init(&par->open_lock); - err = pci_enable_device(dev); + err = pcim_enable_device(dev); if (err) goto failed_enable; - if ((err = pci_request_regions(dev, "savagefb"))) { + err = pcim_request_all_regions(dev, "savagefb"); + if (err) { printk(KERN_ERR "cannot request PCI regions\n"); goto failed_enable; } @@ -2200,7 +2199,7 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) err = -ENOMEM; if ((err = savage_init_fb_info(info, dev, id))) - goto failed_init; + goto failed_enable; err = savage_map_mmio(info); if (err) @@ -2331,8 +2330,6 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) savage_unmap_mmio(info); failed_mmio: kfree(info->pixmap.addr); - failed_init: - pci_release_regions(dev); failed_enable: framebuffer_release(info); @@ -2355,7 +2352,6 @@ static void savagefb_remove(struct pci_dev *dev) savage_unmap_video(info); savage_unmap_mmio(info); kfree(info->pixmap.addr); - pci_release_regions(dev); framebuffer_release(info); } } diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 644b8d97b381..c4fdecafd856 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/pwm.h> +#include <linux/spinlock.h> #include <linux/uaccess.h> #include <linux/regulator/consumer.h> @@ -72,6 +73,13 @@ struct ssd1307fb_par { struct i2c_client *client; u32 height; struct fb_info *info; + /* Pending damage, with exclusive x2/y2, protected by damage_lock. */ + spinlock_t damage_lock; + bool damage_pending; + u32 damage_x1; + u32 damage_x2; + u32 damage_y1; + u32 damage_y2; u8 lookup_table[4]; u32 page_offset; u32 col_offset; @@ -302,19 +310,49 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); } +static void ssd1307fb_schedule_damage(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) +{ + struct ssd1307fb_par *par = info->par; + unsigned long flags; + u32 x2, y2; + + if (!width || !height || x >= par->width || y >= par->height) + return; + + x2 = x + min(width, par->width - x); + y2 = y + min(height, par->height - y); + + spin_lock_irqsave(&par->damage_lock, flags); + if (par->damage_pending) { + par->damage_x1 = min(par->damage_x1, x); + par->damage_y1 = min(par->damage_y1, y); + par->damage_x2 = max(par->damage_x2, x2); + par->damage_y2 = max(par->damage_y2, y2); + } else { + par->damage_x1 = x; + par->damage_y1 = y; + par->damage_x2 = x2; + par->damage_y2 = y2; + par->damage_pending = true; + } + spin_unlock_irqrestore(&par->damage_lock, flags); + + /* Advance an already-pending mmap update as well. */ + mod_delayed_work(system_wq, &info->deferred_work, 0); +} + static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct ssd1307fb_par *par = info->par; - ssd1307fb_update_display(par); + ssd1307fb_schedule_damage(info, 0, 0, par->width, par->height); } static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) { - struct ssd1307fb_par *par = info->par; - - ssd1307fb_update_rect(par, x, y, width, height); + ssd1307fb_schedule_damage(info, x, y, width, height); } FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb, @@ -329,7 +367,30 @@ static const struct fb_ops ssd1307fb_ops = { static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist) { - ssd1307fb_update_display(info->par); + struct ssd1307fb_par *par = info->par; + unsigned long flags; + u32 x, y, width, height; + + spin_lock_irqsave(&par->damage_lock, flags); + if (!list_empty(pagereflist)) { + x = 0; + y = 0; + width = par->width; + height = par->height; + par->damage_pending = false; + } else if (par->damage_pending) { + x = par->damage_x1; + y = par->damage_y1; + width = par->damage_x2 - par->damage_x1; + height = par->damage_y2 - par->damage_y1; + par->damage_pending = false; + } else { + spin_unlock_irqrestore(&par->damage_lock, flags); + return; + } + spin_unlock_irqrestore(&par->damage_lock, flags); + + ssd1307fb_update_rect(par, x, y, width, height); } static int ssd1307fb_init(struct ssd1307fb_par *par) @@ -601,6 +662,7 @@ static int ssd1307fb_probe(struct i2c_client *client) par = info->par; par->info = info; par->client = client; + spin_lock_init(&par->damage_lock); par->device_info = device_get_match_data(dev); diff --git a/drivers/video/fbdev/sstfb.c b/drivers/video/fbdev/sstfb.c index 2ea947f57efb..2745557822f7 100644 --- a/drivers/video/fbdev/sstfb.c +++ b/drivers/video/fbdev/sstfb.c @@ -1492,6 +1492,7 @@ static const struct pci_device_id sstfb_id_tbl[] = { .driver_data = ID_VOODOO2, }, { 0 }, }; +MODULE_DEVICE_TABLE(pci, sstfb_id_tbl); static struct pci_driver sstfb_driver = { .name = "sstfb", diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index cc6a074f3165..2819875022f6 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -67,10 +67,12 @@ #include <linux/aperture.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> @@ -336,6 +338,144 @@ static u32 do_calc_pll(int freq, int *freq_out) return (n << 8) | (m << 2) | k; } +/* + * Convert a pllctrl register value back to a frequency in kHz. + * Formula from 3dfx documentation. + */ +static u32 tdfx_pll_to_khz(u32 pll) +{ + return (14318 * (((pll >> 8) & 0xff) + 2) / + (((pll >> 2) & 0x3f) + 2)) >> (pll & 3); +} + +/* Layout of the "OEM config" table in voodoo 3 BIOS */ +struct tdfx_bios_cfg { + __le32 pciinit0; /* 0x00 */ + __le32 miscinit0; /* 0x04 */ + __le32 miscinit1; /* 0x08 */ + __le32 draminit0; /* 0x0c */ + __le32 draminit1; /* 0x10 */ + __le32 agpinit0; /* 0x14 */ + __le32 pllctrl1; /* 0x18 - memory PLL */ + __le32 pllctrl2; /* 0x1c - graphics PLL */ + __le32 sgrammode; /* 0x20 - SGRAM/SDRAM mode register data */ +} __packed; + +#define TDFX_ROM_CFG_PTR 0x50 + +static bool tdfxfb_get_bios_cfg(struct pci_dev *pdev, + struct tdfx_bios_cfg *cfg) +{ + u16 romcfg, oemcfg; + void __iomem *rom; + size_t romsize; + u8 *image; + u32 khz; + + /* This only works for the Voodoo 3 for now */ + if (pdev->device != PCI_DEVICE_ID_3DFX_VOODOO3) + return false; + + rom = pci_map_rom(pdev, &romsize); + if (!rom || !romsize) + return false; + + image = vmalloc(romsize); + if (!image) { + pci_unmap_rom(pdev, rom); + return false; + } + memcpy_fromio(image, rom, romsize); + pci_unmap_rom(pdev, rom); + + /* ROM[0x50] -> ROM config table -> OEM config table */ + if (TDFX_ROM_CFG_PTR + 2 > romsize) + goto out; + romcfg = image[TDFX_ROM_CFG_PTR] | image[TDFX_ROM_CFG_PTR + 1] << 8; + if (romcfg == 0xffff || romcfg + 2 > romsize) + goto out; + oemcfg = image[romcfg] | image[romcfg + 1] << 8; + if (oemcfg == 0xffff || oemcfg + sizeof(*cfg) > romsize) + goto out; + memcpy(cfg, image + oemcfg, sizeof(*cfg)); + vfree(image); + + /* + * Make sure we didn't read garbage from the BIOS and will + * end up setting a frequency that explodes someone's expensive + * card. + */ + khz = tdfx_pll_to_khz(le32_to_cpu(cfg->pllctrl1)); + if (khz < 40000 || khz > 250000 || !le32_to_cpu(cfg->draminit0)) + return false; + return true; + +out: + vfree(image); + return false; +} + +/* + * Try to work out if the card was booted or not, just checks if + * one of the dram config registers matches what is in the config + * table if there is one. + * + * If we have a BIOS config table attempt to manually boot the + * card if needed. + */ +static int tdfxfb_hw_init(struct fb_info *info, struct pci_dev *pdev) +{ + u32 mempll, gfxpll, draminit0, draminit1, miscinit1, dram_mode; + struct tdfx_par *par = info->par; + struct tdfx_bios_cfg cfg; + bool have_cfg = tdfxfb_get_bios_cfg(pdev, &cfg); + + /* + * Can't tell if the card is booted or not, + * also cannot boot it. Card might not function. + */ + if (!have_cfg) + return 0; + + /* Card is, probably, already configured. */ + if (tdfx_inl(par, DRAMINIT0) == le32_to_cpu(cfg.draminit0)) + return 0; + + dev_info(&pdev->dev, + "Manually booting card using config table\n"); + + mempll = le32_to_cpu(cfg.pllctrl1); + gfxpll = le32_to_cpu(cfg.pllctrl2); + draminit0 = le32_to_cpu(cfg.draminit0); + draminit1 = le32_to_cpu(cfg.draminit1); + miscinit1 = le32_to_cpu(cfg.miscinit1); + dram_mode = le32_to_cpu(cfg.sgrammode); + tdfx_outl(par, PCIINIT0, le32_to_cpu(cfg.pciinit0)); + tdfx_outl(par, AGPINIT, le32_to_cpu(cfg.agpinit0)); + + /* memory clock, and the graphics clock if the card wants one */ + tdfx_outl(par, PLLCTRL1, mempll); + if (gfxpll) + tdfx_outl(par, PLLCTRL2, gfxpll); + /* flush posted writes */ + tdfx_inl(par, PLLCTRL1); + /* PLL lock */ + udelay(100); + + tdfx_outl(par, MISCINIT1, miscinit1); + tdfx_outl(par, DRAMINIT0, draminit0); + tdfx_outl(par, DRAMINIT1, draminit1); + + /* SDRAM/SGRAM wake up: load the mode register */ + tdfx_outl(par, DRAMDATA, dram_mode); + tdfx_outl(par, DRAMCOMMAND, 0x10d); + + tdfx_outl(par, LFBMEMORYCONFIG, 0x00001fff); + tdfx_outl(par, MISCINIT0, le32_to_cpu(cfg.miscinit0)); + + return 0; +} + static void do_write_regs(struct fb_info *info, struct banshee_reg *reg) { struct tdfx_par *par = info->par; @@ -345,6 +485,10 @@ static void do_write_regs(struct fb_info *info, struct banshee_reg *reg) tdfx_outl(par, MISCINIT1, tdfx_inl(par, MISCINIT1) | 0x01); + /* Wake the VGA core if it hasn't already been woken up */ + tdfx_outl(par, VGAINIT0, reg->vgainit0); + vga_outb(par, 0x3c3, 0x01); + crt_outb(par, 0x11, crt_inb(par, 0x11) & 0x7f); /* CRT unprotect */ banshee_make_room(par, 3); @@ -1385,7 +1529,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) return err; - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { printk(KERN_ERR "tdfxfb: Can't enable pdev: %d\n", err); return err; @@ -1431,6 +1575,9 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_err_regbase; } + if (tdfxfb_hw_init(info, pdev)) + goto out_err_regbase; + info->fix.smem_start = pci_resource_start(pdev, 1); info->fix.smem_len = do_lfb_size(default_par, pdev->device); if (!info->fix.smem_len) { @@ -1539,6 +1686,14 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_err_iobase; } + /* + * Program a video mode and clear the framebuffer now, this + * ensures the display comes up even if fbcon doesn't bind + * when the framebuffer is registered. + */ + tdfxfb_set_par(info); + memset_io(info->screen_base, 0, info->fix.smem_len); + if (register_framebuffer(info) < 0) { printk(KERN_ERR "tdfxfb: can't register framebuffer\n"); fb_dealloc_cmap(&info->cmap); diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index fdbb8671a810..e78d6f95c9c5 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -1586,19 +1586,29 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dlfb, desc += 5; /* the fixed header we've already parsed */ while (desc < desc_end) { + char *value; u8 length; u16 key; - key = *desc++; - key |= (u16)*desc++ << 8; + if (desc_end - desc < sizeof(key) + sizeof(length)) + goto unrecognized; + + key = get_unaligned_le16(desc); + desc += sizeof(key); length = *desc++; + if (length > desc_end - desc) + goto unrecognized; + + value = desc; switch (key) { case 0x0200: { /* max_area */ - u32 max_area = *desc++; - max_area |= (u32)*desc++ << 8; - max_area |= (u32)*desc++ << 16; - max_area |= (u32)*desc++ << 24; + u32 max_area; + + if (length < sizeof(max_area)) + goto unrecognized; + + max_area = get_unaligned_le32(value); dev_warn(&intf->dev, "DL chip limited to %d pixel modes\n", max_area); diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 9d82326c744f..ccc9dbc25813 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1907,6 +1907,8 @@ static int uvesafb_init(void) err = 0; } } + if (err) + cn_del_callback(&uvesafb_cn_id); return err; } diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c index 80f95dac32c8..dd9374e8ad09 100644 --- a/drivers/video/fbdev/via/viafbdev.c +++ b/drivers/video/fbdev/via/viafbdev.c @@ -16,7 +16,6 @@ #define _MASTER_FILE #include "global.h" -static char *viafb_name = "Via"; static u32 pseudo_pal[17]; /* video mode */ @@ -144,7 +143,7 @@ static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix, struct viafb_par *viaparinfo) { memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, viafb_name); + strcpy(fix->id, "Via"); fix->smem_start = viaparinfo->fbmem; fix->smem_len = viaparinfo->fbmem_free; diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c index 0d37e4b10447..1d4477f20450 100644 --- a/drivers/video/sticore.c +++ b/drivers/video/sticore.c @@ -325,7 +325,7 @@ static void sti_rom_copy(unsigned long base, unsigned long count, void *dest) -static char default_sti_path[21] __read_mostly; +static char default_sti_path[32] __read_mostly; #ifndef MODULE static int __init sti_setup(char *str) @@ -1148,6 +1148,26 @@ static void sti_init_roms(void) pr_info("STI GSC/PCI core graphics driver " STI_DRIVERVERSION "\n"); + /* + * Find default console by hardware path which is either stored in + * console entry in stable storage or alternatively from console path + * in PAGE0 used by BCH and PDC. + */ + if (!default_sti_path[0]) { + struct pdc_module_path conspath; + struct device *dev = NULL; + + if (pdc_stable_read(0x60, &conspath, sizeof(conspath)) == PDC_OK) + dev = hwpath_to_device(&conspath.path); + if (!dev) + dev = hwpath_to_device(&PAGE0->mem_cons.dp.path); + if (dev && dev_is_pci(dev)) + print_pci_hwpath(to_pci_dev(dev), default_sti_path); + else if (dev && !dev_is_pci(dev)) + print_pa_hwpath(to_parisc_device(dev), default_sti_path); + pr_debug("default graphic card: %s\n", default_sti_path); + } + /* Register drivers for native & PCI cards */ register_parisc_driver(&pa_sti_driver); WARN_ON(pci_register_driver(&pci_sti_driver)); diff --git a/include/linux/font.h b/include/linux/font.h index ea23b727388b..5e1cf9830084 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -49,6 +49,8 @@ static inline unsigned int font_glyph_pitch(unsigned int width) * scanlines, which is usually the glyph's height in scanlines. Fonts * coming from user space can sometimes have a different vertical pitch * with empty scanlines between two adjacent glyphs. + * + * Returns: the number of bytes per glyph */ static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpitch) { @@ -60,7 +62,7 @@ static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpit */ /** - * font_data_t - Raw font data + * typedef font_data_t - Raw font data * * Values of type font_data_t store a pointer to raw font data. The format * is monochrome. Each bit sets a pixel of a stored glyph. Font data does diff --git a/include/video/maxinefb.h b/include/video/maxinefb.h index 6aeb4acca2bd..2b66b32f355c 100644 --- a/include/video/maxinefb.h +++ b/include/video/maxinefb.h @@ -13,13 +13,13 @@ /* * IMS332 video controller register base address */ -#define MAXINEFB_IMS332_ADDRESS KSEG1ADDR(0x1c140000) +#define MAXINEFB_IMS332_ADDRESS CKSEG1ADDR(0x1c140000) /* * Begin of DECstation 5000/xx onboard framebuffer memory, default resolution * is 1024x768x8 */ -#define DS5000_xx_ONBOARD_FBMEM_START KSEG1ADDR(0x0a000000) +#define DS5000_xx_ONBOARD_FBMEM_START CKSEG1ADDR(0x0a000000) /* * The IMS 332 video controller used in the DECstation 5000/xx series |
