diff options
author | Maxime Ripard <maxime@cerno.tech> | 2020-11-02 13:17:54 +0300 |
---|---|---|
committer | Maxime Ripard <maxime@cerno.tech> | 2020-11-02 13:17:54 +0300 |
commit | c489573b5b6ce6442ad4658d9d5ec77839b91622 (patch) | |
tree | 6db35e19cfb4c1280033a5545c94e15ac9c7b863 /drivers/video/fbdev | |
parent | d23bce963e7f17738374d700f0dc5464c5f7cba2 (diff) | |
parent | 3cea11cd5e3b00d91caf0b4730194039b45c5891 (diff) | |
download | linux-c489573b5b6ce6442ad4658d9d5ec77839b91622.tar.xz |
Merge drm/drm-next into drm-misc-next
Daniel needs -rc2 in drm-misc-next to merge some patches
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r-- | drivers/video/fbdev/Kconfig | 20 | ||||
-rw-r--r-- | drivers/video/fbdev/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/amba-clcd.c | 986 | ||||
-rw-r--r-- | drivers/video/fbdev/core/bitblit.c | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon.c | 358 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon.h | 9 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon_ccw.c | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon_cw.c | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon_rotate.c | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon_ud.c | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/core/tileblit.c | 3 | ||||
-rw-r--r-- | drivers/video/fbdev/hyperv_fb.c | 9 | ||||
-rw-r--r-- | drivers/video/fbdev/s3c2410fb-regs-lcd.h | 143 | ||||
-rw-r--r-- | drivers/video/fbdev/s3c2410fb.c | 16 | ||||
-rw-r--r-- | drivers/video/fbdev/sticore.h | 27 |
15 files changed, 1206 insertions, 411 deletions
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index e36578258b5b..cfb7f5612ef0 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -272,6 +272,26 @@ config FB_PM2_FIFO_DISCONNECT help Support the Permedia2 FIFO disconnect feature. +config FB_ARMCLCD + tristate "ARM PrimeCell PL110 support" + depends on ARM || ARM64 || COMPILE_TEST + depends on FB && ARM_AMBA && HAS_IOMEM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_MODE_HELPERS if OF + select VIDEOMODE_HELPERS if OF + select BACKLIGHT_CLASS_DEVICE if OF + help + This framebuffer device driver is for the ARM PrimeCell PL110 + Colour LCD controller. ARM PrimeCells provide the building + blocks for System on a Chip devices. + + If you want to compile this as a module (=code which can be + inserted into and removed from the running kernel), say M + here and read <file:Documentation/kbuild/modules.rst>. The module + will be called amba-clcd. + config FB_ACORN bool "Acorn VIDC support" depends on (FB = y) && ARM && ARCH_ACORN diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 2ff8849ffde6..477b9624b703 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o +obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c new file mode 100644 index 000000000000..b7682de412d8 --- /dev/null +++ b/drivers/video/fbdev/amba-clcd.c @@ -0,0 +1,986 @@ +/* + * linux/drivers/video/amba-clcd.c + * + * Copyright (C) 2001 ARM Limited, by David A Rusling + * Updated to 2.5, Deep Blue Solutions Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * ARM PrimeCell PL110 Color LCD Controller + */ +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> +#include <linux/backlight.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <video/display_timing.h> +#include <video/of_display_timing.h> +#include <video/videomode.h> + +#define to_clcd(info) container_of(info, struct clcd_fb, fb) + +/* This is limited to 16 characters when displayed by X startup */ +static const char *clcd_name = "CLCD FB"; + +/* + * Unfortunately, the enable/disable functions may be called either from + * process or IRQ context, and we _need_ to delay. This is _not_ good. + */ +static inline void clcdfb_sleep(unsigned int ms) +{ + if (in_atomic()) { + mdelay(ms); + } else { + msleep(ms); + } +} + +static inline void clcdfb_set_start(struct clcd_fb *fb) +{ + unsigned long ustart = fb->fb.fix.smem_start; + unsigned long lstart; + + ustart += fb->fb.var.yoffset * fb->fb.fix.line_length; + lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2; + + writel(ustart, fb->regs + CLCD_UBAS); + writel(lstart, fb->regs + CLCD_LBAS); +} + +static void clcdfb_disable(struct clcd_fb *fb) +{ + u32 val; + + if (fb->board->disable) + fb->board->disable(fb); + + if (fb->panel->backlight) { + fb->panel->backlight->props.power = FB_BLANK_POWERDOWN; + backlight_update_status(fb->panel->backlight); + } + + val = readl(fb->regs + fb->off_cntl); + if (val & CNTL_LCDPWR) { + val &= ~CNTL_LCDPWR; + writel(val, fb->regs + fb->off_cntl); + + clcdfb_sleep(20); + } + if (val & CNTL_LCDEN) { + val &= ~CNTL_LCDEN; + writel(val, fb->regs + fb->off_cntl); + } + + /* + * Disable CLCD clock source. + */ + if (fb->clk_enabled) { + fb->clk_enabled = false; + clk_disable(fb->clk); + } +} + +static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) +{ + /* + * Enable the CLCD clock source. + */ + if (!fb->clk_enabled) { + fb->clk_enabled = true; + clk_enable(fb->clk); + } + + /* + * Bring up by first enabling.. + */ + cntl |= CNTL_LCDEN; + writel(cntl, fb->regs + fb->off_cntl); + + clcdfb_sleep(20); + + /* + * and now apply power. + */ + cntl |= CNTL_LCDPWR; + writel(cntl, fb->regs + fb->off_cntl); + + /* + * Turn on backlight + */ + if (fb->panel->backlight) { + fb->panel->backlight->props.power = FB_BLANK_UNBLANK; + backlight_update_status(fb->panel->backlight); + } + + /* + * finally, enable the interface. + */ + if (fb->board->enable) + fb->board->enable(fb); +} + +static int +clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) +{ + u32 caps; + int ret = 0; + + if (fb->panel->caps && fb->board->caps) + caps = fb->panel->caps & fb->board->caps; + else { + /* Old way of specifying what can be used */ + caps = fb->panel->cntl & CNTL_BGR ? + CLCD_CAP_BGR : CLCD_CAP_RGB; + /* But mask out 444 modes as they weren't supported */ + caps &= ~CLCD_CAP_444; + } + + /* Only TFT panels can do RGB888/BGR888 */ + if (!(fb->panel->cntl & CNTL_LCDTFT)) + caps &= ~CLCD_CAP_888; + + memset(&var->transp, 0, sizeof(var->transp)); + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + /* If we can't do 5551, reject */ + caps &= CLCD_CAP_5551; + if (!caps) { + ret = -EINVAL; + break; + } + + var->red.length = var->bits_per_pixel; + var->red.offset = 0; + var->green.length = var->bits_per_pixel; + var->green.offset = 0; + var->blue.length = var->bits_per_pixel; + var->blue.offset = 0; + break; + + case 16: + /* If we can't do 444, 5551 or 565, reject */ + if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) { + ret = -EINVAL; + break; + } + + /* + * Green length can be 4, 5 or 6 depending whether + * we're operating in 444, 5551 or 565 mode. + */ + if (var->green.length == 4 && caps & CLCD_CAP_444) + caps &= CLCD_CAP_444; + if (var->green.length == 5 && caps & CLCD_CAP_5551) + caps &= CLCD_CAP_5551; + else if (var->green.length == 6 && caps & CLCD_CAP_565) + caps &= CLCD_CAP_565; + else { + /* + * PL110 officially only supports RGB555, + * but may be wired up to allow RGB565. + */ + if (caps & CLCD_CAP_565) { + var->green.length = 6; + caps &= CLCD_CAP_565; + } else if (caps & CLCD_CAP_5551) { + var->green.length = 5; + caps &= CLCD_CAP_5551; + } else { + var->green.length = 4; + caps &= CLCD_CAP_444; + } + } + + if (var->green.length >= 5) { + var->red.length = 5; + var->blue.length = 5; + } else { + var->red.length = 4; + var->blue.length = 4; + } + break; + case 32: + /* If we can't do 888, reject */ + caps &= CLCD_CAP_888; + if (!caps) { + ret = -EINVAL; + break; + } + + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + break; + default: + ret = -EINVAL; + break; + } + + /* + * >= 16bpp displays have separate colour component bitfields + * encoded in the pixel data. Calculate their position from + * the bitfield length defined above. + */ + if (ret == 0 && var->bits_per_pixel >= 16) { + bool bgr, rgb; + + bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0; + rgb = caps & CLCD_CAP_RGB && var->red.offset == 0; + + if (!bgr && !rgb) + /* + * The requested format was not possible, try just + * our capabilities. One of BGR or RGB must be + * supported. + */ + bgr = caps & CLCD_CAP_BGR; + + if (bgr) { + var->blue.offset = 0; + var->green.offset = var->blue.offset + var->blue.length; + var->red.offset = var->green.offset + var->green.length; + } else { + var->red.offset = 0; + var->green.offset = var->red.offset + var->red.length; + var->blue.offset = var->green.offset + var->green.length; + } + } + + return ret; +} + +static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + int ret = -EINVAL; + + if (fb->board->check) + ret = fb->board->check(fb, var); + + if (ret == 0 && + var->xres_virtual * var->bits_per_pixel / 8 * + var->yres_virtual > fb->fb.fix.smem_len) + ret = -EINVAL; + + if (ret == 0) + ret = clcdfb_set_bitfields(fb, var); + + return ret; +} + +static int clcdfb_set_par(struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + struct clcd_regs regs; + + fb->fb.fix.line_length = fb->fb.var.xres_virtual * + fb->fb.var.bits_per_pixel / 8; + + if (fb->fb.var.bits_per_pixel <= 8) + fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + + fb->board->decode(fb, ®s); + + clcdfb_disable(fb); + + writel(regs.tim0, fb->regs + CLCD_TIM0); + writel(regs.tim1, fb->regs + CLCD_TIM1); + writel(regs.tim2, fb->regs + CLCD_TIM2); + writel(regs.tim3, fb->regs + CLCD_TIM3); + + clcdfb_set_start(fb); + + clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); + + fb->clcd_cntl = regs.cntl; + + clcdfb_enable(fb, regs.cntl); + +#ifdef DEBUG + printk(KERN_INFO + "CLCD: Registers set to\n" + " %08x %08x %08x %08x\n" + " %08x %08x %08x %08x\n", + readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), + readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), + readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), + readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl)); +#endif + + return 0; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +/* + * Set a single color register. The values supplied have a 16 bit + * magnitude. Return != 0 for invalid regno. + */ +static int +clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + + if (regno < 16) + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + + if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { + int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); + u32 val, mask, newval; + + newval = (red >> 11) & 0x001f; + newval |= (green >> 6) & 0x03e0; + newval |= (blue >> 1) & 0x7c00; + + /* + * 3.2.11: if we're configured for big endian + * byte order, the palette entries are swapped. + */ + if (fb->clcd_cntl & CNTL_BEBO) + regno ^= 1; + + if (regno & 1) { + newval <<= 16; + mask = 0x0000ffff; + } else { + mask = 0xffff0000; + } + + val = readl(fb->regs + hw_reg) & mask; + writel(val | newval, fb->regs + hw_reg); + } + + return regno > 255; +} + +/* + * Blank the screen if blank_mode != 0, else unblank. If blank == NULL + * then the caller blanks by setting the CLUT (Color Look Up Table) to all + * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due + * to e.g. a video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown + */ +static int clcdfb_blank(int blank_mode, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + + if (blank_mode != 0) { + clcdfb_disable(fb); + } else { + clcdfb_enable(fb, fb->clcd_cntl); + } + return 0; +} + +static int clcdfb_mmap(struct fb_info *info, + struct vm_area_struct *vma) +{ + struct clcd_fb *fb = to_clcd(info); + unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT; + int ret = -EINVAL; + + len = info->fix.smem_len; + + if (off <= len && vma->vm_end - vma->vm_start <= len - off && + fb->board->mmap) + ret = fb->board->mmap(fb, vma); + + return ret; +} + +static const struct fb_ops clcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = clcdfb_check_var, + .fb_set_par = clcdfb_set_par, + .fb_setcolreg = clcdfb_setcolreg, + .fb_blank = clcdfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = clcdfb_mmap, +}; + +static int clcdfb_register(struct clcd_fb *fb) +{ + int ret; + + /* + * ARM PL111 always has IENB at 0x1c; it's only PL110 + * which is reversed on some platforms. + */ + if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) { + fb->off_ienb = CLCD_PL111_IENB; + fb->off_cntl = CLCD_PL111_CNTL; + } else { + fb->off_ienb = CLCD_PL110_IENB; + fb->off_cntl = CLCD_PL110_CNTL; + } + + fb->clk = clk_get(&fb->dev->dev, NULL); + if (IS_ERR(fb->clk)) { + ret = PTR_ERR(fb->clk); + goto out; + } + + ret = clk_prepare(fb->clk); + if (ret) + goto free_clk; + + fb->fb.device = &fb->dev->dev; + + fb->fb.fix.mmio_start = fb->dev->res.start; + fb->fb.fix.mmio_len = resource_size(&fb->dev->res); + + fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); + if (!fb->regs) { + printk(KERN_ERR "CLCD: unable to remap registers\n"); + ret = -ENOMEM; + goto clk_unprep; + } + + fb->fb.fbops = &clcdfb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + + strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.type_aux = 0; + fb->fb.fix.xpanstep = 0; + fb->fb.fix.ypanstep = 0; + fb->fb.fix.ywrapstep = 0; + fb->fb.fix.accel = FB_ACCEL_NONE; + + fb->fb.var.xres = fb->panel->mode.xres; + fb->fb.var.yres = fb->panel->mode.yres; + fb->fb.var.xres_virtual = fb->panel->mode.xres; + fb->fb.var.yres_virtual = fb->panel->mode.yres; + fb->fb.var.bits_per_pixel = fb->panel->bpp; + fb->fb.var.grayscale = fb->panel->grayscale; + fb->fb.var.pixclock = fb->panel->mode.pixclock; + fb->fb.var.left_margin = fb->panel->mode.left_margin; + fb->fb.var.right_margin = fb->panel->mode.right_margin; + fb->fb.var.upper_margin = fb->panel->mode.upper_margin; + fb->fb.var.lower_margin = fb->panel->mode.lower_margin; + fb->fb.var.hsync_len = fb->panel->mode.hsync_len; + fb->fb.var.vsync_len = fb->panel->mode.vsync_len; + fb->fb.var.sync = fb->panel->mode.sync; + fb->fb.var.vmode = fb->panel->mode.vmode; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.nonstd = 0; + fb->fb.var.height = fb->panel->height; + fb->fb.var.width = fb->panel->width; + fb->fb.var.accel_flags = 0; + + fb->fb.monspecs.hfmin = 0; + fb->fb.monspecs.hfmax = 100000; + fb->fb.monspecs.vfmin = 0; + fb->fb.monspecs.vfmax = 400; + fb->fb.monspecs.dclkmin = 1000000; + fb->fb.monspecs.dclkmax = 100000000; + + /* + * Make sure that the bitfields are set appropriately. + */ + clcdfb_set_bitfields(fb, &fb->fb.var); + + /* + * Allocate colourmap. + */ + ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0); + if (ret) + goto unmap; + + /* + * Ensure interrupts are disabled. + */ + writel(0, fb->regs + fb->off_ienb); + + fb_set_var(&fb->fb, &fb->fb.var); + + dev_info(&fb->dev->dev, "%s hardware, %s display\n", + fb->board->name, fb->panel->mode.name); + + ret = register_framebuffer(&fb->fb); + if (ret == 0) + goto out; + + printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); + + fb_dealloc_cmap(&fb->fb.cmap); + unmap: + iounmap(fb->regs); + clk_unprep: + clk_unprepare(fb->clk); + free_clk: + clk_put(fb->clk); + out: + return ret; +} + +#ifdef CONFIG_OF +static int clcdfb_of_get_dpi_panel_mode(struct device_node *node, + struct clcd_panel *clcd_panel) +{ + int err; + struct display_timing timing; + struct videomode video; + + err = of_get_display_timing(node, "panel-timing", &timing); + if (err) { + pr_err("%pOF: problems parsing panel-timing (%d)\n", node, err); + return err; + } + + videomode_from_timing(&timing, &video); + + err = fb_videomode_from_videomode(&video, &clcd_panel->mode); + if (err) + return err; + + /* Set up some inversion flags */ + if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + clcd_panel->tim2 |= TIM2_IPC; + else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)) + /* + * To preserve backwards compatibility, the IPC (inverted + * pixel clock) flag needs to be set on any display that + * doesn't explicitly specify that the pixel clock is + * active on the negative or positive edge. + */ + clcd_panel->tim2 |= TIM2_IPC; + + if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW) + clcd_panel->tim2 |= TIM2_IHS; + + if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW) + clcd_panel->tim2 |= TIM2_IVS; + + if (timing.flags & DISPLAY_FLAGS_DE_LOW) + clcd_panel->tim2 |= TIM2_IOE; + + return 0; +} + +static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode) +{ + return snprintf(buf, size, "%ux%u@%u", mode->xres, mode->yres, + mode->refresh); +} + +static int clcdfb_of_get_backlight(struct device *dev, + struct clcd_panel *clcd_panel) +{ + struct backlight_device *backlight; + + /* Look up the optional backlight device */ + backlight = devm_of_find_backlight(dev); + if (IS_ERR(backlight)) + return PTR_ERR(backlight); + + clcd_panel->backlight = backlight; + return 0; +} + +static int clcdfb_of_get_mode(struct device *dev, struct device_node *panel, + struct clcd_panel *clcd_panel) +{ + int err; + struct fb_videomode *mode; + char *name; + int len; + + /* Only directly connected DPI panels supported for now */ + if (of_device_is_compatible(panel, "panel-dpi")) + err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel); + else + err = -ENOENT; + if (err) + return err; + mode = &clcd_panel->mode; + + len = clcdfb_snprintf_mode(NULL, 0, mode); + name = devm_kzalloc(dev, len + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + clcdfb_snprintf_mode(name, len + 1, mode); + mode->name = name; + + return 0; +} + +static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) +{ + static struct { + unsigned int part; + u32 r0, g0, b0; + u32 caps; + } panels[] = { + { 0x110, 1, 7, 13, CLCD_CAP_5551 }, + { 0x110, 0, 8, 16, CLCD_CAP_888 }, + { 0x110, 16, 8, 0, CLCD_CAP_888 }, + { 0x111, 4, 14, 20, CLCD_CAP_444 }, + { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 }, + { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 | + CLCD_CAP_565 }, + { 0x111, 0, 8, 16, CLCD_CAP_444 | CLCD_CAP_5551 | + CLCD_CAP_565 | CLCD_CAP_888 }, + }; + int i; + + /* Bypass pixel clock divider */ + fb->panel->tim2 |= TIM2_BCD; + + /* TFT display, vert. comp. interrupt at the start of the back porch */ + fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1); + + fb->panel->caps = 0; + + /* Match the setup with known variants */ + for (i = 0; i < ARRAY_SIZE(panels) && !fb->panel->caps; i++) { + if (amba_part(fb->dev) != panels[i].part) + continue; + if (g0 != panels[i].g0) + continue; + if (r0 == panels[i].r0 && b0 == panels[i].b0) + fb->panel->caps = panels[i].caps; + } + + /* + * If we actually physically connected the R lines to B and + * vice versa + */ + if (r0 != 0 && b0 == 0) + fb->panel->bgr_connection = true; + + return fb->panel->caps ? 0 : -EINVAL; +} + +static int clcdfb_of_init_display(struct clcd_fb *fb) +{ + struct device_node *endpoint, *panel; + int err; + unsigned int bpp; + u32 max_bandwidth; + u32 tft_r0b0g0[3]; + + fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL); + if (!fb->panel) + return -ENOMEM; + + /* + * Fetch the panel endpoint. + */ + endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL); + if (!endpoint) + return -ENODEV; + + panel = of_graph_get_remote_port_parent(endpoint); + if (!panel) + return -ENODEV; + + err = clcdfb_of_get_backlight(&fb->dev->dev, fb->panel); + if (err) + return err; + + err = clcdfb_of_get_mode(&fb->dev->dev, panel, fb->panel); + if (err) + return err; + + err = of_property_read_u32(fb->dev->dev.of_node, "max-memory-bandwidth", + &max_bandwidth); + if (!err) { + /* + * max_bandwidth is in bytes per second and pixclock in + * pico-seconds, so the maximum allowed bits per pixel is + * 8 * max_bandwidth / (PICOS2KHZ(pixclock) * 1000) + * Rearrange this calculation to avoid overflow and then ensure + * result is a valid format. + */ + bpp = max_bandwidth / (1000 / 8) + / PICOS2KHZ(fb->panel->mode.pixclock); + bpp = rounddown_pow_of_two(bpp); + if (bpp > 32) + bpp = 32; + } else + bpp = 32; + fb->panel->bpp = bpp; + +#ifdef CONFIG_CPU_BIG_ENDIAN + fb->panel->cntl |= CNTL_BEBO; +#endif + fb->panel->width = -1; + fb->panel->height = -1; + + if (of_property_read_u32_array(endpoint, + "arm,pl11x,tft-r0g0b0-pads", + tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0) + return -ENOENT; + + return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0], + tft_r0b0g0[1], tft_r0b0g0[2]); +} + +static int clcdfb_of_vram_setup(struct clcd_fb *fb) +{ + int err; + struct device_node *memory; + u64 size; + + err = clcdfb_of_init_display(fb); + if (err) + return err; + + memory = of_parse_phandle(fb->dev->dev.of_node, "memory-region", 0); + if (!memory) + return -ENODEV; + + fb->fb.screen_base = of_iomap(memory, 0); + if (!fb->fb.screen_base) + return -ENOMEM; + + fb->fb.fix.smem_start = of_translate_address(memory, + of_get_address(memory, 0, &size, NULL)); + fb->fb.fix.smem_len = size; + + return 0; +} + +static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + unsigned long off, user_size, kernel_size; + + + off = vma->vm_pgoff << PAGE_SHIFT; + user_size = vma->vm_end - vma->vm_start; + kernel_size = fb->fb.fix.smem_len; + + if (off >= kernel_size || user_size > (kernel_size - off)) + return -ENXIO; + + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff, + user_size, + pgprot_writecombine(vma->vm_page_prot)); +} + +static void clcdfb_of_vram_remove(struct clcd_fb *fb) +{ + iounmap(fb->fb.screen_base); +} + +static int clcdfb_of_dma_setup(struct clcd_fb *fb) +{ + unsigned long framesize; + dma_addr_t dma; + int err; + + err = clcdfb_of_init_display(fb); + if (err) + return err; + + framesize = PAGE_ALIGN(fb->panel->mode.xres * fb->panel->mode.yres * + fb->panel->bpp / 8); + fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize, + &dma, GFP_KERNEL); + if (!fb->fb.screen_base) + return -ENOMEM; + + fb->fb.fix.smem_start = dma; + fb->fb.fix.smem_len = framesize; + + return 0; +} + +static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + return dma_mmap_wc(&fb->dev->dev, vma, fb->fb.screen_base, + fb->fb.fix.smem_start, fb->fb.fix.smem_len); +} + +static void clcdfb_of_dma_remove(struct clcd_fb *fb) +{ + dma_free_coherent(&fb->dev->dev, fb->fb.fix.smem_len, + fb->fb.screen_base, fb->fb.fix.smem_start); +} + +static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) +{ + struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board), + GFP_KERNEL); + struct device_node *node = dev->dev.of_node; + + if (!board) + return NULL; + + board->name = of_node_full_name(node); + board->caps = CLCD_CAP_ALL; + board->check = clcdfb_check; + board->decode = clcdfb_decode; + if (of_find_property(node, "memory-region", NULL)) { + board->setup = clcdfb_of_vram_setup; + board->mmap = clcdfb_of_vram_mmap; + board->remove = clcdfb_of_vram_remove; + } else { + board->setup = clcdfb_of_dma_setup; + board->mmap = clcdfb_of_dma_mmap; + board->remove = clcdfb_of_dma_remove; + } + + return board; +} +#else +static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) +{ + return NULL; +} +#endif + +static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct clcd_board *board = dev_get_platdata(&dev->dev); + struct clcd_fb *fb; + int ret; + + if (!board) + board = clcdfb_of_get_board(dev); + + if (!board) + return -EINVAL; + + ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + if (ret) + goto out; + + ret = amba_request_regions(dev, NULL); + if (ret) { + printk(KERN_ERR "CLCD: unable to reserve regs region\n"); + goto out; + } + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) { + ret = -ENOMEM; + goto free_region; + } + + fb->dev = dev; + fb->board = board; + + dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n", + amba_part(dev), amba_manf(dev), amba_rev(dev), + (unsigned long long)dev->res.start); + + ret = fb->board->setup(fb); + if (ret) + goto free_fb; + + ret = clcdfb_register(fb); + if (ret == 0) { + amba_set_drvdata(dev, fb); + goto out; + } + + fb->board->remove(fb); + free_fb: + kfree(fb); + free_region: + amba_release_regions(dev); + out: + return ret; +} + +static int clcdfb_remove(struct amba_device *dev) +{ + struct clcd_fb *fb = amba_get_drvdata(dev); + + clcdfb_disable(fb); + unregister_framebuffer(&fb->fb); + if (fb->fb.cmap.len) + fb_dealloc_cmap(&fb->fb.cmap); + iounmap(fb->regs); + clk_unprepare(fb->clk); + clk_put(fb->clk); + + fb->board->remove(fb); + + kfree(fb); + + amba_release_regions(dev); + + return 0; +} + +static const struct amba_id clcdfb_id_table[] = { + { + .id = 0x00041110, + .mask = 0x000ffffe, + }, + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, clcdfb_id_table); + +static struct amba_driver clcd_driver = { + .drv = { + .name = "clcd-pl11x", + }, + .probe = clcdfb_probe, + .remove = clcdfb_remove, + .id_table = clcdfb_id_table, +}; + +static int __init amba_clcdfb_init(void) +{ + if (fb_get_options("ambafb", NULL)) + return -ENODEV; + + return amba_driver_register(&clcd_driver); +} + +module_init(amba_clcdfb_init); + +static void __exit amba_clcdfb_exit(void) +{ + amba_driver_unregister(&clcd_driver); +} + +module_exit(amba_clcdfb_exit); + +MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index 4e6cbc24346d..9725ecd1255b 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -234,7 +234,7 @@ static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, } static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -247,15 +247,6 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index ae4de3bfd2d7..cef437817b0d 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -122,12 +122,6 @@ static int logo_lines; /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO enums. */ static int logo_shown = FBCON_LOGO_CANSHOW; -/* Software scrollback */ -static int fbcon_softback_size = 32768; -static unsigned long softback_buf, softback_curr; -static unsigned long softback_in; -static unsigned long softback_top, softback_end; -static int softback_lines; /* console mappings */ static int first_fb_vc; static int last_fb_vc = MAX_NR_CONSOLES - 1; @@ -167,12 +161,8 @@ static int margin_color; static const struct consw fb_con; -#define CM_SOFTBACK (8) - #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) -static int fbcon_set_origin(struct vc_data *); - static int fbcon_cursor_noblink; #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) @@ -373,18 +363,6 @@ static int get_color(struct vc_data *vc, struct fb_info *info, return color; } -static void fbcon_update_softback(struct vc_data *vc) -{ - int l = fbcon_softback_size / vc->vc_size_row; - - if (l > 5) - softback_end = softback_buf + l * vc->vc_size_row; - else - /* Smaller scrollback makes no sense, and 0 would screw - the operation totally */ - softback_top = 0; -} - static void fb_flashcursor(struct work_struct *work) { struct fb_info *info = container_of(work, struct fb_info, queue); @@ -414,7 +392,7 @@ static void fb_flashcursor(struct work_struct *work) c = scr_readw((u16 *) vc->vc_pos); mode = (!ops->cursor_flash || ops->cursor_state.enable) ? CM_ERASE : CM_DRAW; - ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), + ops->cursor(vc, info, mode, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); console_unlock(); } @@ -471,13 +449,7 @@ static int __init fb_console_setup(char *this_opt) } if (!strncmp(options, "scrollback:", 11)) { - options += 11; - if (*options) { - fbcon_softback_size = simple_strtoul(options, &options, 0); - if (*options == 'k' || *options == 'K') { - fbcon_softback_size *= 1024; - } - } + pr_warn("Ignoring scrollback size option\n"); continue; } @@ -1022,31 +994,6 @@ static const char *fbcon_startup(void) set_blitting_type(vc, info); - if (info->fix.type != FB_TYPE_TEXT) { - if (fbcon_softback_size) { - if (!softback_buf) { - softback_buf = - (unsigned long) - kvmalloc(fbcon_softback_size, - GFP_KERNEL); - if (!softback_buf) { - fbcon_softback_size = 0; - softback_top = 0; - } - } - } else { - if (softback_buf) { - kvfree((void *) softback_buf); - softback_buf = 0; - softback_top = 0; - } - } - if (softback_buf) - softback_in = softback_top = softback_curr = - softback_buf; - softback_lines = 0; - } - /* Setup default font */ if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) @@ -1220,9 +1167,6 @@ static void fbcon_init(struct vc_data *vc, int init) if (logo) fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); - if (vc == svc && softback_buf) - fbcon_update_softback(vc); - if (ops->rotate_font && ops->rotate_font(info, vc)) { ops->rotate = FB_ROTATE_UR; set_blitting_type(vc, info); @@ -1385,7 +1329,6 @@ static void fbcon_cursor(struct vc_data *vc, int mode) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct fbcon_ops *ops = info->fbcon_par; - int y; int c = scr_readw((u16 *) vc->vc_pos); ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); @@ -1399,16 +1342,8 @@ static void fbcon_cursor(struct vc_data *vc, int mode) fbcon_add_cursor_timer(info); ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; - if (mode & CM_SOFTBACK) { - mode &= ~CM_SOFTBACK; - y = softback_lines; - } else { - if (softback_lines) - fbcon_set_origin(vc); - y = 0; - } - ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), + ops->cursor(vc, info, mode, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); } @@ -1479,8 +1414,6 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, if (con_is_visible(vc)) { update_screen(vc); - if (softback_buf) - fbcon_update_softback(vc); } } @@ -1618,99 +1551,6 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) scrollback_current = 0; } -static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p, - long delta) -{ - int count = vc->vc_rows; - unsigned short *d, *s; - unsigned long n; - int line = 0; - - d = (u16 *) softback_curr; - if (d == (u16 *) softback_in) - d = (u16 *) vc->vc_origin; - n = softback_curr + delta * vc->vc_size_row; - softback_lines -= delta; - if (delta < 0) { - if (softback_curr < softback_top && n < softback_buf) { - n += softback_end - softback_buf; - if (n < softback_top) { - softback_lines -= - (softback_top - n) / vc->vc_size_row; - n = softback_top; - } - } else if (softback_curr >= softback_top - && n < softback_top) { - softback_lines -= - (softback_top - n) / vc->vc_size_row; - n = softback_top; - } - } else { - if (softback_curr > softback_in && n >= softback_end) { - n += softback_buf - softback_end; - if (n > softback_in) { - n = softback_in; - softback_lines = 0; - } - } else if (softback_curr <= softback_in && n > softback_in) { - n = softback_in; - softback_lines = 0; - } - } - if (n == softback_curr) - return; - softback_curr = n; - s = (u16 *) softback_curr; - if (s == (u16 *) softback_in) - s = (u16 *) vc->vc_origin; - while (count--) { - unsigned short *start; - unsigned short *le; - unsigned short c; - int x = 0; - unsigned short attr = 1; - - start = s; - le = advance_row(s, 1); - do { - c = scr_readw(s); - if (attr != (c & 0xff00)) { - attr = c & 0xff00; - if (s > start) { - fbcon_putcs(vc, start, s - start, - line, x); - x += s - start; - start = s; - } - } - if (c == scr_readw(d)) { - if (s > start) { - fbcon_putcs(vc, start, s - start, - line, x); - x += s - start + 1; - start = s + 1; - } else { - x++; - start++; - } - } - s++; - d++; - } while (s < le); - if (s > start) - fbcon_putcs(vc, start, s - start, line, x); - line++; - if (d == (u16 *) softback_end) - d = (u16 *) softback_buf; - if (d == (u16 *) softback_in) - d = (u16 *) vc->vc_origin; - if (s == (u16 *) softback_end) - s = (u16 *) softback_buf; - if (s == (u16 *) softback_in) - s = (u16 *) vc->vc_origin; - } -} - static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, int line, int count, int dy) { @@ -1850,31 +1690,6 @@ static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, } } -static inline void fbcon_softback_note(struct vc_data *vc, int t, - int count) -{ - unsigned short *p; - - if (vc->vc_num != fg_console) - return; - p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); - - while (count) { - scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row); - count--; - p = advance_row(p, 1); - softback_in += vc->vc_size_row; - if (softback_in == softback_end) - softback_in = softback_buf; - if (softback_in == softback_top) { - softback_top += vc->vc_size_row; - if (softback_top == softback_end) - softback_top = softback_buf; - } - } - softback_curr = softback_in; -} - static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int count) { @@ -1897,8 +1712,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; - if (softback_top) - fbcon_softback_note(vc, t, count); if (logo_shown >= 0) goto redraw_up; switch (p->scrollmode) { @@ -2201,7 +2014,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, struct fb_var_screeninfo var = info->var; int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh; - if (ops->p && ops->p->userfont && FNTSIZE(vc->vc_font.data)) { + if (p->userfont && FNTSIZE(vc->vc_font.data)) { int size; int pitch = PITCH(vc->vc_font.width); @@ -2267,14 +2080,6 @@ static int fbcon_switch(struct vc_data *vc) info = registered_fb[con2fb_map[vc->vc_num]]; ops = info->fbcon_par; - if (softback_top) { - if (softback_lines) - fbcon_set_origin(vc); - softback_top = softback_curr = softback_in = softback_buf; - softback_lines = 0; - fbcon_update_softback(vc); - } - if (logo_shown >= 0) { struct vc_data *conp2 = vc_cons[logo_shown].d; @@ -2490,6 +2295,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) if (font->width <= 8) { j = vc->vc_font.height; + if (font->charcount * j > FNTSIZE(fontdata)) + return -EINVAL; + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 32 - j); @@ -2498,6 +2306,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) } } else if (font->width <= 16) { j = vc->vc_font.height * 2; + if (font->charcount * j > FNTSIZE(fontdata)) + return -EINVAL; + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 64 - j); @@ -2505,6 +2316,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) fontdata += j; } } else if (font->width <= 24) { + if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata)) + return -EINVAL; + for (i = 0; i < font->charcount; i++) { for (j = 0; j < vc->vc_font.height; j++) { *data++ = fontdata[0]; @@ -2517,6 +2331,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) } } else { j = vc->vc_font.height * 4; + if (font->charcount * j > FNTSIZE(fontdata)) + return -EINVAL; + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 128 - j); @@ -2598,9 +2415,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int cnt; char *old_data = NULL; - if (con_is_visible(vc) && softback_lines) - fbcon_set_origin(vc); - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); if (p->userfont) old_data = vc->vc_font.data; @@ -2626,8 +2440,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, cols /= w; rows /= h; vc_resize(vc, cols, rows); - if (con_is_visible(vc) && softback_buf) - fbcon_update_softback(vc); } else if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) { fbcon_clear_margins(vc, 0); @@ -2784,21 +2596,9 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) fb_set_cmap(&palette_cmap, info); } -static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) +static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset) { - unsigned long p; - int line; - - if (vc->vc_num != fg_console || !softback_lines) - return (u16 *) (vc->vc_origin + offset); - line = offset / vc->vc_size_row; - if (line >= softback_lines) - return (u16 *) (vc->vc_origin + offset - - softback_lines * vc->vc_size_row); - p = softback_curr + offset; - if (p >= softback_end) - p += softback_buf - softback_end; - return (u16 *) p; + return (u16 *) (vc->vc_origin + offset); } static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, @@ -2812,22 +2612,7 @@ static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, x = offset % vc->vc_cols; y = offset / vc->vc_cols; - if (vc->vc_num == fg_console) - y += softback_lines; ret = pos + (vc->vc_cols - x) * 2; - } else if (vc->vc_num == fg_console && softback_lines) { - unsigned long offset = pos - softback_curr; - - if (pos < softback_curr) - offset += softback_end - softback_buf; - offset /= 2; - x = offset % vc->vc_cols; - y = offset / vc->vc_cols; - ret = pos + (vc->vc_cols - x) * 2; - if (ret == softback_end) - ret = softback_buf; - if (ret == softback_in) - ret = vc->vc_origin; } else { /* Should not happen */ x = y = 0; @@ -2855,107 +2640,7 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); scr_writew(a, p++); - if (p == (u16 *) softback_end) - p = (u16 *) softback_buf; - if (p == (u16 *) softback_in) - p = (u16 *) vc->vc_origin; - } -} - -static void fbcon_scrolldelta(struct vc_data *vc, int lines) -{ - struct fb_info *info = registered_fb[con2fb_map[fg_console]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *disp = &fb_display[fg_console]; - int offset, limit, scrollback_old; - - if (softback_top) { - if (vc->vc_num != fg_console) - return; - if (vc->vc_mode != KD_TEXT || !lines) - return; - if (logo_shown >= 0) { - struct vc_data *conp2 = vc_cons[logo_shown].d; - - if (conp2->vc_top == logo_lines - && conp2->vc_bottom == conp2->vc_rows) - conp2->vc_top = 0; - if (logo_shown == vc->vc_num) { - unsigned long p, q; - int i; - - p = softback_in; - q = vc->vc_origin + - logo_lines * vc->vc_size_row; - for (i = 0; i < logo_lines; i++) { - if (p == softback_top) - break; - if (p == softback_buf) - p = softback_end; - p -= vc->vc_size_row; - q -= vc->vc_size_row; - scr_memcpyw((u16 *) q, (u16 *) p, - vc->vc_size_row); - } - softback_in = softback_curr = p; - update_region(vc, vc->vc_origin, - logo_lines * vc->vc_cols); - } - logo_shown = FBCON_LOGO_CANSHOW; - } - fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); - fbcon_redraw_softback(vc, disp, lines); - fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); - return; - } - - if (!scrollback_phys_max) - return; - - scrollback_old = scrollback_current; - scrollback_current -= lines; - if (scrollback_current < 0) - scrollback_current = 0; - else if (scrollback_current > scrollback_max) - scrollback_current = scrollback_max; - if (scrollback_current == scrollback_old) - return; - - if (fbcon_is_inactive(vc, info)) - return; - - fbcon_cursor(vc, CM_ERASE); - - offset = disp->yscroll - scrollback_current; - limit = disp->vrows; - switch (disp->scrollmode) { - case SCROLL_WRAP_MOVE: - info->var.vmode |= FB_VMODE_YWRAP; - break; - case SCROLL_PAN_MOVE: - case SCROLL_PAN_REDRAW: - limit -= vc->vc_rows; - info->var.vmode &= ~FB_VMODE_YWRAP; - break; } - if (offset < 0) - offset += limit; - else if (offset >= limit) - offset -= limit; - - ops->var.xoffset = 0; - ops->var.yoffset = offset * vc->vc_font.height; - ops->update_start(info); - - if (!scrollback_current) - fbcon_cursor(vc, CM_DRAW); -} - -static int fbcon_set_origin(struct vc_data *vc) -{ - if (softback_lines) - fbcon_scrolldelta(vc, softback_lines); - return 0; } void fbcon_suspended(struct fb_info *info) @@ -3018,8 +2703,6 @@ static void fbcon_modechanged(struct fb_info *info) fbcon_set_palette(vc, color_table); update_screen(vc); - if (softback_buf) - fbcon_update_softback(vc); } } @@ -3430,8 +3113,6 @@ static const struct consw fb_con = { .con_font_default = fbcon_set_def_font, .con_font_copy = fbcon_copy_font, .con_set_palette = fbcon_set_palette, - .con_scrolldelta = fbcon_scrolldelta, - .con_set_origin = fbcon_set_origin, .con_invert_region = fbcon_invert_region, .con_screen_pos = fbcon_screen_pos, .con_getxy = fbcon_getxy, @@ -3665,9 +3346,6 @@ static void fbcon_exit(void) } #endif - kvfree((void *)softback_buf); - softback_buf = 0UL; - for_each_registered_fb(i) { int pending = 0; diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 20dea853765f..9315b360c898 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -62,7 +62,7 @@ struct fbcon_ops { void (*clear_margins)(struct vc_data *vc, struct fb_info *info, int color, int bottom_only); void (*cursor)(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg); + int fg, int bg); int (*update_start)(struct fb_info *info); int (*rotate_font)(struct fb_info *info, struct vc_data *vc); struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ @@ -152,13 +152,6 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, #define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) #define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) -/* Font */ -#define REFCOUNT(fd) (((int *)(fd))[-1]) -#define FNTSIZE(fd) (((int *)(fd))[-2]) -#define FNTCHARCNT(fd) (((int *)(fd))[-3]) -#define FNTSUM(fd) (((int *)(fd))[-4]) -#define FONT_EXTRA_WORDS 4 - /* * Scroll Method */ diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c index 5b177131e062..bbd869efd03b 100644 --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -219,7 +219,7 @@ static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info, } static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -236,15 +236,6 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c index 894d01a62f30..a34cbe8e9874 100644 --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -202,7 +202,7 @@ static void cw_clear_margins(struct vc_data *vc, struct fb_info *info, } static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -219,15 +219,6 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); diff --git a/drivers/video/fbdev/core/fbcon_rotate.c b/drivers/video/fbdev/core/fbcon_rotate.c index c0d445294aa7..ac72d4f85f7d 100644 --- a/drivers/video/fbdev/core/fbcon_rotate.c +++ b/drivers/video/fbdev/core/fbcon_rotate.c @@ -14,6 +14,7 @@ #include <linux/fb.h> #include <linux/vt_kern.h> #include <linux/console.h> +#include <linux/font.h> #include <asm/types.h> #include "fbcon.h" #include "fbcon_rotate.h" diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c index 01b87f278d79..199cbc7abe35 100644 --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -249,7 +249,7 @@ static void ud_clear_margins(struct vc_data *vc, struct fb_info *info, } static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -267,15 +267,6 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height)); diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c index 1dfaff0881fb..628fe5e010c0 100644 --- a/drivers/video/fbdev/core/tileblit.c +++ b/drivers/video/fbdev/core/tileblit.c @@ -13,6 +13,7 @@ #include <linux/fb.h> #include <linux/vt_kern.h> #include <linux/console.h> +#include <linux/font.h> #include <asm/types.h> #include "fbcon.h" @@ -80,7 +81,7 @@ static void tile_clear_margins(struct vc_data *vc, struct fb_info *info, } static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_tilecursor cursor; int use_sw = vc->vc_cursor_type & CUR_SW; diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 02411d89cb46..e36fb1a0ecdb 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -1114,8 +1114,15 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) getmem_done: remove_conflicting_framebuffers(info->apertures, KBUILD_MODNAME, false); - if (!gen2vm) + + if (gen2vm) { + /* framebuffer is reallocated, clear screen_info to avoid misuse from kexec */ + screen_info.lfb_size = 0; + screen_info.lfb_base = 0; + screen_info.orig_video_isVGA = 0; + } else { pci_dev_put(pdev); + } kfree(info->apertures); return 0; diff --git a/drivers/video/fbdev/s3c2410fb-regs-lcd.h b/drivers/video/fbdev/s3c2410fb-regs-lcd.h new file mode 100644 index 000000000000..1e46f7a788e5 --- /dev/null +++ b/drivers/video/fbdev/s3c2410fb-regs-lcd.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk> + * http://www.simtec.co.uk/products/SWLINUX/ + */ + +#ifndef ___ASM_ARCH_REGS_LCD_H +#define ___ASM_ARCH_REGS_LCD_H + +/* + * a couple of values are used as platform data in + * include/linux/platform_data/fb-s3c2410.h and not + * duplicated here. + */ +#include <linux/platform_data/fb-s3c2410.h> + +#define S3C2410_LCDREG(x) (x) + +/* LCD control registers */ +#define S3C2410_LCDCON1 S3C2410_LCDREG(0x00) +#define S3C2410_LCDCON2 S3C2410_LCDREG(0x04) +#define S3C2410_LCDCON3 S3C2410_LCDREG(0x08) +#define S3C2410_LCDCON4 S3C2410_LCDREG(0x0C) +#define S3C2410_LCDCON5 S3C2410_LCDREG(0x10) + +#define S3C2410_LCDCON1_CLKVAL(x) ((x) << 8) +#define S3C2410_LCDCON1_MMODE (1<<7) +#define S3C2410_LCDCON1_DSCAN4 (0<<5) +#define S3C2410_LCDCON1_STN4 (1<<5) +#define S3C2410_LCDCON1_STN8 (2<<5) +#define S3C2410_LCDCON1_TFT (3<<5) + +#define S3C2410_LCDCON1_STN1BPP (0<<1) +#define S3C2410_LCDCON1_STN2GREY (1<<1) +#define S3C2410_LCDCON1_STN4GREY (2<<1) +#define S3C2410_LCDCON1_STN8BPP (3<<1) +#define S3C2410_LCDCON1_STN12BPP (4<<1) + +#define S3C2410_LCDCON1_ENVID (1) + +#define S3C2410_LCDCON1_MODEMASK 0x1E + +#define S3C2410_LCDCON2_VBPD(x) ((x) << 24) +#define S3C2410_LCDCON2_LINEVAL(x) ((x) << 14) +#define S3C2410_LCDCON2_VFPD(x) ((x) << 6) +#define S3C2410_LCDCON2_VSPW(x) ((x) << 0) + +#define S3C2410_LCDCON2_GET_VBPD(x) ( ((x) >> 24) & 0xFF) +#define S3C2410_LCDCON2_GET_VFPD(x) ( ((x) >> 6) & 0xFF) +#define S3C2410_LCDCON2_GET_VSPW(x) ( ((x) >> 0) & 0x3F) + +#define S3C2410_LCDCON3_HBPD(x) ((x) << 19) +#define S3C2410_LCDCON3_WDLY(x) ((x) << 19) +#define S3C2410_LCDCON3_HOZVAL(x) ((x) << 8) +#define S3C2410_LCDCON3_HFPD(x) ((x) << 0) +#define S3C2410_LCDCON3_LINEBLANK(x)((x) << 0) + +#define S3C2410_LCDCON3_GET_HBPD(x) ( ((x) >> 19) & 0x7F) +#define S3C2410_LCDCON3_GET_HFPD(x) ( ((x) >> 0) & 0xFF) + +/* LDCCON4 changes for STN mode on the S3C2412 */ + +#define S3C2410_LCDCON4_MVAL(x) ((x) << 8) +#define S3C2410_LCDCON4_HSPW(x) ((x) << 0) +#define S3C2410_LCDCON4_WLH(x) ((x) << 0) + +#define S3C2410_LCDCON4_GET_HSPW(x) ( ((x) >> 0) & 0xFF) + +/* framebuffer start addressed */ +#define S3C2410_LCDSADDR1 S3C2410_LCDREG(0x14) +#define S3C2410_LCDSADDR2 S3C2410_LCDREG(0x18) +#define S3C2410_LCDSADDR3 S3C2410_LCDREG(0x1C) + +#define S3C2410_LCDBANK(x) ((x) << 21) +#define S3C2410_LCDBASEU(x) (x) + +#define S3C2410_OFFSIZE(x) ((x) << 11) +#define S3C2410_PAGEWIDTH(x) (x) + +/* colour lookup and miscellaneous controls */ + +#define S3C2410_REDLUT S3C2410_LCDREG(0x20) +#define S3C2410_GREENLUT S3C2410_LCDREG(0x24) +#define S3C2410_BLUELUT S3C2410_LCDREG(0x28) + +#define S3C2410_DITHMODE S3C2410_LCDREG(0x4C) +#define S3C2410_TPAL S3C2410_LCDREG(0x50) + +#define S3C2410_TPAL_EN (1<<24) + +/* interrupt info */ +#define S3C2410_LCDINTPND S3C2410_LCDREG(0x54) +#define S3C2410_LCDSRCPND S3C2410_LCDREG(0x58) +#define S3C2410_LCDINTMSK S3C2410_LCDREG(0x5C) +#define S3C2410_LCDINT_FIWSEL (1<<2) +#define S3C2410_LCDINT_FRSYNC (1<<1) +#define S3C2410_LCDINT_FICNT (1<<0) + +/* s3c2442 extra stn registers */ + +#define S3C2442_REDLUT S3C2410_LCDREG(0x20) +#define S3C2442_GREENLUT S3C2410_LCDREG(0x24) +#define S3C2442_BLUELUT S3C2410_LCDREG(0x28) +#define S3C2442_DITHMODE S3C2410_LCDREG(0x20) + +#define S3C2410_LPCSEL S3C2410_LCDREG(0x60) + +#define S3C2410_TFTPAL(x) S3C2410_LCDREG((0x400 + (x)*4)) + +/* S3C2412 registers */ + +#define S3C2412_TPAL S3C2410_LCDREG(0x20) + +#define S3C2412_LCDINTPND S3C2410_LCDREG(0x24) +#define S3C2412_LCDSRCPND S3C2410_LCDREG(0x28) +#define S3C2412_LCDINTMSK S3C2410_LCDREG(0x2C) + +#define S3C2412_TCONSEL S3C2410_LCDREG(0x30) + +#define S3C2412_LCDCON6 S3C2410_LCDREG(0x34) +#define S3C2412_LCDCON7 S3C2410_LCDREG(0x38) +#define S3C2412_LCDCON8 S3C2410_LCDREG(0x3C) +#define S3C2412_LCDCON9 S3C2410_LCDREG(0x40) + +#define S3C2412_REDLUT(x) S3C2410_LCDREG(0x44 + ((x)*4)) +#define S3C2412_GREENLUT(x) S3C2410_LCDREG(0x60 + ((x)*4)) +#define S3C2412_BLUELUT(x) S3C2410_LCDREG(0x98 + ((x)*4)) + +#define S3C2412_FRCPAT(x) S3C2410_LCDREG(0xB4 + ((x)*4)) + +/* general registers */ + +/* base of the LCD registers, where INTPND, INTSRC and then INTMSK + * are available. */ + +#define S3C2410_LCDINTBASE S3C2410_LCDREG(0x54) +#define S3C2412_LCDINTBASE S3C2410_LCDREG(0x24) + +#define S3C24XX_LCDINTPND (0x00) +#define S3C24XX_LCDSRCPND (0x04) +#define S3C24XX_LCDINTMSK (0x08) + +#endif /* ___ASM_ARCH_REGS_LCD_H */ diff --git a/drivers/video/fbdev/s3c2410fb.c b/drivers/video/fbdev/s3c2410fb.c index 6f8fa501583f..d8ae5258de46 100644 --- a/drivers/video/fbdev/s3c2410fb.c +++ b/drivers/video/fbdev/s3c2410fb.c @@ -29,19 +29,18 @@ #include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/io.h> +#include <linux/platform_data/fb-s3c2410.h> #include <asm/div64.h> #include <asm/mach/map.h> -#include <mach/regs-lcd.h> -#include <mach/regs-gpio.h> -#include <mach/fb.h> #ifdef CONFIG_PM #include <linux/pm.h> #endif #include "s3c2410fb.h" +#include "s3c2410fb-regs-lcd.h" /* Debugging stuff */ static int debug = IS_BUILTIN(CONFIG_FB_S3C2410_DEBUG); @@ -672,6 +671,9 @@ static inline void modify_gpio(void __iomem *reg, { unsigned long tmp; + if (!reg) + return; + tmp = readl(reg) & ~mask; writel(tmp | set, reg); } @@ -702,10 +704,10 @@ static int s3c2410fb_init_registers(struct fb_info *info) /* modify the gpio(s) with interrupts set (bjd) */ - modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask); - modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask); - modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask); - modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask); + modify_gpio(mach_info->gpcup_reg, mach_info->gpcup, mach_info->gpcup_mask); + modify_gpio(mach_info->gpccon_reg, mach_info->gpccon, mach_info->gpccon_mask); + modify_gpio(mach_info->gpdup_reg, mach_info->gpdup, mach_info->gpdup_mask); + modify_gpio(mach_info->gpdcon_reg, mach_info->gpdcon, mach_info->gpdcon_mask); local_irq_restore(flags); diff --git a/drivers/video/fbdev/sticore.h b/drivers/video/fbdev/sticore.h index fb8f58f9867a..c338f7848ae2 100644 --- a/drivers/video/fbdev/sticore.h +++ b/drivers/video/fbdev/sticore.h @@ -4,12 +4,6 @@ /* generic STI structures & functions */ -#if 0 -#define DPRINTK(x) printk x -#else -#define DPRINTK(x) -#endif - #define MAX_STI_ROMS 4 /* max no. of ROMs which this driver handles */ #define STI_REGION_MAX 8 /* hardcoded STI constants */ @@ -246,8 +240,12 @@ struct sti_rom_font { /* sticore internal font handling */ struct sti_cooked_font { - struct sti_rom_font *raw; + struct sti_rom_font *raw; /* native ptr for STI functions */ + void *raw_ptr; /* kmalloc'ed font data */ struct sti_cooked_font *next_font; + int height, width; + int refcount; + u32 crc; }; struct sti_cooked_rom { @@ -341,9 +339,6 @@ struct sti_all_data { struct sti_struct { spinlock_t lock; - /* the following fields needs to be filled in by the word/byte routines */ - int font_width; - int font_height; /* char **mon_strings; */ int sti_mem_request; u32 graphics_id[2]; @@ -362,6 +357,7 @@ struct sti_struct { struct sti_glob_cfg *glob_cfg; /* points into sti_all_data */ + int wordmode; struct sti_cooked_font *font; /* ptr to selected font (cooked) */ struct pci_dev *pd; @@ -380,6 +376,7 @@ struct sti_struct { /* sticore interface functions */ struct sti_struct *sti_get_rom(unsigned int index); /* 0: default sti */ +void sti_font_convert_bytemode(struct sti_struct *sti, struct sti_cooked_font *f); /* sticore main function to call STI firmware */ @@ -391,12 +388,14 @@ int sti_call(const struct sti_struct *sti, unsigned long func, /* functions to call the STI ROM directly */ -void sti_putc(struct sti_struct *sti, int c, int y, int x); +void sti_putc(struct sti_struct *sti, int c, int y, int x, + struct sti_cooked_font *font); void sti_set(struct sti_struct *sti, int src_y, int src_x, - int height, int width, u8 color); + int height, int width, u8 color); void sti_clear(struct sti_struct *sti, int src_y, int src_x, - int height, int width, int c); + int height, int width, int c, struct sti_cooked_font *font); void sti_bmove(struct sti_struct *sti, int src_y, int src_x, - int dst_y, int dst_x, int height, int width); + int dst_y, int dst_x, int height, int width, + struct sti_cooked_font *font); #endif /* STICORE_H */ |