From 36ac3fa1184b392dc54024de6d98e4355f2baba8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 4 Nov 2008 15:12:21 +0200 Subject: [PATCH] DSS: OMAPFB: fb driver for new display subsystem Signed-off-by: Tomi Valkeinen --- arch/arm/plat-omap/fb.c | 9 +- arch/arm/plat-omap/include/mach/omapfb.h | 7 + drivers/video/Kconfig | 1 + drivers/video/Makefile | 1 + drivers/video/omap/Kconfig | 5 +- drivers/video/omap2/Kconfig | 29 + drivers/video/omap2/Makefile | 2 + drivers/video/omap2/omapfb-ioctl.c | 428 ++++++++++ drivers/video/omap2/omapfb-main.c | 1276 ++++++++++++++++++++++++++++++ drivers/video/omap2/omapfb-sysfs.c | 833 +++++++++++++++++++ drivers/video/omap2/omapfb.h | 104 +++ 11 files changed, 2692 insertions(+), 3 deletions(-) create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/omapfb-ioctl.c create mode 100644 drivers/video/omap2/omapfb-main.c create mode 100644 drivers/video/omap2/omapfb-sysfs.c create mode 100644 drivers/video/omap2/omapfb.h diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 3746222..0ba1603 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -36,7 +36,8 @@ #include #include -#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) \ + || defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE) static struct omapfb_platform_data omapfb_config; static int config_invalid; @@ -298,14 +299,18 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart, return reserved; } +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) void omapfb_set_ctrl_platform_data(void *data) { omapfb_config.ctrl_platform_data = data; } +#endif static inline int omap_init_fb(void) { +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) const struct omap_lcd_config *conf; +#endif if (config_invalid) return 0; @@ -313,6 +318,7 @@ static inline int omap_init_fb(void) printk(KERN_ERR "Invalid FB mem configuration entries\n"); return 0; } +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); if (conf == NULL) { if (configured_regions) @@ -321,6 +327,7 @@ static inline int omap_init_fb(void) return 0; } omapfb_config.lcd = *conf; +#endif return platform_device_register(&omap_fb_device); } diff --git a/arch/arm/plat-omap/include/mach/omapfb.h b/arch/arm/plat-omap/include/mach/omapfb.h index 90d63c5..1e34304 100644 --- a/arch/arm/plat-omap/include/mach/omapfb.h +++ b/arch/arm/plat-omap/include/mach/omapfb.h @@ -90,6 +90,13 @@ enum omapfb_color_format { OMAPFB_COLOR_CLUT_1BPP, OMAPFB_COLOR_RGB444, OMAPFB_COLOR_YUY422, + + OMAPFB_COLOR_ARGB16, + OMAPFB_COLOR_RGB24U, /* RGB24, 32-bit container */ + OMAPFB_COLOR_RGB24P, /* RGB24, 24-bit container */ + OMAPFB_COLOR_ARGB32, + OMAPFB_COLOR_RGBA32, + OMAPFB_COLOR_RGBX32, }; struct omapfb_update_window { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3f3ce13..689a3b1 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2116,6 +2116,7 @@ config FB_PRE_INIT_FB the bootloader. source "drivers/video/omap/Kconfig" +source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e39e33e..3d9d50e 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -120,6 +120,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-$(CONFIG_OMAP2_DSS) += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index c355b59..541fab3 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,6 +1,7 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && ARCH_OMAP + depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") + select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -80,7 +81,7 @@ config FB_OMAP_BOOTLOADER_INIT config FB_OMAP_CONSISTENT_DMA_SIZE int "Consistent DMA memory size (MB)" - depends on FB_OMAP + depends on FB && ARCH_OMAP range 1 14 default 2 help diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig new file mode 100644 index 0000000..4b72479 --- /dev/null +++ b/drivers/video/omap2/Kconfig @@ -0,0 +1,29 @@ +config FB_OMAP2 + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + depends on FB && OMAP2_DSS + + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Frame buffer driver for OMAP2/3 based boards. + +config FB_OMAP2_DEBUG + bool "Debug output for OMAP2/3 FB" + depends on FB_OMAP2 + +config FB_OMAP2_FORCE_AUTO_UPDATE + bool "Force main display to automatic update mode" + depends on FB_OMAP2 + help + Forces main display to automatic update mode (if possible), + and also enables tearsync (if possible). By default + displays that support manual update are started in manual + update mode. + +menu "OMAP2/3 Display Device Drivers" + depends on OMAP2_DSS + + +endmenu + diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile new file mode 100644 index 0000000..51c2e00 --- /dev/null +++ b/drivers/video/omap2/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FB_OMAP2) += omapfb.o +omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o diff --git a/drivers/video/omap2/omapfb-ioctl.c b/drivers/video/omap2/omapfb-ioctl.c new file mode 100644 index 0000000..1ceb6b9 --- /dev/null +++ b/drivers/video/omap2/omapfb-ioctl.c @@ -0,0 +1,428 @@ +/* + * linux/drivers/video/omap2/omapfb-ioctl.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include "omapfb.h" + +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + struct omap_overlay *ovl; + int r = 0; + + DBG("omapfb_setup_plane\n"); + + if (ofbi->num_overlays != 1) { + r = -EINVAL; + goto out; + } + + ovl = ofbi->overlays[0]; + + omapfb_lock(fbdev); + + if (display) { + if (pi->pos_x + pi->out_width > display->x_res || + pi->pos_y + pi->out_height > display->y_res) { + r = -EINVAL; + goto out; + } + } + + if (pi->enabled && !ofbi->region.size) { + /* + * This plane's memory was freed, can't enable it + * until it's reallocated. + */ + r = -EINVAL; + goto out; + } + + if (!ovl) { + r = -EINVAL; + goto out; + } + + r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y, + pi->out_width, pi->out_height); + if (r) + goto out; + + ovl->enable(ovl, pi->enabled); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + if (display) { + if (display->sync) + display->sync(display); + + if (display->update) + display->update(display, 0, 0, + display->x_res, display->y_res); + } + +out: + omapfb_unlock(fbdev); + if (r) + dev_err(fbdev->dev, "setup_plane failed\n"); + return r; +} + +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + + omapfb_lock(fbdev); + + if (ofbi->num_overlays != 1) { + memset(pi, 0, sizeof(*pi)); + } else { + struct omap_overlay_info *ovli; + struct omap_overlay *ovl; + + ovl = ofbi->overlays[0]; + ovli = &ovl->info; + + pi->pos_x = ovli->pos_x; + pi->pos_y = ovli->pos_y; + pi->enabled = ovli->enabled; + pi->channel_out = 0; /* xxx */ + pi->mirror = 0; + pi->out_width = ovli->out_width; + pi->out_height = ovli->out_height; + } + + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg; + int ret = -EINVAL; + + rg = &ofbi->region; + + omapfb_lock(fbdev); + if (mi->size > rg->size) { + ret = -ENOMEM; + goto out; + } + + if (mi->type != rg->type) + goto out; + + ret = 0; +out: + omapfb_unlock(fbdev); + + return ret; +} + +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg; + + rg = &ofbi->region; + memset(mi, 0, sizeof(*mi)); + + omapfb_lock(fbdev); + mi->size = rg->size; + mi->type = rg->type; + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_update_window(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + if (!display) + return 0; + + if (w == 0 || h == 0) + return 0; + + if (x + w > display->x_res || y + h > display->y_res) + return -EINVAL; + + omapfb_lock(fbdev); + display->update(display, x, y, w, h); + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_set_update_mode(struct fb_info *fbi, + enum omapfb_update_mode mode) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + enum omap_dss_update_mode um; + int r; + + if (!display || !display->set_update_mode) + return -EINVAL; + + switch (mode) { + case OMAPFB_UPDATE_DISABLED: + um = OMAP_DSS_UPDATE_DISABLED; + break; + + case OMAPFB_AUTO_UPDATE: + um = OMAP_DSS_UPDATE_AUTO; + break; + + case OMAPFB_MANUAL_UPDATE: + um = OMAP_DSS_UPDATE_MANUAL; + break; + + default: + return -EINVAL; + } + + omapfb_lock(fbdev); + r = display->set_update_mode(display, um); + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_get_update_mode(struct fb_info *fbi, + enum omapfb_update_mode *mode) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + enum omap_dss_update_mode m; + + if (!display || !display->get_update_mode) + return -EINVAL; + + omapfb_lock(fbdev); + m = display->get_update_mode(display); + omapfb_unlock(fbdev); + + switch (m) { + case OMAP_DSS_UPDATE_DISABLED: + *mode = OMAPFB_UPDATE_DISABLED; + break; + case OMAP_DSS_UPDATE_AUTO: + *mode = OMAPFB_AUTO_UPDATE; + break; + case OMAP_DSS_UPDATE_MANUAL: + *mode = OMAPFB_MANUAL_UPDATE; + break; + default: + BUG(); + } + + return 0; +} + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + union { + struct omapfb_update_window_old uwnd_o; + struct omapfb_update_window uwnd; + struct omapfb_plane_info plane_info; + struct omapfb_caps caps; + struct omapfb_mem_info mem_info; + enum omapfb_update_mode update_mode; + int test_num; + } p; + + int r = 0; + + DBG("ioctl %x (%d)\n", cmd, cmd & 0xff); + + switch (cmd) { + case OMAPFB_SYNC_GFX: + if (!display || !display->sync) { + r = -EINVAL; + break; + } + + omapfb_lock(fbdev); + r = display->sync(display); + omapfb_unlock(fbdev); + break; + + case OMAPFB_UPDATE_WINDOW_OLD: + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd_o, + (void __user *)arg, + sizeof(p.uwnd_o))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y, + p.uwnd_o.width, p.uwnd_o.height); + break; + + case OMAPFB_UPDATE_WINDOW: + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd, (void __user *)arg, + sizeof(p.uwnd))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y, + p.uwnd.width, p.uwnd.height); + break; + + case OMAPFB_SETUP_PLANE: + if (copy_from_user(&p.plane_info, (void __user *)arg, + sizeof(p.plane_info))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbi, &p.plane_info); + break; + + case OMAPFB_QUERY_PLANE: + r = omapfb_query_plane(fbi, &p.plane_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.plane_info, + sizeof(p.plane_info))) + r = -EFAULT; + break; + + case OMAPFB_SETUP_MEM: + if (copy_from_user(&p.mem_info, (void __user *)arg, + sizeof(p.mem_info))) + r = -EFAULT; + else + r = omapfb_setup_mem(fbi, &p.mem_info); + break; + + case OMAPFB_QUERY_MEM: + r = omapfb_query_mem(fbi, &p.mem_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.mem_info, + sizeof(p.mem_info))) + r = -EFAULT; + break; + + case OMAPFB_GET_CAPS: + if (!display) { + r = -EINVAL; + break; + } + + p.caps.ctrl = display->caps; + + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) + r = -EFAULT; + break; + + case OMAPFB_SET_UPDATE_MODE: + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbi, p.update_mode); + break; + + case OMAPFB_GET_UPDATE_MODE: + r = omapfb_get_update_mode(fbi, &p.update_mode); + if (r) + break; + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + + /* LCD and CTRL tests do the same thing for backward + * compatibility */ + case OMAPFB_LCD_TEST: + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_CTRL_TEST: + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + default: + DBG("ioctl unhandled\n"); + r = -EINVAL; + } + + return r; +} + + diff --git a/drivers/video/omap2/omapfb-main.c b/drivers/video/omap2/omapfb-main.c new file mode 100644 index 0000000..c0f1664 --- /dev/null +++ b/drivers/video/omap2/omapfb-main.c @@ -0,0 +1,1276 @@ +/* + * linux/drivers/video/omap2/omapfb-main.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "omapfb.h" + +#define MODULE_NAME "omapfb" + +#ifdef DEBUG +static void fill_fb(void *addr, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + + const short w = var->xres_virtual; + const short h = var->yres_virtual; + + int y, x; + u8 *p = addr; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (var->bits_per_pixel == 16) { + u16 *pw = (u16 *)p; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + *pw = 0xffff; + else if (x == y || w - x == h - y) + *pw = ((1<<5)-1)<<11; + else if (w - x == y || x == h - y) + *pw = ((1<<6)-1)<<5; + else { + int t = x / (w/3); + if (t == 0) + *pw = y % 32; + else if (t == 1) + *pw = (y % 64) << 5; + else if (t == 2) + *pw = (y % 32) << 11; + } + } else if (var->bits_per_pixel == 24) { + u8 *pb = (u8 *)p; + + int r = 0, g = 0, b = 0; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + r = g = b = 0xff; + else if (x == y || w - x == h - y) + r = 0xff; + else if (w - x == y || x == h - y) + g = 0xff; + else { + int q = x / (w / 3); + u8 base = 255 - (y % 256); + if (q == 0) + r = base; + else if (q == 1) + g = base; + else if (q == 2) + b = base; + } + + pb[0] = b; + pb[1] = g; + pb[2] = r; + + } else if (var->bits_per_pixel == 32) { + u32 *pd = (u32 *)p; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + *pd = 0xffffff; + else if (x == y || w - x == h - y) + *pd = 0xff0000; + else if (w - x == y || x == h - y) + *pd = 0x00ff00; + else { + u8 base = 255 - (y % 256); + *pd = base << ((x / (w/3)) << 3); + } + } + + p += var->bits_per_pixel >> 3; + } + } +} +#endif + +static enum omap_color_mode fb_mode_to_dss_mode(struct fb_var_screeninfo *var) +{ + switch (var->nonstd) { + case 0: + break; + case OMAPFB_COLOR_YUV422: + return OMAP_DSS_COLOR_YUV2; + + case OMAPFB_COLOR_YUY422: + return OMAP_DSS_COLOR_UYVY; + + case OMAPFB_COLOR_ARGB16: + return OMAP_DSS_COLOR_ARGB16; + + case OMAPFB_COLOR_ARGB32: + return OMAP_DSS_COLOR_ARGB32; + + case OMAPFB_COLOR_RGBA32: + return OMAP_DSS_COLOR_RGBA32; + + case OMAPFB_COLOR_RGBX32: + return OMAP_DSS_COLOR_RGBX32; + + default: + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 1: + return OMAP_DSS_COLOR_CLUT1; + case 2: + return OMAP_DSS_COLOR_CLUT2; + case 4: + return OMAP_DSS_COLOR_CLUT4; + case 8: + return OMAP_DSS_COLOR_CLUT8; + case 12: + return OMAP_DSS_COLOR_RGB12U; + case 16: + return OMAP_DSS_COLOR_RGB16; + case 24: + return OMAP_DSS_COLOR_RGB24P; + case 32: + return OMAP_DSS_COLOR_RGB24U; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static void set_fb_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + struct omapfb_mem_region *rg = &FB2OFB(fbi)->region; + + DBG("set_fb_fix\n"); + + /* used by open/write in fbmem.c */ + fbi->screen_base = (char __iomem *)rg->vaddr; + + /* used by mmap in fbmem.c */ + fix->smem_start = rg->paddr; + fix->smem_len = rg->size; + + fix->type = FB_TYPE_PACKED_PIXELS; + + if (var->nonstd) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else { + switch (var->bits_per_pixel) { + case 32: + case 24: + case 16: + case 12: + fix->visual = FB_VISUAL_TRUECOLOR; + /* 12bpp is stored in 16 bits */ + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + } + + fix->accel = FB_ACCEL_NONE; + fix->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; + + fix->xpanstep = 1; + fix->ypanstep = 1; +} + +/* check new var and possibly modify it to be ok */ +static int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_display *display = fb2display(fbi); + unsigned long max_frame_size; + unsigned long line_size; + int xres_min, xres_max; + int yres_min, yres_max; + enum omap_color_mode mode = 0; + struct omap_overlay *ovl; + + DBG("check_fb_var %d\n", ofbi->id); + + if (ofbi->num_overlays == 0) { + dev_err(ofbi->fbdev->dev, "no overlays, aborting\n"); + return -EINVAL; + } + + /* XXX: uses the first overlay */ + ovl = ofbi->overlays[0]; + + /* if we are using non standard mode, fix the bpp first */ + switch (var->nonstd) { + case 0: + break; + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + case OMAPFB_COLOR_ARGB16: + var->bits_per_pixel = 16; + break; + case OMAPFB_COLOR_ARGB32: + case OMAPFB_COLOR_RGBA32: + case OMAPFB_COLOR_RGBX32: + var->bits_per_pixel = 32; + break; + default: + DBG("invalid nonstd mode\n"); + return -EINVAL; + } + + mode = fb_mode_to_dss_mode(var); + if (mode < 0) { + DBG("cannot convert var to omap dss mode\n"); + return -EINVAL; + } + + if ((ovl->supported_modes & mode) == 0) { + DBG("invalid mode\n"); + return -EINVAL; + } + + xres_min = OMAPFB_PLANE_XRES_MIN; + xres_max = (display ? display->x_res : 2048) - ovl->info.pos_x; + yres_min = OMAPFB_PLANE_YRES_MIN; + yres_max = (display ? display->y_res : 2048) - ovl->info.pos_y; + + if (var->xres < xres_min) + var->xres = xres_min; + if (var->yres < yres_min) + var->yres = yres_min; + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + max_frame_size = ofbi->region.size; + line_size = (var->xres_virtual * var->bits_per_pixel) >> 3; + + if (line_size * var->yres_virtual > max_frame_size) { + /* Try to keep yres_virtual first */ + line_size = max_frame_size / var->yres_virtual; + var->xres_virtual = line_size * 8 / var->bits_per_pixel; + if (var->xres_virtual < var->xres) { + /* Still doesn't fit. Shrink yres_virtual too */ + var->xres_virtual = var->xres; + line_size = var->xres * var->bits_per_pixel / 8; + var->yres_virtual = max_frame_size / line_size; + } + /* Recheck this, as the virtual size changed. */ + if (var->xres_virtual < var->xres) + var->xres = var->xres_virtual; + if (var->yres_virtual < var->yres) + var->yres = var->yres_virtual; + if (var->xres < xres_min || var->yres < yres_min) { + DBG("Cannot fit FB to memory\n"); + return -EINVAL; + } + } + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + if (var->bits_per_pixel == 16) { + var->red.offset = 11; var->red.length = 5; + var->red.msb_right = 0; + var->green.offset = 5; var->green.length = 6; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 5; + var->blue.msb_right = 0; + } else if (var->bits_per_pixel == 24) { + var->red.offset = 16; var->red.length = 8; + var->red.msb_right = 0; + var->green.offset = 8; var->green.length = 8; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 8; + var->blue.msb_right = 0; + var->transp.offset = 0; var->transp.length = 0; + } else if (var->bits_per_pixel == 32) { + var->red.offset = 16; var->red.length = 8; + var->red.msb_right = 0; + var->green.offset = 8; var->green.length = 8; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 8; + var->blue.msb_right = 0; + var->transp.offset = 0; var->transp.length = 0; + } else { + DBG("failed to setup fb color mask\n"); + return -EINVAL; + } + + DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual); + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + if (display && display->check_timings) { + struct omap_video_timings timings; + timings.pixel_clock = PICOS2KHZ(var->pixclock); + timings.hfp = var->left_margin; + timings.hbp = var->right_margin; + timings.vfp = var->upper_margin; + timings.vbp = var->lower_margin; + timings.hsw = var->hsync_len; + timings.vsw = var->vsync_len; + + if (display->check_timings(display, &timings)) { + DBG("illegal video timings\n"); + return -EINVAL; + } + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = KHZ2PICOS(timings.pixel_clock); + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks + * --------------------------------------------------------------------------- + */ +static int omapfb_open(struct fb_info *fbi, int user) +{ + return 0; +} + +static int omapfb_release(struct fb_info *fbi, int user) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + DBG("Closing fb with plane index %d\n", ofbi->id); + + omapfb_lock(fbdev); +#if 1 + if (display) { + /* XXX Is this really needed ? */ + if (display->sync) + display->sync(display); + + if (display->update) + display->update(display, + 0, 0, + display->x_res, display->y_res); + } +#endif + + if (display && display->sync) + display->sync(display); + + omapfb_unlock(fbdev); + + return 0; +} + +/* setup overlay according to the fb */ +int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + int posx, int posy, int outw, int outh) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + enum omap_color_mode mode = 0; + int offset; + u32 data_start_p; + void *data_start_v; + + DBG("setup_overlay %d\n", ofbi->id); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && + (outw != var->xres || outh != var->yres)) { + r = -EINVAL; + goto err; + } + + offset = ((var->yoffset * var->xres_virtual + + var->xoffset) * var->bits_per_pixel) >> 3; + + data_start_p = ofbi->region.paddr + offset; + data_start_v = ofbi->region.vaddr + offset; + + mode = fb_mode_to_dss_mode(var); + + if (mode == -EINVAL) { + r = -EINVAL; + goto err; + } + + r = ovl->setup_input(ovl, + data_start_p, data_start_v, + var->xres_virtual, + var->xres, var->yres, + mode); + + if (r) + goto err; + + r = ovl->setup_output(ovl, + posx, posy, + outw, outh); + + if (r) + goto err; + + return 0; + +err: + DBG("setup_overlay failed\n"); + return r; +} + +/* apply var to the overlay */ +int omapfb_apply_changes(struct fb_info *fbi, int init) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + /*struct omap_display *display = fb2display(fbi);*/ + struct omap_overlay *ovl; + int posx, posy; + int outw, outh; + int i; + + for (i = 0; i < ofbi->num_overlays; i++) { + ovl = ofbi->overlays[i]; + + DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); + + if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = var->xres; + outh = var->yres; + } else { + outw = ovl->info.out_width; + outh = ovl->info.out_height; + } + + if (init) { + posx = 0; + posy = 0; + } else { + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + } + + r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); + if (r) + goto err; + + /* disabled for now. if the display has changed, var + * still contains the old timings. */ +#if 0 + if (display && display->set_timings) { + struct omap_video_timings timings; + timings.pixel_clock = PICOS2KHZ(var->pixclock); + timings.hfp = var->left_margin; + timings.hbp = var->right_margin; + timings.vfp = var->upper_margin; + timings.vbp = var->lower_margin; + timings.hsw = var->hsync_len; + timings.vsw = var->vsync_len; + + display->set_timings(display, &timings); + } +#endif + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + } + return 0; +err: + DBG("apply_changes failed\n"); + return r; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + int r; + + DBG("check_var(%d)\n", FB2OFB(fbi)->id); + + r = check_fb_var(fbi, var); + + return r; +} + +/* set the video mode according to info->var */ +static int omapfb_set_par(struct fb_info *fbi) +{ + int r; + + DBG("set_par(%d)\n", FB2OFB(fbi)->id); + + set_fb_fix(fbi); + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static void omapfb_rotate(struct fb_info *fbi, int rotate) +{ + DBG("rotate(%d)\n", FB2OFB(fbi)->id); + return; +} + +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int r = 0; + + DBG("pan_display(%d)\n", ofbi->id); + + omapfb_lock(fbdev); + + if (var->xoffset != fbi->var.xoffset || + var->yoffset != fbi->var.yoffset) { + struct fb_var_screeninfo new_var; + + new_var = fbi->var; + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + + r = check_fb_var(fbi, &new_var); + + if (r == 0) { + fbi->var = new_var; + set_fb_fix(fbi); + r = omapfb_apply_changes(fbi, 0); + } + } + + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg = &ofbi->region; + + return dma_mmap_writecombine(fbdev->dev, vma, + rg->vaddr, + rg->paddr, + rg->size); +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ + /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ + struct fb_var_screeninfo *var = &fbi->var; + int r = 0; + + enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ + + /*switch (plane->color_mode) {*/ + switch (mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + case OMAPFB_COLOR_YUY422: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + /* + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + */ + /* Fallthrough */ + r = -EINVAL; + break; + case OMAPFB_COLOR_RGB565: + case OMAPFB_COLOR_RGB444: + case OMAPFB_COLOR_RGB24P: + case OMAPFB_COLOR_RGB24U: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> (16 - var->red.length)) << + var->red.offset) | + ((green >> (16 - var->green.length)) << + var->green.offset) | + (blue >> (16 - var->blue.length)); + ((u32 *)(fbi->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + DBG("setcolreg\n"); + + return _setcolreg(info, regno, red, green, blue, transp, 1); +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + DBG("setcmap\n"); + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + int do_update = 0; + int r = 0; + + omapfb_lock(fbdev); + + switch (blank) { + case VESA_NO_BLANKING: + if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto exit; + } + + if (display->resume) + r = display->resume(display); + + if (r == 0 && display->get_update_mode && + display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) + do_update = 1; + + break; + + case VESA_POWERDOWN: + if (display->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto exit; + } + + if (display->suspend) + r = display->suspend(display); + + break; + + default: + r = -EINVAL; + } + +exit: + omapfb_unlock(fbdev); + + if (r == 0 && do_update && display->update) + r = display->update(display, + 0, 0, + display->x_res, display->y_res); + + return r; +} + +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_rotate = omapfb_rotate, + .fb_pan_display = omapfb_pan_display, + .fb_mmap = omapfb_mmap, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, +}; + +static int omapfb_free_fbmem(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free fbmem\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + struct omapfb_mem_region *rg; + + rg = &ofbi->region; + + if (rg->alloc) { + dma_free_writecombine(fbdev->dev, rg->size, + rg->vaddr, rg->paddr); + } + + rg->vaddr = NULL; + rg->paddr = 0; + rg->alloc = 0; + } + + fbdev->num_fbs = 0; + + return 0; +} + +static int omapfb_allocate_fbmem(struct omapfb2_device *fbdev) +{ + int i; + struct omapfb_mem_desc *plat_mem_desc; + struct omapfb_platform_data *pdata = fbdev->dev->platform_data; + + plat_mem_desc = &pdata->mem_desc; + + DBG("omapfb: setup mem regions, %d regions\n", + plat_mem_desc->region_cnt); + + for (i = 0; i < plat_mem_desc->region_cnt; i++) { + struct omapfb_mem_region *plat_rg; + struct omapfb_mem_region *rg; + struct omapfb_info *ofb_info = FB2OFB(fbdev->fbs[i]); + + plat_rg = &plat_mem_desc->region[i]; + rg = &ofb_info->region; + + memset(rg, 0, sizeof(*rg)); + + DBG("platform region%d phys %08x virt %p size=%lu\n", + i, + plat_rg->paddr, + plat_rg->vaddr, + plat_rg->size); + + if (plat_rg->paddr == 0) { + u32 paddr; + void *vaddr; + + vaddr = dma_alloc_writecombine(fbdev->dev, + plat_rg->size, + &paddr, GFP_KERNEL); + + if (vaddr == NULL) { + dev_err(fbdev->dev, + "failed to allocate framebuffer\n"); + return -ENOMEM; + } + + rg->paddr = paddr; + rg->vaddr = vaddr; + rg->size = plat_rg->size; + rg->alloc = 1; + } else { + dev_err(fbdev->dev, + "Using preallocated fb not supported\n"); + return -EINVAL; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofb_info = FB2OFB(fbdev->fbs[i]); + struct omapfb_mem_region *rg; + rg = &ofb_info->region; + + DBG("region%d phys %08x virt %p size=%lu\n", + i, + rg->paddr, + rg->vaddr, + rg->size); + } + + return 0; +} + +/* initialize fb_info, var, fix to something sane based on the display */ +static int fbinfo_init(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + struct omap_display *display = fb2display(fbi); + int r = 0; + + if (!display) { + dev_err(fbdev->dev, "cannot fbinfo_init, no display\n"); + return -EINVAL; + } + + fbi->fbops = &omapfb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = fbdev->pseudo_palette; + + strncpy(fix->id, MODULE_NAME, sizeof(fix->id)); + + var->xres = display->x_res; + var->yres = display->y_res; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + /* var->rotate = def_rotate; */ + + var->nonstd = 0; + + switch (display->bpp) { + case 16: + var->bits_per_pixel = 16; + break; + case 18: + var->bits_per_pixel = 16; + break; + case 24: + var->bits_per_pixel = 32; + break; + default: + dev_err(fbdev->dev, "illegal display bpp\n"); + return -EINVAL; + } + + if (display->get_timings) { + struct omap_video_timings timings; + display->get_timings(display, &timings); + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = KHZ2PICOS(timings.pixel_clock); + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } else { + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + } + + r = check_fb_var(fbi, var); + if (r) + goto err; + + set_fb_fix(fbi); + +#ifdef DEBUG + fill_fb(FB2OFB(fbi)->region.vaddr, fbi); +#endif +err: + return r; +} + +static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + fb_dealloc_cmap(&fbi->cmap); +} + + +static void omapfb_free_resources(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free_resources\n"); + + if (fbdev == NULL) + return; + + for (i = 0; i < fbdev->num_fbs; i++) + unregister_framebuffer(fbdev->fbs[i]); + + /* free the reserved fbmem */ + omapfb_free_fbmem(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + fbinfo_cleanup(fbdev, fbdev->fbs[i]); + framebuffer_release(fbdev->fbs[i]); + } + + + for (i = 0; i < fbdev->num_displays; i++) { + if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) + fbdev->displays[i]->disable(fbdev->displays[i]); + + omap_dss_put_display(fbdev->displays[i]); + } + + dev_set_drvdata(fbdev->dev, NULL); + kfree(fbdev); +} + +static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) +{ + int r; + int i; + struct omapfb_mem_desc *plat_mem_desc; + struct omapfb_platform_data *pdata = fbdev->dev->platform_data; + + plat_mem_desc = &pdata->mem_desc; + + fbdev->num_fbs = 0; + + DBG("create %d framebuffers\n", plat_mem_desc->region_cnt); + + /* allocate fb_infos */ + for (i = 0; i < plat_mem_desc->region_cnt; i++) { + struct fb_info *fbi; + struct omapfb_info *ofbi; + + fbi = framebuffer_alloc(sizeof(struct omapfb_info), + fbdev->dev); + + if (fbi == NULL) { + dev_err(fbdev->dev, + "unable to allocate memory for plane info\n"); + return -ENOMEM; + } + + fbdev->fbs[i] = fbi; + + ofbi = FB2OFB(fbi); + ofbi->fbdev = fbdev; + /* XXX here we presume we have enough overlays */ + ofbi->overlays[0] = fbdev->overlays[i]; + ofbi->num_overlays = 1; + ofbi->id = i; + fbdev->num_fbs++; + } + + DBG("fb_infos allocated\n"); + + /* allocate fb memories */ + r = omapfb_allocate_fbmem(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to allocate fbmem\n"); + return r; + } + + DBG("fbmems allocated\n"); + + /* setup fb_infos */ + for (i = 0; i < fbdev->num_fbs; i++) { + r = fbinfo_init(fbdev, fbdev->fbs[i]); + if (r) { + dev_err(fbdev->dev, "failed to setup fb_info\n"); + return r; + } + } + + DBG("fb_infos initialized\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = register_framebuffer(fbdev->fbs[i]); + if (r != 0) { + dev_err(fbdev->dev, + "registering framebuffer %d failed\n", i); + return r; + } + } + + DBG("framebuffers registered\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_apply_changes(fbdev->fbs[i], 1); + if (r) + dev_err(fbdev->dev, "failed to change mode\n"); + } + + /* Enable the first framebuffer that has overlay that is connected + * to display. Usually this would be the GFX plane. */ + r = 0; + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + int t; + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl = ofbi->overlays[t]; + if (ovl->manager && ovl->manager->display) { + ovl->enable(ovl, 1); + r = 1; + break; + } + } + + if (r) + break; + } + + DBG("create_framebuffers done\n"); + + return 0; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = NULL; + int r = 0; + int i, t; + struct omap_overlay *ovl; + struct omap_display *def_display; + + DBG("omapfb_probe\n"); + + if (pdev->num_resources != 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + goto err0; + } + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + r = -ENOENT; + goto err0; + } + + fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); + if (fbdev == NULL) { + r = -ENOMEM; + goto err0; + } + + mutex_init(&fbdev->mtx); + + fbdev->dev = &pdev->dev; + platform_set_drvdata(pdev, fbdev); + + fbdev->num_displays = 0; + t = omap_dss_get_num_displays(); + for (i = 0; i < t; i++) { + struct omap_display *display; + display = omap_dss_get_display(i); + if (!display) { + dev_err(&pdev->dev, "can't get display %d\n", i); + r = -EINVAL; + goto cleanup; + } + + fbdev->displays[fbdev->num_displays++] = display; + } + + if (fbdev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + r = -EINVAL; + goto cleanup; + } + + fbdev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < fbdev->num_overlays; i++) + fbdev->overlays[i] = omap_dss_get_overlay(i); + + fbdev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < fbdev->num_managers; i++) + fbdev->managers[i] = omap_dss_get_overlay_manager(i); + + + /* gfx overlay should be the default one. find a display + * connected to that, and use it as default display */ + ovl = omap_dss_get_overlay(0); + if (ovl->manager && ovl->manager->display) { + def_display = ovl->manager->display; + } else { + dev_err(&pdev->dev, "cannot find default display\n"); + r = -EINVAL; + goto cleanup; + } + + r = omapfb_create_framebuffers(fbdev); + if (r) + goto cleanup; + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_overlay_manager *mgr; + mgr = fbdev->managers[i]; + r = mgr->apply(mgr); + if (r) { + dev_err(fbdev->dev, "failed to apply dispc config\n"); + goto cleanup; + } + } + + DBG("mgr->apply'ed\n"); + + r = def_display->enable(def_display); + if (r) { + dev_err(fbdev->dev, "Failed to enable display '%s'\n", + def_display->name); + goto cleanup; + } + + /* set the update mode */ + if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + if (def_display->enable_te) + def_display->enable_te(def_display, 1); +#else + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); + if (def_display->enable_te) + def_display->enable_te(def_display, 0); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + + for (i = 0; i < fbdev->num_displays; i++) { + struct omap_display *display = fbdev->displays[i]; + + if (display->update) + display->update(display, + 0, 0, + display->x_res, display->y_res); + } + + DBG("display->updated\n"); + + omapfb_create_sysfs(fbdev); + DBG("sysfs created\n"); + + return 0; + +cleanup: + omapfb_free_resources(fbdev); +err0: + dev_err(&pdev->dev, "failed to setup omapfb\n"); + return r; +} + +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + + /* FIXME: wait till completion of pending events */ + + omapfb_remove_sysfs(fbdev); + + omapfb_free_resources(fbdev); + + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .driver = { + .name = "omapfb", + .owner = THIS_MODULE, + }, +}; + +static int __init omapfb_init(void) +{ + DBG("omapfb_init\n"); + + if (platform_driver_register(&omapfb_driver)) { + printk(KERN_ERR "failed to register omapfb driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit omapfb_exit(void) +{ + DBG("omapfb_exit\n"); + platform_driver_unregister(&omapfb_driver); +} + +/* late_initcall to let panel/ctrl drivers loaded first. + * I guess better option would be a more dynamic approach, + * so that omapfb reacts to new panels when they are loaded */ +late_initcall(omapfb_init); +/*module_init(omapfb_init);*/ +module_exit(omapfb_exit); + +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/omap2/omapfb-sysfs.c b/drivers/video/omap2/omapfb-sysfs.c new file mode 100644 index 0000000..e01edd1 --- /dev/null +++ b/drivers/video/omap2/omapfb-sysfs.c @@ -0,0 +1,833 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "omapfb.h" + +static int omapfb_attach_framebuffer(struct fb_info *fbi, + struct omap_overlay *ovl) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int i, t; + int r; + + if (ofbi->num_overlays >= OMAPFB_MAX_OVL_PER_FB) { + dev_err(fbdev->dev, "fb has max number of overlays already\n"); + return -EINVAL; + } + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i] == ovl) { + dev_err(fbdev->dev, "fb already attached to overlay\n"); + return -EINVAL; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); + for (t = 0; t < ofbi2->num_overlays; t++) { + if (ofbi2->overlays[t] == ovl) { + dev_err(fbdev->dev, "overlay already in use\n"); + return -EINVAL; + } + } + } + + ofbi->overlays[ofbi->num_overlays++] = ovl; + +/* + if (ovl->manager && ovl->manager->display) + omapfb_adjust_fb(fbi, ovl, 0, 0); +*/ + r = omapfb_apply_changes(fbi, 1); + if (r) + return r; + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + return 0; +} + +static int omapfb_detach_framebuffer(struct fb_info *fbi, + struct omap_overlay *ovl) +{ + int i; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i] == ovl) + break; + } + + if (i == ofbi->num_overlays) { + dev_err(fbdev->dev, "cannot detach fb, overlay not attached\n"); + return -EINVAL; + } + + ovl->enable(ovl, 0); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + for (i = i + 1; i < ofbi->num_overlays; i++) + ofbi->overlays[i-1] = ofbi->overlays[i]; + + ofbi->num_overlays--; + + return 0; +} + + +static ssize_t show_framebuffers(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i, t; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + l += snprintf(buf + l, size - l, "%d t:", ofbi->id); + + if (ofbi->num_overlays == 0) + l += snprintf(buf + l, size - l, "none"); + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl; + ovl = ofbi->overlays[t]; + + l += snprintf(buf + l, size - l, "%s%s", + t == 0 ? "" : ",", + ovl->name); + } + + l += snprintf(buf + l, size - l, "\n"); + } + + omapfb_unlock(fbdev); + + return l; +} + +static struct omap_overlay *find_overlay_by_name(struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_overlays; i++) + if (strcmp(name, fbdev->overlays[i]->name) == 0) + return fbdev->overlays[i]; + + return NULL; +} + +static struct omap_display *find_display_by_name(struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_displays; i++) + if (strcmp(name, fbdev->displays[i]->name) == 0) + return fbdev->displays[i]; + + return NULL; +} + +static struct omap_overlay_manager *find_manager_by_name( + struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_managers; i++) + if (strcmp(name, fbdev->managers[i]->name) == 0) + return fbdev->managers[i]; + + return NULL; +} + +static int parse_overlays(struct omapfb2_device *fbdev, char *str, + struct omap_overlay *ovls[]) +{ + int num_ovls = 0; + int s, e = 0; + char ovlname[10]; + + while (1) { + struct omap_overlay *ovl; + + s = e; + + while (e < strlen(str) && str[e] != ',') + e++; + + strncpy(ovlname, str + s, e - s); + ovlname[e-s] = 0; + + DBG("searching for '%s'\n", ovlname); + ovl = find_overlay_by_name(fbdev, ovlname); + + if (ovl) { + DBG("found an overlay\n"); + ovls[num_ovls] = ovl; + num_ovls++; + } else { + DBG("unknown overlay %s\n", str); + return 0; + } + + if (e == strlen(str)) + break; + + e++; + } + + return num_ovls; +} + +static ssize_t store_framebuffers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + char fbname[3]; + unsigned long fbnum; + char ovlnames[40]; + int num_ovls = 0; + struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; + struct fb_info *fbi; + struct omapfb_info *ofbi; + int r, i; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(fbname)) + return -EINVAL; + + strncpy(fbname, buf, idx); + fbname[idx] = 0; + idx++; + + if (strict_strtoul(fbname, 10, &fbnum)) + return -EINVAL; + + r = sscanf(buf + idx, "t:%39s", ovlnames); + + if (r != 1) { + r = -EINVAL; + goto err; + } + + omapfb_lock(fbdev); + + if (fbnum >= fbdev->num_fbs) { + dev_err(dev, "fb not found\n"); + r = -EINVAL; + goto err; + } + + fbi = fbdev->fbs[fbnum]; + ofbi = FB2OFB(fbi); + + if (strcmp(ovlnames, "none") == 0) { + num_ovls = 0; + } else { + num_ovls = parse_overlays(fbdev, ovlnames, ovls); + + if (num_ovls == 0) { + dev_err(dev, "overlays not found\n"); + r = -EINVAL; + goto err; + } + } + + for (i = 0; i < ofbi->num_overlays; i++) { + r = omapfb_detach_framebuffer(fbi, ofbi->overlays[i]); + if (r) { + dev_err(dev, "detach failed\n"); + goto err; + } + } + + if (num_ovls > 0) { + for (i = 0; i < num_ovls; i++) { + r = omapfb_attach_framebuffer(fbi, ovls[i]); + if (r) { + dev_err(dev, "attach failed\n"); + goto err; + } + } + } + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_overlays(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i, mgr_num; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_overlays; i++) { + struct omap_overlay *ovl; + struct omap_overlay_manager *mgr; + + ovl = fbdev->overlays[i]; + mgr = ovl->manager; + + for (mgr_num = 0; mgr_num < fbdev->num_managers; mgr_num++) + if (fbdev->managers[mgr_num] == mgr) + break; + + l += snprintf(buf + l, size - l, + "%s t:%s x:%d y:%d iw:%d ih:%d w: %d h: %d e:%d\n", + ovl->name, + mgr ? mgr->name : "none", + ovl->info.pos_x, + ovl->info.pos_y, + ovl->info.width, + ovl->info.height, + ovl->info.out_width, + ovl->info.out_height, + ovl->info.enabled); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_overlays(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + struct omap_overlay *ovl = NULL; + struct omap_overlay_manager *mgr; + int r; + char ovlname[10]; + int posx, posy, outw, outh; + int enabled; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(ovlname)) + return -EINVAL; + + strncpy(ovlname, buf, idx); + ovlname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + ovl = find_overlay_by_name(fbdev, ovlname); + + if (!ovl) { + dev_err(dev, "ovl not found\n"); + r = -EINVAL; + goto err; + } + + DBG("ovl %s found\n", ovl->name); + + mgr = ovl->manager; + + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + outw = ovl->info.out_width; + outh = ovl->info.out_height; + enabled = ovl->info.enabled; + + while (idx < count) { + char c; + int val; + int len; + char sval[10]; + + r = sscanf(buf + idx, "%c:%d%n", &c, &val, &len); + + if (r != 2) { + val = 0; + + r = sscanf(buf + idx, "%c:%9s%n", &c, sval, &len); + + if (r != 2) { + dev_err(dev, "sscanf failed, aborting\n"); + r = -EINVAL; + goto err; + } + } else { + sval[0] = 0; + } + + switch (c) { + case 't': + if (strcmp(sval, "none") == 0) { + mgr = NULL; + } else { + mgr = find_manager_by_name(fbdev, sval); + + if (mgr == NULL) { + dev_err(dev, "no such manager\n"); + r = -EINVAL; + goto err; + } + + DBG("manager %s found\n", mgr->name); + } + + break; + + case 'x': + posx = val; + break; + + case 'y': + posy = val; + break; + + case 'w': + if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) + outw = val; + break; + + case 'h': + if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) + outh = val; + break; + + case 'e': + enabled = val; + break; + + default: + dev_err(dev, "unknown option %c\n", c); + r = -EINVAL; + goto err; + } + + idx += len + 1; + } + + r = ovl->setup_output(ovl, posx, posy, outw, outh); + + if (r) { + dev_err(dev, "setup overlay failed\n"); + goto err; + } + + if (mgr != ovl->manager) { + /* detach old manager */ + if (ovl->manager) { + r = ovl->unset_manager(ovl); + if (r) { + dev_err(dev, "detach failed\n"); + goto err; + } + } + + if (mgr) { + r = ovl->set_manager(ovl, mgr); + if (r) { + dev_err(dev, "Failed to attach overlay\n"); + goto err; + } + } + } + + r = ovl->enable(ovl, enabled); + + if (r) { + dev_err(dev, "enable overlay failed\n"); + goto err; + } + + if (mgr) { + r = mgr->apply(mgr); + if (r) { + dev_err(dev, "failed to apply dispc config\n"); + goto err; + } + } else { + ovl->enable(ovl, 0); + } + + if (mgr && mgr->display && mgr->display->update) + mgr->display->update(mgr->display, + 0, 0, + mgr->display->x_res, mgr->display->y_res); + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_managers(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_display *display; + struct omap_overlay_manager *mgr; + + mgr = fbdev->managers[i]; + display = mgr->display; + + l += snprintf(buf + l, size - l, "%s t:%s\n", + mgr->name, display ? display->name : "none"); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_managers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + struct omap_overlay_manager *mgr; + struct omap_display *display; + char mgrname[10]; + char displayname[10]; + int r; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(mgrname)) + return -EINVAL; + + strncpy(mgrname, buf, idx); + mgrname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + mgr = find_manager_by_name(fbdev, mgrname); + + if (!mgr) { + dev_err(dev, "manager not found\n"); + r = -EINVAL; + goto err; + } + + r = sscanf(buf + idx, "t:%9s", displayname); + + if (r != 1) { + r = -EINVAL; + goto err; + } + + if (strcmp(displayname, "none") == 0) { + display = NULL; + } else { + display = find_display_by_name(fbdev, displayname); + + if (!display) { + dev_err(dev, "display not found\n"); + r = -EINVAL; + goto err; + } + } + + if (mgr->display) { + r = mgr->unset_display(mgr); + if (r) { + dev_err(dev, "failed to unset display\n"); + goto err; + } + } + + if (display) { + r = mgr->set_display(mgr, display); + if (r) { + dev_err(dev, "failed to set manager\n"); + goto err; + } + + r = mgr->apply(mgr); + if (r) { + dev_err(dev, "failed to apply dispc config\n"); + goto err; + } + } + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_displays(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_displays; i++) { + struct omap_display *display; + enum omap_dss_update_mode mode = -1; + int te = 0; + + display = fbdev->displays[i]; + + if (display->get_update_mode) + mode = display->get_update_mode(display); + + if (display->get_te) + te = display->get_te(display); + + l += snprintf(buf + l, size - l, + "%s w:%d h:%d e:%d u:%d t:%d\n", + display->name, + display->x_res, + display->y_res, + display->state != OMAP_DSS_DISPLAY_DISABLED, + mode, te); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_displays(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + int enable, width, height; + enum omap_dss_update_mode mode; + struct omap_display *display = NULL; + int r; + char displayname[10]; + int te; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(displayname)) + return -EINVAL; + + strncpy(displayname, buf, idx); + displayname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + display = find_display_by_name(fbdev, displayname); + + if (!display) { + dev_err(dev, "display not found\n"); + r = -EINVAL; + goto err; + } + + width = display->x_res; + height = display->y_res; + enable = display->state != OMAP_DSS_DISPLAY_DISABLED; + if (display->get_update_mode) + mode = display->get_update_mode(display); + else + mode = 0; + + if (display->get_te) + te = display->get_te(display); + else + te = 0; + + while (idx < count) { + char c; + int val; + int len; + + r = sscanf(buf + idx, "%c:%d%n", &c, &val, &len); + + if (r != 2) { + dev_err(dev, "sscanf failed, aborting\n"); + r = -EINVAL; + goto err; + } + + switch (c) { + case 'w': + width = val; + break; + + case 'h': + height = val; + break; + + case 'e': + enable = val; + break; + + case 'u': + mode = val; + break; + + case 't': + te = val; + break; + + default: + dev_err(dev, "unknown option %c\n", c); + r = -EINVAL; + goto err; + } + + idx += len + 1; + } + + /* XXX: setmode */ + if (enable != (display->state != OMAP_DSS_DISPLAY_DISABLED)) { + if (enable) { + r = display->enable(display); + if (r) + dev_err(dev, "failed to enable display\n"); + } else { + display->disable(display); + } + } + + if (display->set_update_mode && display->get_update_mode) { + if (mode != display->get_update_mode(display)) + display->set_update_mode(display, mode); + } + + if (display->enable_te && display->get_te) { + if (te != display->get_te(display)) + display->enable_te(display, te); + } + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + + +static DEVICE_ATTR(framebuffers, S_IRUGO | S_IWUSR, + show_framebuffers, store_framebuffers); +static DEVICE_ATTR(overlays, S_IRUGO | S_IWUSR, + show_overlays, store_overlays); +static DEVICE_ATTR(managers, S_IRUGO | S_IWUSR, + show_managers, store_managers); +static DEVICE_ATTR(displays, S_IRUGO | S_IWUSR, + show_displays, store_displays); + +static struct attribute *omapfb_attrs[] = { + &dev_attr_framebuffers.attr, + &dev_attr_overlays.attr, + &dev_attr_managers.attr, + &dev_attr_displays.attr, + NULL, +}; + +static struct attribute_group omapfb_attr_group = { + .attrs = omapfb_attrs, +}; + +void omapfb_create_sysfs(struct omapfb2_device *fbdev) +{ + int r; + + r = sysfs_create_group(&fbdev->dev->kobj, &omapfb_attr_group); + if (r) + dev_err(fbdev->dev, "failed to create sysfs clk file\n"); +} + +void omapfb_remove_sysfs(struct omapfb2_device *fbdev) +{ + sysfs_remove_group(&fbdev->dev->kobj, &omapfb_attr_group); +} + diff --git a/drivers/video/omap2/omapfb.h b/drivers/video/omap2/omapfb.h new file mode 100644 index 0000000..04ca444 --- /dev/null +++ b/drivers/video/omap2/omapfb.h @@ -0,0 +1,104 @@ +/* + * linux/drivers/video/omap2/omapfb.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ +#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ + +#ifdef CONFIG_FB_OMAP2_DEBUG +#define DEBUG +#endif + +#ifdef DEBUG +#define DBG(format, ...) printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par)) + +/* max number of overlays to which a framebuffer data can be direct */ +#define OMAPFB_MAX_OVL_PER_FB 3 + +/* appended to fb_info */ +struct omapfb_info { + int id; + struct omapfb_mem_region region; + int num_overlays; + struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; + struct omapfb2_device *fbdev; +}; + +struct omapfb2_device { + struct device *dev; + struct mutex mtx; + + u32 pseudo_palette[17]; + + int state; + + int num_fbs; + struct fb_info *fbs[10]; + + int num_displays; + struct omap_display *displays[10]; + int num_overlays; + struct omap_overlay *overlays[10]; + int num_managers; + struct omap_overlay_manager *managers[10]; +}; + +int omapfb_apply_changes(struct fb_info *fbi, int init); +int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + int posx, int posy, int outw, int outh); + +void omapfb_create_sysfs(struct omapfb2_device *fbdev); +void omapfb_remove_sysfs(struct omapfb2_device *fbdev); + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg); + +/* find the display connected to this fb, if any */ +static inline struct omap_display *fb2display(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int i; + + /* XXX: returns the display connected to first attached overlay */ + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) + return ofbi->overlays[i]->manager->display; + } + + return NULL; +} + +static inline void omapfb_lock(struct omapfb2_device *fbdev) +{ + mutex_lock(&fbdev->mtx); +} + +static inline void omapfb_unlock(struct omapfb2_device *fbdev) +{ + mutex_unlock(&fbdev->mtx); +} + + +#endif -- 1.5.6.3