From: Thierry R. <thi...@av...> - 2009-08-14 14:13:09
|
This patch adds support for the Avionic Design Xanthos framebuffer. Signed-off-by: Thierry Reding <thi...@av...> Acked-by: Krzysztof Helt <krz...@wp...> --- drivers/video/Kconfig | 12 ++ drivers/video/Makefile | 1 + drivers/video/adxfb/Makefile | 1 + drivers/video/adxfb/adxfb.h | 131 +++++++++++++ drivers/video/adxfb/fb.c | 405 +++++++++++++++++++++++++++++++++++++++++ drivers/video/adxfb/overlay.c | 190 +++++++++++++++++++ drivers/video/adxfb/scaler.c | 231 +++++++++++++++++++++++ include/video/Kbuild | 1 + include/video/adxfb.h | 79 ++++++++ 9 files changed, 1051 insertions(+), 0 deletions(-) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3b54b39..7f1a94d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1668,6 +1668,18 @@ config CARMINE_DRAM_CUSTOM Use custom board timings. endchoice +config FB_ADX + tristate "Avionic Design Xanthos framebuffer support" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Framebuffer driver for the LCD controller in Avionic Design + Xanthos boards. + + If in doubt, say N. + config FB_AU1100 bool "Au1100 LCD Driver" depends on (FB = y) && MIPS && SOC_AU1100 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f..c648e23 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ +obj-$(CONFIG_FB_ADX) += adxfb/ # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/adxfb/Makefile b/drivers/video/adxfb/Makefile new file mode 100644 index 0000000..389d65c --- /dev/null +++ b/drivers/video/adxfb/Makefile @@ -0,0 +1 @@ +obj-y += fb.o overlay.o scaler.o diff --git a/drivers/video/adxfb/adxfb.h b/drivers/video/adxfb/adxfb.h new file mode 100644 index 0000000..3be8727 --- /dev/null +++ b/drivers/video/adxfb/adxfb.h @@ -0,0 +1,131 @@ +/* + * linux/drivers/video/adxfb/adxfb.h + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Written by Thierry Reding <thi...@av...> + */ + +#ifndef _DRIVERS_VIDEO_ADXFB_ADXFB_H +#define _DRIVERS_VIDEO_ADXFB_ADXFB_H 1 + +#include <linux/spinlock.h> +#include <video/adxfb.h> + +struct adxfb_mode_info { + /* mode resolution */ + u_short xres; + u_short yres; + u_short bpp; + + /* color packing specification */ + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield alpha; +}; + +/** + * struct adxfb_info - Avionic Design Xanthos framebuffer info structure + * @palette: VGA palette storage + * @io_base: memory-mapped base address for graphics controller + * @scaler_base: memory-mapped base address for scaler + * @ioctl: machine-specific I/O control handler + */ +struct adxfb_info { + u32 palette[16]; + + void __iomem *io_base; + void __iomem *scaler_base; + spinlock_t lock; + + int (*ioctl)(struct fb_info *info, unsigned int command, + unsigned long arg); +}; + +/* register definitions */ +#define ADXFB_CONTROL 0x000 +#define ADXFB_CONTROL_ENABLE (1 << 0) +#define ADXFB_CONTROL_SYNC (1 << 1) +#define ADXFB_CONTROL_DOUBLE_Y (1 << 2) +#define ADXFB_CONTROL_HALVE_X (1 << 3) +#define ADXFB_CONTROL_TRIPLE_Y (1 << 4) +#define ADXFB_CONTROL_LOCK (1 << 31) +#define ADXFB_OVERLAY_CONTROL 0x008 +#define ADXFB_OVERLAY_CONTROL_OVERLAY (1 << 0) +#define ADXFB_OVERLAY_CONTROL_ALPHA (1 << 1) +#define ADXFB_OVERLAY_START 0x010 +#define ADXFB_OVERLAY_END 0x018 +#define ADXFB_OVERLAY_PAGE0_START 0x020 +#define ADXFB_OVERLAY_PAGE0_END 0x028 +#define ADXFB_OVERLAY_PAGE0_SIZE 0x030 +#define ADXFB_OVERLAY_PAGE1_START 0x038 +#define ADXFB_OVERLAY_PAGE1_END 0x040 +#define ADXFB_OVERLAY_PAGE1_SIZE 0x048 +#define ADXFB_OVERLAY_LEVEL 0x050 +#define ADXFB_ALPHA_START 0x058 +#define ADXFB_ALPHA_END 0x060 +#define ADXFB_ALPHA_PAGE1_START 0x068 +#define ADXFB_ALPHA_PAGE1_END 0x070 +#define ADXFB_ALPHA_PAGE1_SIZE 0x078 +#define ADXFB_PAGE0_BASE 0x080 +#define ADXFB_PAGE0_FORMAT 0x088 +#define ADXFB_PAGE0_RESOLUTION 0x090 +#define ADXFB_PAGE0_RESOLUTION_BYTE 0x098 +#define ADXFB_PAGE0_SIZE 0x0a0 +#define ADXFB_PAGE1_BASE 0x0b0 +#define ADXFB_PAGE1_FORMAT 0x0b8 +#define ADXFB_PAGE1_RESOLUTION 0x0c0 +#define ADXFB_PAGE1_RESOLUTION_PHYSICAL 0x0c8 +#define ADXFB_PAGE1_RESOLUTION_VIRTUAL 0x0d0 +#define ADXFB_PAGE1_SIZE_PHYSICAL 0x0d8 +#define ADXFB_PAGE1_SIZE_VIRTUAL 0x0e0 +#define ADXFB_PAGE1_OFFSET 0x0e8 +#define ADXFB_PAGE1_SECONDARY_BASE 0x0f0 +#define ADXFB_COLOR_OFFSET 0x100 +#define ADXFB_COLOR_MUL 0x108 + +/* page format (ADXFB_PAGE0_FORMAT, ADXFB_PAGE1_FORMAT) */ +#define ADXFB_FMT_NONE 0 +#define ADXFB_FMT_GRAYSCALE_8 1 +#define ADXFB_FMT_RGB_565 2 +#define ADXFB_FMT_RGB_888 3 +#define ADXFB_FMT_RGBA_8888 4 + +/** + * adxfb_r32() - read 32-bit register + * @fb: framebuffer context + * @offset: relative register offset + */ +static inline u32 adxfb_r32(struct adxfb_info *fb, unsigned long offset) +{ + return readl(fb->io_base + offset); +} + +/** + * adxfb_w32() - write 32-bit register + * @fb: framebuffer context + * @offset: relative register offset + * @value: value to write to register + */ +static inline void adxfb_w32(struct adxfb_info *fb, unsigned long offset, + u32 value) +{ + writel(value, fb->io_base + offset); +} + +extern int adxfb_scaler_set_mode(struct fb_info *info, + struct adxfb_scaler_mode *mode); +extern int adxfb_scaler_get_mode(struct fb_info *info, + struct adxfb_scaler_mode *mode); + +extern int adxfb_overlay_enable(struct fb_info *info, unsigned long flags); +extern int adxfb_overlay_set_viewport(struct fb_info *info, + struct adxfb_viewport *viewport); + +#endif /* !_DRIVERS_VIDEO_ADXFB_ADXFB_H */ diff --git a/drivers/video/adxfb/fb.c b/drivers/video/adxfb/fb.c new file mode 100644 index 0000000..935042b --- /dev/null +++ b/drivers/video/adxfb/fb.c @@ -0,0 +1,405 @@ +/* + * linux/drivers/video/adxfb/fb.c + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Written by Thierry Reding <thi...@av...> + */ + +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> + +#include "adxfb.h" + +/** + * adxfb_setcolreg() - set color register + * @regno: register number to set + * @red: red color component + * @green: green color component + * @blue: blue color component + * @transp: transparency component + * @info: framebuffer context + */ +static int adxfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + if ((regno >= info->cmap.len) || (regno > 255)) + return 1; + +#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16) + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + ((struct adxfb_info *)info->par)->palette[regno] = + (CNVT_TOHW(red, info->var.red.length) + << info->var.red.offset) | + (CNVT_TOHW(green, info->var.green.length) + << info->var.green.offset) | + (CNVT_TOHW(blue, info->var.blue.length) + << info->var.blue.offset) | + (CNVT_TOHW(transp, info->var.transp.length) + << info->var.transp.offset); + } + break; + + default: + dev_err(info->dev, "bad depth: %u\n", info->var.bits_per_pixel); + break; + } +#undef CNVT_TOHW + + return 0; +} + +/** + * adxfb_ioctl() - handle I/O controls + * @info: framebuffer context + * @command: I/O control code + * @arg: I/O control argument + */ +static int adxfb_ioctl(struct fb_info *info, unsigned int command, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct adxfb_scaler_mode mode; + struct adxfb_viewport viewport; + int err = 0; + + switch (command) { + case ADXFB_IOCTL_SCALER_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) + return -EFAULT; + + err = adxfb_scaler_set_mode(info, &mode); + if (err < 0) + return err; + + break; + + case ADXFB_IOCTL_SCALER_GET_MODE: + err = adxfb_scaler_get_mode(info, &mode); + if (err < 0) + return err; + + if (copy_to_user(argp, &mode, sizeof(mode))) + return -EFAULT; + + break; + + case ADXFB_IOCTL_OVERLAY_ENABLE: + err = adxfb_overlay_enable(info, arg); + if (err < 0) + return err; + + break; + + case ADXFB_IOCTL_OVERLAY_SET_VIEWPORT: + if (copy_from_user(&viewport, argp, sizeof(viewport))) + return -EFAULT; + + err = adxfb_overlay_set_viewport(info, &viewport); + if (err < 0) + return err; + + break; + + default: + return -ENOTTY; + } + + return 0; +} + +/* framebuffer operations */ +static struct fb_ops adxfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = adxfb_setcolreg, + .fb_ioctl = adxfb_ioctl, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/** + * adxfb_set_bitfield() - initialize a bitfield structure + * @bf: bitfield structure to fill + * @offset: value for offset field + * @length: value for length field + * @msb_right: value for msb_right field + */ +static void adxfb_set_bitfield(struct fb_bitfield *bf, u32 offset, u32 length, + u32 msb_right) +{ + bf->offset = offset; + bf->length = length; + bf->msb_right = msb_right; +} + +/** + * adxfb_fmt_to_mode() - convert format type to video mode parameters + * @fmt: ADXFB format type + * @mode: video mode + */ +static int adxfb_fmt_to_mode(u8 fmt, struct adxfb_mode_info *mode) +{ + switch (fmt) { + case ADXFB_FMT_GRAYSCALE_8: + /* FIXME: use correct values here */ + adxfb_set_bitfield(&mode->red, 5, 3, 0); + adxfb_set_bitfield(&mode->green, 3, 2, 0); + adxfb_set_bitfield(&mode->blue, 0, 3, 0); + mode->bpp = 8; + break; + + case ADXFB_FMT_RGB_565: + adxfb_set_bitfield(&mode->red, 11, 5, 0); + adxfb_set_bitfield(&mode->green, 5, 6, 0); + adxfb_set_bitfield(&mode->blue, 0, 5, 0); + mode->bpp = 16; + break; + + case ADXFB_FMT_RGB_888: + /* FIXME: verify that these are correct values */ + adxfb_set_bitfield(&mode->red, 16, 8, 0); + adxfb_set_bitfield(&mode->green, 8, 8, 0); + adxfb_set_bitfield(&mode->blue, 0, 8, 0); + mode->bpp = 24; + break; + + case ADXFB_FMT_RGBA_8888: + /* FIXME: verify that these are correct values */ + adxfb_set_bitfield(&mode->alpha, 24, 8, 0); + adxfb_set_bitfield(&mode->red, 16, 8, 0); + adxfb_set_bitfield(&mode->green, 8, 8, 0); + adxfb_set_bitfield(&mode->blue, 0, 8, 0); + mode->bpp = 32; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/** + * adxfb_get_mach_mode() - obtain the current video mode + * @fb: framebuffer context + * @mode: structure to return the video mode in + */ +static int adxfb_get_mach_mode(struct adxfb_info *fb, + struct adxfb_mode_info *mode) +{ + u32 size; + u16 resx; + u16 resy; + u8 fmt; + + size = adxfb_r32(fb, ADXFB_PAGE0_RESOLUTION); + resx = (size >> 16) & 0x7ff; + resy = (size >> 0) & 0x7ff; + + fmt = adxfb_r32(fb, ADXFB_PAGE0_FORMAT) & 0xff; + + mode->xres = resx; + mode->yres = resy; + + return adxfb_fmt_to_mode(fmt, mode); +} + +/** + * adxfb_probe() - initialize the framebuffer device + * @pdev: platform device + */ +static int __init adxfb_probe(struct platform_device *pdev) +{ + struct adxfb_mode_info mode; + struct adxfb_info *fb; + struct fb_info *info; + struct resource *res; + int err = 0; + + info = framebuffer_alloc(sizeof(struct adxfb_info), &pdev->dev); + if (!info) { + dev_err(&pdev->dev, "failed to allocate framebuffer device\n"); + return -ENOMEM; + } + + fb = info->par; + spin_lock_init(&fb->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get graphics controller I/O " + "memory resource\n"); + err = -ENXIO; + goto free; + } + + res = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!res) { + dev_err(&pdev->dev, "failed to request graphics controller " + "I/O memory region\n"); + err = -ENXIO; + goto free; + } + + fb->io_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!fb->io_base) { + err = -ENXIO; + goto free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "failed to get scaler I/O memory " + "resource\n"); + err = -ENXIO; + goto free; + } + + res = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!res) { + dev_err(&pdev->dev, "failed to request scaler I/O memory " + "region\n"); + err = -ENXIO; + goto free; + } + + fb->scaler_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!fb->scaler_base) { + err = -ENXIO; + goto free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(&pdev->dev, "failed to get framebuffer I/O memory " + "resource\n"); + err = -ENXIO; + goto free; + } + + res = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!res) { + dev_err(&pdev->dev, "failed to request framebuffer I/O " + "memory region\n"); + err = -ENXIO; + goto free; + } + + info->screen_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!info->screen_base) { + err = -ENXIO; + goto free; + } + + /* TODO: add some checking for these parameters */ + memset(&mode, 0, sizeof(mode)); + adxfb_get_mach_mode(fb, &mode); + + snprintf(info->fix.id, sizeof(info->fix.id), "adxfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.accel = FB_ACCEL_NONE; + info->fix.line_length = mode.xres * (mode.bpp / 8); + info->fix.smem_start = res->start; + info->fix.smem_len = res->end - res->start + 1; + + info->var.activate = FB_ACTIVATE_NOW; + info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.xres = mode.xres; + info->var.yres = mode.yres; + info->var.xres_virtual = mode.xres; + info->var.yres_virtual = mode.yres; + info->var.bits_per_pixel = mode.bpp; + info->var.red = mode.red; + info->var.green = mode.green; + info->var.blue = mode.blue; + info->var.transp = mode.alpha; + info->var.width = mode.xres; + info->var.height = mode.yres; + + info->fbops = &adxfb_ops; + info->flags = FBINFO_DEFAULT; + info->pseudo_palette = fb->palette; + + err = fb_alloc_cmap(&info->cmap, 256, 0); + if (err < 0) { + err = -ENOMEM; + goto free; + } + + err = register_framebuffer(info); + if (err < 0) { + dev_err(&pdev->dev, "failed to register framebuffer\n"); + goto cmap; + } + + dev_info(info->dev, "ADX framebuffer initialized\n"); + platform_set_drvdata(pdev, info); + return 0; + +cmap: + fb_dealloc_cmap(&info->cmap); +free: + framebuffer_release(info); + return err; +} + +/** + * adxfb_remove() - shutdown the framebuffer device + * @pdev: platform device + */ +static int adxfb_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + unregister_framebuffer(info); + return 0; +} + +/* ADXFB platform driver */ +static struct platform_driver adxfb_driver = { + .probe = adxfb_probe, + .remove = adxfb_remove, + .driver = { + .name = "adxfb", + }, +}; + +/** + * adxfb_init() - module initialization + */ +int __init adxfb_init(void) +{ + return platform_driver_register(&adxfb_driver); +} + +/** + * adxfb_exit() - module cleanup + */ +void __exit adxfb_exit(void) +{ + platform_driver_unregister(&adxfb_driver); +} + +module_init(adxfb_init); +module_exit(adxfb_exit); + +MODULE_AUTHOR("Thierry Reding <thi...@av...>"); +MODULE_DESCRIPTION("Avionic Design Xanthos framebuffer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/adxfb/overlay.c b/drivers/video/adxfb/overlay.c new file mode 100644 index 0000000..f114a54 --- /dev/null +++ b/drivers/video/adxfb/overlay.c @@ -0,0 +1,190 @@ +/* + * linux/drivers/video/adxfb/overlay.c + * + * Copyright (C) 2008 Avionic Design Development GmbH + * Copyright (C) 2008 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Written by Thierry Reding <thi...@av...> + */ + +#include <linux/fb.h> +#include "adxfb.h" + +/* format to bit-depth table */ +struct fmt_bpp { + u8 fmt; + int bpp; +}; + +static struct fmt_bpp formats[] = { + { ADXFB_FMT_GRAYSCALE_8, 8 }, + { ADXFB_FMT_RGB_565, 16 }, + { ADXFB_FMT_RGB_888, 24 }, + { ADXFB_FMT_RGBA_8888, 32 }, +}; + +/** + * fmt_to_bpp() - obtain the bit-depth for a given page format + * @fmt: format of which to retrieve the bit-depth + */ +static int fmt_to_bpp(u8 fmt) +{ + int bpp = 0, i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (fmt == formats[i].fmt) { + bpp = formats[i].bpp; + break; + } + } + + return bpp; +} + +/** + * adxfb_disable() - disable the graphics controller + * @fb: framebuffer context + */ +static inline void adxfb_disable(struct adxfb_info *fb) +{ + u32 ctrl; + + spin_lock(&fb->lock); + + ctrl = adxfb_r32(fb, ADXFB_CONTROL); + ctrl &= ~ADXFB_CONTROL_ENABLE; + ctrl |= ADXFB_CONTROL_LOCK; + adxfb_w32(fb, ADXFB_CONTROL, ctrl); + + spin_unlock(&fb->lock); +} + +/** + * adxfb_enable() - enable the graphics controller + * @fb: framebuffer context + */ +static inline void adxfb_enable(struct adxfb_info *fb) +{ + u32 ctrl; + + spin_lock(&fb->lock); + + ctrl = adxfb_r32(fb, ADXFB_CONTROL); + ctrl &= ~ADXFB_CONTROL_LOCK; + ctrl |= ADXFB_CONTROL_ENABLE; + adxfb_w32(fb, ADXFB_CONTROL, ctrl); + + spin_unlock(&fb->lock); +} + +/** + * adxfb_overlay_enable() - enable the overlay window + * @info: framebuffer context + * @flags: flags for enabling/disabling + */ +int adxfb_overlay_enable(struct fb_info *info, unsigned long flags) +{ + struct adxfb_info *fb = info->par; + u32 ctrl; + + if (!info) + return -EINVAL; + + spin_lock(&fb->lock); + + ctrl = adxfb_r32(fb, ADXFB_OVERLAY_CONTROL); + + if (flags & ADXFB_OVERLAY_ENABLE) + ctrl |= ADXFB_OVERLAY_CONTROL_OVERLAY; + else + ctrl &= ~ADXFB_OVERLAY_CONTROL_OVERLAY; + + adxfb_w32(fb, ADXFB_OVERLAY_CONTROL, ctrl); + + spin_unlock(&fb->lock); + return 0; +} + +/** + * adxfb_overlay_set_viewport() - set the region for the overlay window + * @info: framebuffer context + * @viewport: new screen region for the overlay window + */ +int adxfb_overlay_set_viewport(struct fb_info *info, + struct adxfb_viewport *viewport) +{ + struct adxfb_info *fb = info->par; + int sx, sy, dx, dy, ex, ey; + int p0dx, p0dy; + int p0ps, p1ps; + u32 size; + + if (!info || !viewport) + return -EINVAL; + + sx = viewport->x & ~0x3; + sy = viewport->y & ~0x3; + dx = viewport->width & ~0x3; + dy = viewport->height & ~0x3; + ex = sx + dx - 1; + ey = sy + dy - 1; + + size = adxfb_r32(fb, ADXFB_PAGE0_RESOLUTION); + p0dx = (size >> 16) & 0x7ff; + p0dy = (size >> 0) & 0x7ff; + + size = adxfb_r32(fb, ADXFB_PAGE0_FORMAT) & 0xff; + p0ps = fmt_to_bpp(size) / 8; + + size = adxfb_r32(fb, ADXFB_PAGE1_FORMAT) & 0xff; + p1ps = fmt_to_bpp(size) / 8; + + adxfb_disable(fb); + spin_lock(&fb->lock); + + size = (((dx + 0) & 0x7ff) << 16) | (dy & 0x7ff); + adxfb_w32(fb, ADXFB_PAGE1_RESOLUTION, size); + + /* FIXME: (dx + 4) is a hack! */ + size = ((((dx + 4) * p1ps) & 0x1fff) << 16) | (dy & 0x7ff); + adxfb_w32(fb, ADXFB_PAGE1_RESOLUTION_PHYSICAL, size); + + size = ((((p0dx + 0) * p1ps) & 0x1fff) << 16) | (p0dy & 0x7ff); + adxfb_w32(fb, ADXFB_PAGE1_RESOLUTION_VIRTUAL, size); + + adxfb_w32(fb, ADXFB_PAGE1_SIZE_PHYSICAL, dx * dy * p1ps); + adxfb_w32(fb, ADXFB_PAGE1_SIZE_VIRTUAL, p0dx * p0dy * p1ps); + + size = ((sx & 0x7ff) << 16) | (sy & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_START, size); + + size = ((ex & 0x7ff) << 16) | (ey & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_END, size); + + size = (((sx * p0ps) & 0x1fff) << 16) | (sy & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_PAGE0_START, size); + adxfb_w32(fb, ADXFB_PAGE1_OFFSET, size); + + size = (((ex * p0ps) & 0x1fff) << 16) | (ey & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_PAGE0_END, size); + + size = (((sx * p1ps) & 0x1fff) << 16) | (sy & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_PAGE1_START, size); + + size = (((ex * p1ps) & 0x1fff) << 16) | (ey & 0x7ff); + adxfb_w32(fb, ADXFB_OVERLAY_PAGE1_END, size); + + adxfb_w32(fb, ADXFB_OVERLAY_PAGE0_SIZE, dx * dy * p0ps); + adxfb_w32(fb, ADXFB_OVERLAY_PAGE1_SIZE, dx * dy * p1ps); + + adxfb_w32(fb, ADXFB_OVERLAY_LEVEL, 0xff); + + spin_unlock(&fb->lock); + adxfb_enable(fb); + + return 0; +} diff --git a/drivers/video/adxfb/scaler.c b/drivers/video/adxfb/scaler.c new file mode 100644 index 0000000..96e5796 --- /dev/null +++ b/drivers/video/adxfb/scaler.c @@ -0,0 +1,231 @@ +/* + * linux/drivers/video/adxfb/fb.c + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Written by Thierry Reding <thi...@av...> + */ + +#include <linux/fb.h> +#include "adxfb.h" + +/** + * struct scaler_mode - scaler mode values + * @uh: horizontal up scaling factor + * @dh: horizontal down scaling factor + * @uv: vertical up scaling factor + * @dv: vertical down scaling factor + * @ox: original horizontal resolution + * @oy: original vertical resolution + * @tx: target horizontal resolution + * @ty: target vertical resolution + */ +struct scaler_mode { + u16 uh, dh; + u16 uv, dv; + u16 ox, oy; + u16 tx, ty; +}; + +/* register definitions */ +#define SCALER_MODE 0x000 +#define SCALER_MODE_ENABLE_X (1 << 0) +#define SCALER_MODE_ENABLE_Y (1 << 1) +#define SCALER_MODE_BYPASS_X (1 << 2) +#define SCALER_MODE_BYPASS_Y (1 << 3) +#define SCALER_MODE_RGB_888 (1 << 4) +#define SCALER_MODE_LOWPASS (1 << 7) +#define SCALER_MODE_SYNC (1 << 8) +#define SCALER_MODE_RESET (1 << 15) +#define SCALER_MODE_LOCK (1 << 31) +#define SCALER_UH 0x008 +#define SCALER_DH 0x010 +#define SCALER_UV 0x030 +#define SCALER_DV 0x038 +#define SCALER_ORIGIN_X 0x040 +#define SCALER_ORIGIN_Y 0x048 +#define SCALER_OFFSET_X 0x050 +#define SCALER_OFFSET_Y 0x058 +#define SCALER_TARGET_X 0x060 +#define SCALER_TARGET_Y 0x068 +#define SCALER_ORIGIN_STRIDE 0x070 +#define SCALER_TARGET_STRIDE 0x078 +#define SCALER_ORIGIN_SIZE 0x080 +#define SCALER_TARGET_SIZE 0x088 +#define SCALER_PRIMARY_PRE_TARGET_ADDR 0x090 +#define SCALER_PRIMARY_TARGET_ADDR 0x098 +#define SCALER_SECONDARY_PRE_TARGET_ADDR 0x0a0 +#define SCALER_SECONDARY_TARGET_ADDR 0x0a8 + +/** + * scaler_r32() - read a 32-bit register + * @fb: framebuffer context + * @offset: relative register offset + */ +static inline u32 scaler_r32(struct adxfb_info *fb, unsigned long offset) +{ + return readl(fb->scaler_base + offset); +} + +/** + * scaler_w32() - write a 32-bit register + * @fb: framebuffer context + * @offset: relative register offset + * @value: value to write to the register + */ +static inline void scaler_w32(struct adxfb_info *fb, unsigned long offset, + u32 value) +{ + writel(value, fb->scaler_base + offset); +} + +/** + * scaler_enable() - enable the scaling unit + * @fb: framebuffer context + */ +static inline void scaler_enable(struct adxfb_info *fb) +{ + u32 mode; + + spin_lock(&fb->lock); + + mode = scaler_r32(fb, SCALER_MODE); + mode |= SCALER_MODE_ENABLE_X; + mode |= SCALER_MODE_ENABLE_Y; + mode &= ~SCALER_MODE_LOCK; + scaler_w32(fb, SCALER_MODE, mode); + + spin_unlock(&fb->lock); +} + +/** + * scaler_disable() - disable the scaling unit + * @fb: framebuffer context + */ +static inline void scaler_disable(struct adxfb_info *fb) +{ + u32 mode; + + spin_lock(&fb->lock); + + mode = scaler_r32(fb, SCALER_MODE); + mode |= SCALER_MODE_LOCK; + mode &= ~SCALER_MODE_ENABLE_Y; + mode &= ~SCALER_MODE_ENABLE_X; + scaler_w32(fb, SCALER_MODE, mode); + + spin_unlock(&fb->lock); +} + +/** + * find_mode() - match a request to the best mode that can be achieved + * @fb: framebuffer context + * @mode: requested mode + */ +static int find_mode(struct adxfb_info *fb, struct scaler_mode *mode) +{ + if (!fb || !mode) + return -EINVAL; + + if ((mode->tx == 0) || (mode->ty == 0)) + return 0; + + mode->uh = scaler_r32(fb, SCALER_UH) & 0x7ff; + mode->uh = 128; /* FIXME: don't hardcode */ + mode->uv = scaler_r32(fb, SCALER_UV) & 0x7ff; + mode->uv = 128; /* FIXME: don't hardcode */ + + mode->dh = (mode->ox * mode->uh) / mode->tx; + mode->dv = (mode->oy * mode->uv) / mode->ty; + + mode->tx = (mode->ox * mode->uh) / mode->dh; + mode->ty = (mode->oy * mode->uv) / mode->dv; + + /* TODO: check the parameters */ + + return 0; +} + +/** + * adxfb_scaler_set_mode() - set a given scaler mode + * @info: framebuffer context + * @modep: scaler mode + */ +int adxfb_scaler_set_mode(struct fb_info *info, + struct adxfb_scaler_mode *modep) +{ + struct adxfb_info *fb = info->par; + struct scaler_mode mode; + int err, bpp; + u32 ctrl; + + memset(&mode, 0, sizeof(mode)); + mode.ox = modep->origin_x; + mode.oy = modep->origin_y; + mode.tx = modep->target_x; + mode.ty = modep->target_y; + + scaler_disable(fb); + spin_lock(&fb->lock); + + err = find_mode(fb, &mode); + if (err < 0) { + spin_unlock(&fb->lock); + scaler_enable(fb); + return err; + } + + ctrl = scaler_r32(fb, SCALER_MODE); + if (ctrl & SCALER_MODE_RGB_888) + bpp = 24; + else + bpp = 16; + + scaler_w32(fb, SCALER_UH, mode.uh); + scaler_w32(fb, SCALER_DH, mode.dh); + scaler_w32(fb, SCALER_UV, mode.uv); + scaler_w32(fb, SCALER_DV, mode.dv); + + scaler_w32(fb, SCALER_OFFSET_X, 0); + scaler_w32(fb, SCALER_OFFSET_Y, 0); + + scaler_w32(fb, SCALER_ORIGIN_X, mode.ox); + scaler_w32(fb, SCALER_ORIGIN_Y, mode.oy); + scaler_w32(fb, SCALER_TARGET_X, mode.tx); + scaler_w32(fb, SCALER_TARGET_Y, mode.ty); + + scaler_w32(fb, SCALER_ORIGIN_STRIDE, mode.ox * (bpp / 8)); + scaler_w32(fb, SCALER_TARGET_STRIDE, mode.tx * (bpp / 8)); + + scaler_w32(fb, SCALER_ORIGIN_SIZE, mode.ox * mode.oy * (bpp / 8)); + scaler_w32(fb, SCALER_TARGET_SIZE, mode.tx * mode.ty * (bpp / 8)); + + spin_unlock(&fb->lock); + scaler_enable(fb); + return 0; +} + +/** + * adxfb_scaler_mode() - obtain the current scaler mode + * @info: framebuffer context + * @modep: structure to return the mode in + */ +int adxfb_scaler_get_mode(struct fb_info *info, struct adxfb_scaler_mode *modep) +{ + struct adxfb_info *fb = info->par; + + spin_lock(&fb->lock); + + modep->origin_x = scaler_r32(fb, SCALER_ORIGIN_X) & 0x7ff; + modep->origin_y = scaler_r32(fb, SCALER_ORIGIN_Y) & 0x7ff; + modep->target_x = scaler_r32(fb, SCALER_TARGET_X) & 0x7ff; + modep->target_y = scaler_r32(fb, SCALER_TARGET_Y) & 0x7ff; + + spin_unlock(&fb->lock); + return 0; +} diff --git a/include/video/Kbuild b/include/video/Kbuild index 0e406f7..72fc9b0 100644 --- a/include/video/Kbuild +++ b/include/video/Kbuild @@ -1,2 +1,3 @@ unifdef-y += sisfb.h uvesafb.h unifdef-y += edid.h +unifdef-y += adxfb.h diff --git a/include/video/adxfb.h b/include/video/adxfb.h new file mode 100644 index 0000000..534b9e7 --- /dev/null +++ b/include/video/adxfb.h @@ -0,0 +1,79 @@ +/* + * linux/include/video/adxfb.h + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding <thi...@av...> + */ + +#ifndef _VIDEO_ADXFB_H +#define _VIDEO_ADXFB_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * struct adxfb_scaler_mode - scaler mode definition structure + * @origin_x: original horizontal resolution + * @origin_y: original vertical resolution + * @target_x: targetted horizontal resolution + * @target_y: targetted vertical resolution + */ +struct adxfb_scaler_mode { + /* original resolution */ + __u16 origin_x; + __u16 origin_y; + /* target resolution */ + __u16 target_x; + __u16 target_y; +}; + +/** + * struct adxfb_viewport - overlay viewport structure + * @x: horizontal start position of the overlay window + * @y: vertical start position of the overlay window + * @width: width of the overlay window + * @height: height of the overlay window + */ +struct adxfb_viewport { + /* viewport position */ + __u16 x; + __u16 y; + /* viewport resolution */ + __u16 width; + __u16 height; +}; + +/* I/O control codes */ +#define ADXFB_IOC_MAGIC 'a' + +/* set a new scaler mode */ +#define ADXFB_IOCTL_SCALER_SET_MODE \ + _IOW(ADXFB_IOC_MAGIC, 0, struct adxfb_scaler_mode) +/* obtain the current scaler mode */ +#define ADXFB_IOCTL_SCALER_GET_MODE \ + _IOR(ADXFB_IOC_MAGIC, 1, struct adxfb_scaler_mode) +/* enable/disable the overlay window */ +#define ADXFB_IOCTL_OVERLAY_ENABLE \ + _IOW(ADXFB_IOC_MAGIC, 2, unsigned long) +/* set a new region for the overlay window */ +#define ADXFB_IOCTL_OVERLAY_SET_VIEWPORT \ + _IOW(ADXFB_IOC_MAGIC, 3, struct adxfb_viewport) +/* set a new input video standard */ +#define ADXFB_IOCTL_SET_INPUT \ + _IOW(ADXFB_IOC_MAGIC, 4, unsigned long) + +/* ADXFB_IOCTL_OVERLAY_ENABLE flags */ +#define ADXFB_OVERLAY_ENABLE (1 << 0) /* enable/disable overlay */ + +/* ADXFB_IOCTL_SET_INPUT parameters */ +#define ADXFB_INPUT_PAL (0x00) /* input is PAL standard */ +#define ADXFB_INPUT_NTSC (0x01) /* input is NTSC stardard */ +#define ADXFB_INPUT_MASK (0xff) /* mask to extract input */ + +#endif /* !_VIDEO_ADXFB_H */ -- tg: (c98059f..) adx/fb (depends on: adx/master) |