diff options
Diffstat (limited to 'drivers/video/xilinxfb.c')
-rw-r--r-- | drivers/video/xilinxfb.c | 353 |
1 files changed, 252 insertions, 101 deletions
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 6ef9733a18d4..6ef99b2d13ca 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -6,9 +6,12 @@ * Author: MontaVista Software, Inc. * source@mvista.com * - * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is licensed - * "as is" without any warranty of any kind, whether express or implied. + * 2002-2007 (c) MontaVista Software, Inc. + * 2007 (c) Secret Lab Technologies, Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. */ /* @@ -18,6 +21,7 @@ * Geert Uytterhoeven. */ +#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> @@ -28,9 +32,12 @@ #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> - +#if defined(CONFIG_OF) +#include <linux/of_device.h> +#include <linux/of_platform.h> +#endif #include <asm/io.h> -#include <syslib/virtex_devices.h> +#include <linux/xilinxfb.h> #define DRIVER_NAME "xilinxfb" #define DRIVER_DESCRIPTION "Xilinx TFT LCD frame buffer driver" @@ -63,12 +70,6 @@ */ #define BYTES_PER_PIXEL 4 #define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8) -#define XRES 640 -#define YRES 480 -#define XRES_VIRTUAL 1024 -#define YRES_VIRTUAL YRES -#define LINE_LENGTH (XRES_VIRTUAL * BYTES_PER_PIXEL) -#define FB_SIZE (YRES_VIRTUAL * LINE_LENGTH) #define RED_SHIFT 16 #define GREEN_SHIFT 8 @@ -77,23 +78,26 @@ #define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */ /* + * Default xilinxfb configuration + */ +static struct xilinxfb_platform_data xilinx_fb_default_pdata = { + .xres = 640, + .yres = 480, + .xvirt = 1024, + .yvirt = 480; +}; + +/* * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures */ static struct fb_fix_screeninfo xilinx_fb_fix = { .id = "Xilinx", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, - .smem_len = FB_SIZE, - .line_length = LINE_LENGTH, .accel = FB_ACCEL_NONE }; static struct fb_var_screeninfo xilinx_fb_var = { - .xres = XRES, - .yres = YRES, - .xres_virtual = XRES_VIRTUAL, - .yres_virtual = YRES_VIRTUAL, - .bits_per_pixel = BITS_PER_PIXEL, .red = { RED_SHIFT, 8, 0 }, @@ -111,8 +115,9 @@ struct xilinxfb_drvdata { u32 regs_phys; /* phys. address of the control registers */ u32 __iomem *regs; /* virt. address of the control registers */ - unsigned char __iomem *fb_virt; /* virt. address of the frame buffer */ + void *fb_virt; /* virt. address of the frame buffer */ dma_addr_t fb_phys; /* phys. address of the frame buffer */ + int fb_alloced; /* Flag, was the fb memory alloced? */ u32 reg_ctrl_default; @@ -195,130 +200,136 @@ static struct fb_ops xilinxfb_ops = .fb_imageblit = cfb_imageblit, }; -/* === The device driver === */ +/* --------------------------------------------------------------------- + * Bus independent setup/teardown + */ -static int -xilinxfb_drv_probe(struct device *dev) +static int xilinxfb_assign(struct device *dev, unsigned long physaddr, + struct xilinxfb_platform_data *pdata) { - struct platform_device *pdev; - struct xilinxfb_platform_data *pdata; struct xilinxfb_drvdata *drvdata; - struct resource *regs_res; - int retval; - - if (!dev) - return -EINVAL; - - pdev = to_platform_device(dev); - pdata = pdev->dev.platform_data; + int rc; + int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL; + /* Allocate the driver data region */ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); if (!drvdata) { - printk(KERN_ERR "Couldn't allocate device private record\n"); + dev_err(dev, "Couldn't allocate device private record\n"); return -ENOMEM; } dev_set_drvdata(dev, drvdata); /* Map the control registers in */ - regs_res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!regs_res || (regs_res->end - regs_res->start + 1 < 8)) { - printk(KERN_ERR "Couldn't get registers resource\n"); - retval = -EFAULT; - goto failed1; + if (!request_mem_region(physaddr, 8, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", + physaddr); + rc = -ENODEV; + goto err_region; } - - if (!request_mem_region(regs_res->start, 8, DRIVER_NAME)) { - printk(KERN_ERR - "Couldn't lock memory region at 0x%08X\n", - regs_res->start); - retval = -EBUSY; - goto failed1; + drvdata->regs_phys = physaddr; + drvdata->regs = ioremap(physaddr, 8); + if (!drvdata->regs) { + dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", + physaddr); + rc = -ENODEV; + goto err_map; } - drvdata->regs = (u32 __iomem*) ioremap(regs_res->start, 8); - drvdata->regs_phys = regs_res->start; /* Allocate the framebuffer memory */ - drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(FB_SIZE), - &drvdata->fb_phys, GFP_KERNEL); + if (pdata->fb_phys) { + drvdata->fb_phys = pdata->fb_phys; + drvdata->fb_virt = ioremap(pdata->fb_phys, fbsize); + } else { + drvdata->fb_alloced = 1; + drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(fbsize), + &drvdata->fb_phys, GFP_KERNEL); + } + if (!drvdata->fb_virt) { - printk(KERN_ERR "Could not allocate frame buffer memory\n"); - retval = -ENOMEM; - goto failed2; + dev_err(dev, "Could not allocate frame buffer memory\n"); + rc = -ENOMEM; + goto err_fbmem; } /* Clear (turn to black) the framebuffer */ - memset_io((void *) drvdata->fb_virt, 0, FB_SIZE); + memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize); /* Tell the hardware where the frame buffer is */ xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys); /* Turn on the display */ drvdata->reg_ctrl_default = REG_CTRL_ENABLE; - if (pdata && pdata->rotate_screen) + if (pdata->rotate_screen) drvdata->reg_ctrl_default |= REG_CTRL_ROTATE; xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default); /* Fill struct fb_info */ drvdata->info.device = dev; - drvdata->info.screen_base = drvdata->fb_virt; + drvdata->info.screen_base = (void __iomem *)drvdata->fb_virt; drvdata->info.fbops = &xilinxfb_ops; drvdata->info.fix = xilinx_fb_fix; drvdata->info.fix.smem_start = drvdata->fb_phys; - drvdata->info.pseudo_palette = drvdata->pseudo_palette; - - if (fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0) < 0) { - printk(KERN_ERR "Fail to allocate colormap (%d entries)\n", - PALETTE_ENTRIES_NO); - retval = -EFAULT; - goto failed3; - } + drvdata->info.fix.smem_len = fbsize; + drvdata->info.fix.line_length = pdata->xvirt * BYTES_PER_PIXEL; + drvdata->info.pseudo_palette = drvdata->pseudo_palette; drvdata->info.flags = FBINFO_DEFAULT; - if (pdata) { - xilinx_fb_var.height = pdata->screen_height_mm; - xilinx_fb_var.width = pdata->screen_width_mm; - } drvdata->info.var = xilinx_fb_var; + drvdata->info.var.height = pdata->screen_height_mm; + drvdata->info.var.width = pdata->screen_width_mm; + drvdata->info.var.xres = pdata->xres; + drvdata->info.var.yres = pdata->yres; + drvdata->info.var.xres_virtual = pdata->xvirt; + drvdata->info.var.yres_virtual = pdata->yvirt; + + /* Allocate a colour map */ + rc = fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0); + if (rc) { + dev_err(dev, "Fail to allocate colormap (%d entries)\n", + PALETTE_ENTRIES_NO); + goto err_cmap; + } /* Register new frame buffer */ - if (register_framebuffer(&drvdata->info) < 0) { - printk(KERN_ERR "Could not register frame buffer\n"); - retval = -EINVAL; - goto failed4; + rc = register_framebuffer(&drvdata->info); + if (rc) { + dev_err(dev, "Could not register frame buffer\n"); + goto err_regfb; } + /* Put a banner in the log (for DEBUG) */ + dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr, drvdata->regs); + dev_dbg(dev, "fb: phys=%p, virt=%p, size=%x\n", + (void*)drvdata->fb_phys, drvdata->fb_virt, fbsize); + return 0; /* success */ -failed4: +err_regfb: fb_dealloc_cmap(&drvdata->info.cmap); -failed3: - dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt, - drvdata->fb_phys); - +err_cmap: + if (drvdata->fb_alloced) + dma_free_coherent(dev, PAGE_ALIGN(fbsize), drvdata->fb_virt, + drvdata->fb_phys); /* Turn off the display */ xilinx_fb_out_be32(drvdata, REG_CTRL, 0); + +err_fbmem: iounmap(drvdata->regs); -failed2: - release_mem_region(regs_res->start, 8); +err_map: + release_mem_region(physaddr, 8); -failed1: +err_region: kfree(drvdata); dev_set_drvdata(dev, NULL); - return retval; + return rc; } -static int -xilinxfb_drv_remove(struct device *dev) +static int xilinxfb_release(struct device *dev) { - struct xilinxfb_drvdata *drvdata; - - if (!dev) - return -ENODEV; - - drvdata = (struct xilinxfb_drvdata *) dev_get_drvdata(dev); + struct xilinxfb_drvdata *drvdata = dev_get_drvdata(dev); #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info); @@ -328,8 +339,9 @@ xilinxfb_drv_remove(struct device *dev) fb_dealloc_cmap(&drvdata->info.cmap); - dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt, - drvdata->fb_phys); + if (drvdata->fb_alloced) + dma_free_coherent(dev, PAGE_ALIGN(drvdata->info.fix.smem_len), + drvdata->fb_virt, drvdata->fb_phys); /* Turn off the display */ xilinx_fb_out_be32(drvdata, REG_CTRL, 0); @@ -343,29 +355,168 @@ xilinxfb_drv_remove(struct device *dev) return 0; } +/* --------------------------------------------------------------------- + * Platform bus binding + */ + +static int +xilinxfb_platform_probe(struct platform_device *pdev) +{ + struct xilinxfb_platform_data *pdata; + struct resource *res; -static struct device_driver xilinxfb_driver = { - .name = DRIVER_NAME, - .bus = &platform_bus_type, + /* Find the registers address */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Couldn't get registers resource\n"); + return -ENODEV; + } - .probe = xilinxfb_drv_probe, - .remove = xilinxfb_drv_remove + /* If a pdata structure is provided, then extract the parameters */ + pdata = &xilinx_fb_default_pdata; + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + if (!pdata->xres) + pdata->xres = xilinx_fb_default_pdata.xres; + if (!pdata->yres) + pdata->yres = xilinx_fb_default_pdata.yres; + if (!pdata->xvirt) + pdata->xvirt = xilinx_fb_default_pdata.xvirt; + if (!pdata->yvirt) + pdata->yvirt = xilinx_fb_default_pdata.yvirt; + } + + return xilinxfb_assign(&pdev->dev, res->start, pdata); +} + +static int +xilinxfb_platform_remove(struct platform_device *pdev) +{ + return xilinxfb_release(&pdev->dev); +} + + +static struct platform_driver xilinxfb_platform_driver = { + .probe = xilinxfb_platform_probe, + .remove = xilinxfb_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +/* --------------------------------------------------------------------- + * OF bus binding + */ + +#if defined(CONFIG_OF) +static int __devinit +xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const u32 *prop; + struct xilinxfb_platform_data pdata; + int size, rc; + + /* Copy with the default pdata (not a ptr reference!) */ + pdata = xilinx_fb_default_pdata; + + dev_dbg(&op->dev, "xilinxfb_of_probe(%p, %p)\n", op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + prop = of_get_property(op->node, "phys-size", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.screen_width_mm = prop[0]; + pdata.screen_height_mm = prop[1]; + } + + prop = of_get_property(op->node, "resolution", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.xres = prop[0]; + pdata.yres = prop[1]; + } + + prop = of_get_property(op->node, "virtual-resolution", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.xvirt = prop[0]; + pdata.yvirt = prop[1]; + } + + if (of_find_property(op->node, "rotate-display", NULL)) + pdata.rotate_screen = 1; + + return xilinxfb_assign(&op->dev, res.start, &pdata); +} + +static int __devexit xilinxfb_of_remove(struct of_device *op) +{ + return xilinxfb_release(&op->dev); +} + +/* Match table for of_platform binding */ +static struct of_device_id __devinit xilinxfb_of_match[] = { + { .compatible = "xilinx,ml300-fb", }, + {}, }; +MODULE_DEVICE_TABLE(of, xilinxfb_of_match); + +static struct of_platform_driver xilinxfb_of_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = xilinxfb_of_match, + .probe = xilinxfb_of_probe, + .remove = __devexit_p(xilinxfb_of_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __init xilinxfb_of_register(void) +{ + pr_debug("xilinxfb: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&xilinxfb_of_driver); +} + +static inline void __exit xilinxfb_of_unregister(void) +{ + of_unregister_platform_driver(&xilinxfb_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __init xilinxfb_of_register(void) { return 0; } +static inline void __exit xilinxfb_of_unregister(void) { } +#endif /* CONFIG_OF */ + +/* --------------------------------------------------------------------- + * Module setup and teardown + */ static int __init xilinxfb_init(void) { - /* - * No kernel boot options used, - * so we just need to register the driver - */ - return driver_register(&xilinxfb_driver); + int rc; + rc = xilinxfb_of_register(); + if (rc) + return rc; + + rc = platform_driver_register(&xilinxfb_platform_driver); + if (rc) + xilinxfb_of_unregister(); + + return rc; } static void __exit xilinxfb_cleanup(void) { - driver_unregister(&xilinxfb_driver); + platform_driver_unregister(&xilinxfb_platform_driver); + xilinxfb_of_unregister(); } module_init(xilinxfb_init); |