summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2023-12-12 15:18:02 +0100
committerHelge Deller <deller@gmx.de>2024-01-12 12:38:37 +0100
commitdee56ccb468a832074397fdbf22bbd9bf6d710aa (patch)
treef23fe590fc0bb95e3dc21f91e54ec3ba781cba65 /drivers/video
parentd9f25b59ed85ae45801cf45fe17eb269b0ef3038 (diff)
downloadlinux-dee56ccb468a832074397fdbf22bbd9bf6d710aa.tar.gz
linux-dee56ccb468a832074397fdbf22bbd9bf6d710aa.tar.bz2
linux-dee56ccb468a832074397fdbf22bbd9bf6d710aa.zip
fbdev: amba-clcd: Delete the old CLCD driver
We have managed to ascertain that all users of the old FBDEV code that are out of tree are now gone. The new DRM driver can be found in drivers/gpu/drm/pl111/. The remaining out of tree user was the ARM FVP emulation platform, running Android. Thanks to changes in Android versions 13 and 14, Android can now use the DRM driver when being emulated under FVP. Some final patches are being put in place to make it fully featured. This is essentially a revert of the partial revert in commit 112c35237c72 ("Partially revert "video: fbdev: amba-clcd: Retire elder CLCD driver"") Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/fbdev/Kconfig18
-rw-r--r--drivers/video/fbdev/Makefile1
-rw-r--r--drivers/video/fbdev/amba-clcd.c984
3 files changed, 0 insertions, 1003 deletions
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index b65f20006bd7..3cdd2d50f59b 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -116,24 +116,6 @@ 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_IOMEM_HELPERS
- 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 7cf7b0af1465..3eecd51267fa 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -74,7 +74,6 @@ 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
deleted file mode 100644
index 0399db369e70..000000000000
--- a/drivers/video/fbdev/amba-clcd.c
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * 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";
-
-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);
-
- msleep(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);
-
- msleep(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, &regs);
-
- 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_DEFAULT_IOMEM_OPS_RDWR,
- .fb_check_var = clcdfb_check_var,
- .fb_set_par = clcdfb_set_par,
- .fb_setcolreg = clcdfb_setcolreg,
- .fb_blank = clcdfb_blank,
- __FB_DEFAULT_IOMEM_OPS_DRAW,
- .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.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) {
- err = -ENODEV;
- goto out_endpoint_put;
- }
-
- err = clcdfb_of_get_backlight(&fb->dev->dev, fb->panel);
- if (err)
- goto out_panel_put;
-
- err = clcdfb_of_get_mode(&fb->dev->dev, panel, fb->panel);
- if (err)
- goto out_panel_put;
-
- 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) {
- err = -ENOENT;
- goto out_panel_put;
- }
-
- of_node_put(panel);
- of_node_put(endpoint);
-
- return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
- tft_r0b0g0[1], tft_r0b0g0[2]);
-out_panel_put:
- of_node_put(panel);
-out_endpoint_put:
- of_node_put(endpoint);
- return err;
-}
-
-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) {
- of_node_put(memory);
- return -ENOMEM;
- }
-
- fb->fb.fix.smem_start = of_translate_address(memory,
- of_get_address(memory, 0, &size, NULL));
- fb->fb.fix.smem_len = size;
- of_node_put(memory);
-
- 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_property_present(node, "memory-region")) {
- 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 void 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);
-}
-
-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");