From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
To: linux-fbdev-devel@lists.sourceforge.net
Cc: linux-omap@vger.kernel.org
Subject: [REVIEW PATCH 6/9] DSS: OMAPFB: fb driver for new display subsystem
Date: Tue, 04 Nov 2008 18:10:25 +0200 [thread overview]
Message-ID: <20081104161025.19071.96754.stgit@tubuntu> (raw)
In-Reply-To: <20081104160946.19071.44903.stgit@tubuntu>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
---
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 | 1247 ++++++++++++++++++++++++++++++
drivers/video/omap2/omapfb-sysfs.c | 833 ++++++++++++++++++++
drivers/video/omap2/omapfb.h | 104 +++
11 files changed, 2663 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 <mach/sram.h>
#include <mach/omapfb.h>
-#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 a3c4408..e69c0b1 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 0f13448..4b45731 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2084,6 +2084,7 @@ config FB_METRONOME
and could also have been called by some vendors as PVI-nnnn.
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 248bddc..4d69355 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
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig
index 5ebd591..8b6c675 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
@@ -76,7 +77,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 <tomi.valkeinen@nokia.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#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..7ef7080
--- /dev/null
+++ b/drivers/video/omap2/omapfb-main.c
@@ -0,0 +1,1247 @@
+/*
+ * linux/drivers/video/omap2/omapfb-main.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#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;
+}
+
+/* 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)
+{
+ DBG("pan_display(%d)\n", FB2OFB(fbi)->id);
+ return 0;
+}
+
+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 <tomi.valkeinen@nokia.com>");
+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 <tomi.valkeinen@nokia.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#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 <tomi.valkeinen@nokia.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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
next prev parent reply other threads:[~2008-11-04 16:10 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-11-04 16:09 [REVIEW PATCH 0/9] DSS: Series description Tomi Valkeinen
2008-11-04 16:09 ` [REVIEW PATCH 1/9] DSS: Documentation for OMAP2/3 display subsystem Tomi Valkeinen
2008-11-05 7:56 ` [Linux-fbdev-devel] " Geert Uytterhoeven
2008-11-05 10:12 ` Tomi Valkeinen
2008-11-04 16:09 ` [REVIEW PATCH 2/9] DSS: New display subsystem driver for OMAP2/3 Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 3/9] DSS: RFBI support for OMAP2/3 DSS Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 4/9] DSS: TV-out " Tomi Valkeinen
2008-11-05 10:27 ` Jarkko Nikula
2008-11-04 16:10 ` [REVIEW PATCH 5/9] DSS: DSI " Tomi Valkeinen
2008-11-04 16:10 ` Tomi Valkeinen [this message]
2008-11-04 16:10 ` [REVIEW PATCH 7/9] DSS: Add generic DVI panel Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 8/9] DSS: support for Beagle Board Tomi Valkeinen
2008-11-04 17:28 ` Koen Kooi
2008-11-05 10:05 ` Tomi Valkeinen
2008-11-05 21:15 ` Koen Kooi
2008-11-04 18:24 ` [Linux-fbdev-devel] " Tony Lindgren
2008-11-05 10:09 ` Tomi Valkeinen
2008-11-04 21:35 ` FSUSB Register access York, Jeffrey-P56387
2008-11-04 22:20 ` Felipe Balbi
2008-11-05 0:38 ` York, Jeffrey-P56387
2008-11-05 0:55 ` Felipe Balbi
2008-11-05 1:03 ` York, Jeffrey-P56387
2008-11-05 1:11 ` Felipe Balbi
2008-11-04 22:35 ` Woodruff, Richard
2008-11-05 10:27 ` [REVIEW PATCH 8/9] DSS: support for Beagle Board Jarkko Nikula
2008-11-05 23:21 ` David Brownell
2008-11-06 8:23 ` Tomi Valkeinen
2008-11-06 8:30 ` Koen Kooi
2008-11-04 16:10 ` [REVIEW PATCH 9/9] DSS: support for OMAP3 SDP board Tomi Valkeinen
2008-11-05 10:54 ` Jarkko Nikula
2008-11-10 4:03 ` [Linux-fbdev-devel] [REVIEW PATCH 0/9] DSS: Series description Shah, Hardik
2008-11-10 11:31 ` Tomi Valkeinen
2008-11-10 12:03 ` Shah, Hardik
2008-11-18 6:40 ` Shah, Hardik
2008-11-18 12:06 ` Tomi Valkeinen
2008-11-24 6:26 ` Arun KS
2008-11-24 6:30 ` Hiremath, Vaibhav
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081104161025.19071.96754.stgit@tubuntu \
--to=tomi.valkeinen@nokia.com \
--cc=linux-fbdev-devel@lists.sourceforge.net \
--cc=linux-omap@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.