OMAP: DSS2: omapfb driver

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
This commit is contained in:
Tomi Valkeinen 2009-08-04 16:12:50 +03:00
parent 3de7a1dc0c
commit b39a982dde
11 changed files with 3813 additions and 3 deletions

View file

@ -55,6 +55,10 @@ static struct platform_device omap_fb_device = {
.num_resources = 0,
};
void omapfb_set_platform_data(struct omapfb_platform_data *data)
{
}
static inline int ranges_overlap(unsigned long start1, unsigned long size1,
unsigned long start2, unsigned long size2)
{
@ -327,7 +331,33 @@ static inline int omap_init_fb(void)
arch_initcall(omap_init_fb);
#else
#elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
static u64 omap_fb_dma_mask = ~(u32)0;
static struct omapfb_platform_data omapfb_config;
static struct platform_device omap_fb_device = {
.name = "omapfb",
.id = -1,
.dev = {
.dma_mask = &omap_fb_dma_mask,
.coherent_dma_mask = ~(u32)0,
.platform_data = &omapfb_config,
},
.num_resources = 0,
};
void omapfb_set_platform_data(struct omapfb_platform_data *data)
{
omapfb_config = *data;
}
static inline int omap_init_fb(void)
{
return platform_device_register(&omap_fb_device);
}
arch_initcall(omap_init_fb);
void omapfb_reserve_sdram(void) {}
unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
@ -339,5 +369,20 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
return 0;
}
#else
void omapfb_set_platform_data(struct omapfb_platform_data *data)
{
}
void omapfb_reserve_sdram(void) {}
unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
unsigned long sram_vstart,
unsigned long sram_size,
unsigned long start_avail,
unsigned long size_avail)
{
return 0;
}
#endif

View file

@ -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
@ -72,7 +73,7 @@ config FB_OMAP_LCD_MIPID
config FB_OMAP_BOOTLOADER_INIT
bool "Check bootloader initialization"
depends on FB_OMAP
depends on FB_OMAP || FB_OMAP2
help
Say Y here if you want to enable checking if the bootloader has
already initialized the display controller. In this case the

View file

@ -5,3 +5,4 @@ config OMAP2_VRFB
bool
source "drivers/video/omap2/dss/Kconfig"
source "drivers/video/omap2/omapfb/Kconfig"

View file

@ -2,3 +2,4 @@ obj-$(CONFIG_OMAP2_VRAM) += vram.o
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-y += dss/
obj-y += omapfb/

View file

@ -0,0 +1,37 @@
menuconfig FB_OMAP2
tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)"
depends on FB && OMAP2_DSS
select OMAP2_VRAM
select OMAP2_VRFB
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_SUPPORT
bool "Debug support for OMAP2/3 FB"
default y
depends on FB_OMAP2
help
Support for debug output. You have to enable the actual printing
with debug module parameter.
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.
config FB_OMAP2_NUM_FBS
int "Number of framebuffers"
range 1 10
default 3
depends on FB_OMAP2
help
Select the number of framebuffers created. OMAP2/3 has 3 overlays
so normally this would be 3.

View file

@ -0,0 +1,2 @@
obj-$(CONFIG_FB_OMAP2) += omapfb.o
omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o

View file

@ -0,0 +1,755 @@
/*
* 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 <linux/mm.h>
#include <linux/omapfb.h>
#include <linux/vmalloc.h>
#include <plat/display.h>
#include <plat/vrfb.h>
#include <plat/vram.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_overlay *ovl;
struct omap_overlay_info info;
int r = 0;
DBG("omapfb_setup_plane\n");
if (ofbi->num_overlays != 1) {
r = -EINVAL;
goto out;
}
/* XXX uses only the first overlay */
ovl = ofbi->overlays[0];
if (pi->enabled && !ofbi->region.size) {
/*
* This plane's memory was freed, can't enable it
* until it's reallocated.
*/
r = -EINVAL;
goto out;
}
ovl->get_overlay_info(ovl, &info);
info.pos_x = pi->pos_x;
info.pos_y = pi->pos_y;
info.out_width = pi->out_width;
info.out_height = pi->out_height;
info.enabled = pi->enabled;
r = ovl->set_overlay_info(ovl, &info);
if (r)
goto out;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
goto out;
}
out:
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);
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;
}
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 omapfb2_mem_region *rg;
int r, i;
size_t size;
if (mi->type > OMAPFB_MEMTYPE_MAX)
return -EINVAL;
size = PAGE_ALIGN(mi->size);
rg = &ofbi->region;
for (i = 0; i < ofbi->num_overlays; i++) {
if (ofbi->overlays[i]->info.enabled)
return -EBUSY;
}
if (rg->size != size || rg->type != mi->type) {
r = omapfb_realloc_fbmem(fbi, size, mi->type);
if (r) {
dev_err(fbdev->dev, "realloc fbmem failed\n");
return r;
}
}
return 0;
}
static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_mem_region *rg;
rg = &ofbi->region;
memset(mi, 0, sizeof(*mi));
mi->size = rg->size;
mi->type = rg->type;
return 0;
}
static int omapfb_update_window_nolock(struct fb_info *fbi,
u32 x, u32 y, u32 w, u32 h)
{
struct omap_dss_device *display = fb2display(fbi);
u16 dw, dh;
if (!display)
return 0;
if (w == 0 || h == 0)
return 0;
display->get_resolution(display, &dw, &dh);
if (x + w > dw || y + h > dh)
return -EINVAL;
return display->update(display, x, y, w, h);
}
/* This function is exported for SGX driver use */
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;
int r;
omapfb_lock(fbdev);
lock_fb_info(fbi);
r = omapfb_update_window_nolock(fbi, x, y, w, h);
unlock_fb_info(fbi);
omapfb_unlock(fbdev);
return r;
}
EXPORT_SYMBOL(omapfb_update_window);
static int omapfb_set_update_mode(struct fb_info *fbi,
enum omapfb_update_mode mode)
{
struct omap_dss_device *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;
}
r = display->set_update_mode(display, um);
return r;
}
static int omapfb_get_update_mode(struct fb_info *fbi,
enum omapfb_update_mode *mode)
{
struct omap_dss_device *display = fb2display(fbi);
enum omap_dss_update_mode m;
if (!display || !display->get_update_mode)
return -EINVAL;
m = display->get_update_mode(display);
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;
}
/* XXX this color key handling is a hack... */
static struct omapfb_color_key omapfb_color_keys[2];
static int _omapfb_set_color_key(struct omap_overlay_manager *mgr,
struct omapfb_color_key *ck)
{
struct omap_overlay_manager_info info;
enum omap_dss_trans_key_type kt;
int r;
mgr->get_manager_info(mgr, &info);
if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) {
info.trans_enabled = false;
omapfb_color_keys[mgr->id] = *ck;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
return r;
}
switch (ck->key_type) {
case OMAPFB_COLOR_KEY_GFX_DST:
kt = OMAP_DSS_COLOR_KEY_GFX_DST;
break;
case OMAPFB_COLOR_KEY_VID_SRC:
kt = OMAP_DSS_COLOR_KEY_VID_SRC;
break;
default:
return -EINVAL;
}
info.default_color = ck->background;
info.trans_key = ck->trans_key;
info.trans_key_type = kt;
info.trans_enabled = true;
omapfb_color_keys[mgr->id] = *ck;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
return r;
}
static int omapfb_set_color_key(struct fb_info *fbi,
struct omapfb_color_key *ck)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
int r;
int i;
struct omap_overlay_manager *mgr = NULL;
omapfb_lock(fbdev);
for (i = 0; i < ofbi->num_overlays; i++) {
if (ofbi->overlays[i]->manager) {
mgr = ofbi->overlays[i]->manager;
break;
}
}
if (!mgr) {
r = -EINVAL;
goto err;
}
r = _omapfb_set_color_key(mgr, ck);
err:
omapfb_unlock(fbdev);
return r;
}
static int omapfb_get_color_key(struct fb_info *fbi,
struct omapfb_color_key *ck)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_overlay_manager *mgr = NULL;
int r = 0;
int i;
omapfb_lock(fbdev);
for (i = 0; i < ofbi->num_overlays; i++) {
if (ofbi->overlays[i]->manager) {
mgr = ofbi->overlays[i]->manager;
break;
}
}
if (!mgr) {
r = -EINVAL;
goto err;
}
*ck = omapfb_color_keys[mgr->id];
err:
omapfb_unlock(fbdev);
return r;
}
static int omapfb_memory_read(struct fb_info *fbi,
struct omapfb_memory_read *mr)
{
struct omap_dss_device *display = fb2display(fbi);
void *buf;
int r;
if (!display || !display->memory_read)
return -ENOENT;
if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size))
return -EFAULT;
if (mr->w * mr->h * 3 > mr->buffer_size)
return -EINVAL;
buf = vmalloc(mr->buffer_size);
if (!buf) {
DBG("vmalloc failed\n");
return -ENOMEM;
}
r = display->memory_read(display, buf, mr->buffer_size,
mr->x, mr->y, mr->w, mr->h);
if (r > 0) {
if (copy_to_user(mr->buffer, buf, mr->buffer_size))
r = -EFAULT;
}
vfree(buf);
return r;
}
static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev,
struct omapfb_ovl_colormode *mode)
{
int ovl_idx = mode->overlay_idx;
int mode_idx = mode->mode_idx;
struct omap_overlay *ovl;
enum omap_color_mode supported_modes;
struct fb_var_screeninfo var;
int i;
if (ovl_idx >= fbdev->num_overlays)
return -ENODEV;
ovl = fbdev->overlays[ovl_idx];
supported_modes = ovl->supported_modes;
mode_idx = mode->mode_idx;
for (i = 0; i < sizeof(supported_modes) * 8; i++) {
if (!(supported_modes & (1 << i)))
continue;
/*
* It's possible that the FB doesn't support a mode
* that is supported by the overlay, so call the
* following here.
*/
if (dss_mode_to_fb_mode(1 << i, &var) < 0)
continue;
mode_idx--;
if (mode_idx < 0)
break;
}
if (i == sizeof(supported_modes) * 8)
return -ENOENT;
mode->bits_per_pixel = var.bits_per_pixel;
mode->nonstd = var.nonstd;
mode->red = var.red;
mode->green = var.green;
mode->blue = var.blue;
mode->transp = var.transp;
return 0;
}
static int omapfb_wait_for_go(struct fb_info *fbi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
int r = 0;
int i;
for (i = 0; i < ofbi->num_overlays; ++i) {
struct omap_overlay *ovl = ofbi->overlays[i];
r = ovl->wait_for_go(ovl);
if (r)
break;
}
return r;
}
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_dss_device *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;
struct omapfb_color_key color_key;
struct omapfb_ovl_colormode ovl_colormode;
enum omapfb_update_mode update_mode;
int test_num;
struct omapfb_memory_read memory_read;
struct omapfb_vram_info vram_info;
struct omapfb_tearsync_info tearsync_info;
} p;
int r = 0;
switch (cmd) {
case OMAPFB_SYNC_GFX:
DBG("ioctl SYNC_GFX\n");
if (!display || !display->sync) {
/* DSS1 never returns an error here, so we neither */
/*r = -EINVAL;*/
break;
}
r = display->sync(display);
break;
case OMAPFB_UPDATE_WINDOW_OLD:
DBG("ioctl UPDATE_WINDOW_OLD\n");
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_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y,
p.uwnd_o.width, p.uwnd_o.height);
break;
case OMAPFB_UPDATE_WINDOW:
DBG("ioctl UPDATE_WINDOW\n");
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_nolock(fbi, p.uwnd.x, p.uwnd.y,
p.uwnd.width, p.uwnd.height);
break;
case OMAPFB_SETUP_PLANE:
DBG("ioctl SETUP_PLANE\n");
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:
DBG("ioctl QUERY_PLANE\n");
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:
DBG("ioctl SETUP_MEM\n");
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:
DBG("ioctl QUERY_MEM\n");
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:
DBG("ioctl GET_CAPS\n");
if (!display) {
r = -EINVAL;
break;
}
memset(&p.caps, 0, sizeof(p.caps));
if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE;
if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM)
p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC;
if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
r = -EFAULT;
break;
case OMAPFB_GET_OVERLAY_COLORMODE:
DBG("ioctl GET_OVERLAY_COLORMODE\n");
if (copy_from_user(&p.ovl_colormode, (void __user *)arg,
sizeof(p.ovl_colormode))) {
r = -EFAULT;
break;
}
r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode);
if (r < 0)
break;
if (copy_to_user((void __user *)arg, &p.ovl_colormode,
sizeof(p.ovl_colormode)))
r = -EFAULT;
break;
case OMAPFB_SET_UPDATE_MODE:
DBG("ioctl SET_UPDATE_MODE\n");
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:
DBG("ioctl GET_UPDATE_MODE\n");
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;
case OMAPFB_SET_COLOR_KEY:
DBG("ioctl SET_COLOR_KEY\n");
if (copy_from_user(&p.color_key, (void __user *)arg,
sizeof(p.color_key)))
r = -EFAULT;
else
r = omapfb_set_color_key(fbi, &p.color_key);
break;
case OMAPFB_GET_COLOR_KEY:
DBG("ioctl GET_COLOR_KEY\n");
r = omapfb_get_color_key(fbi, &p.color_key);
if (r)
break;
if (copy_to_user((void __user *)arg, &p.color_key,
sizeof(p.color_key)))
r = -EFAULT;
break;
case OMAPFB_WAITFORVSYNC:
DBG("ioctl WAITFORVSYNC\n");
if (!display) {
r = -EINVAL;
break;
}
r = display->wait_vsync(display);
break;
case OMAPFB_WAITFORGO:
DBG("ioctl WAITFORGO\n");
if (!display) {
r = -EINVAL;
break;
}
r = omapfb_wait_for_go(fbi);
break;
/* LCD and CTRL tests do the same thing for backward
* compatibility */
case OMAPFB_LCD_TEST:
DBG("ioctl LCD_TEST\n");
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:
DBG("ioctl CTRL_TEST\n");
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_MEMORY_READ:
DBG("ioctl MEMORY_READ\n");
if (copy_from_user(&p.memory_read, (void __user *)arg,
sizeof(p.memory_read))) {
r = -EFAULT;
break;
}
r = omapfb_memory_read(fbi, &p.memory_read);
break;
case OMAPFB_GET_VRAM_INFO: {
unsigned long vram, free, largest;
DBG("ioctl GET_VRAM_INFO\n");
omap_vram_get_info(&vram, &free, &largest);
p.vram_info.total = vram;
p.vram_info.free = free;
p.vram_info.largest_free_block = largest;
if (copy_to_user((void __user *)arg, &p.vram_info,
sizeof(p.vram_info)))
r = -EFAULT;
break;
}
case OMAPFB_SET_TEARSYNC: {
DBG("ioctl SET_TEARSYNC\n");
if (copy_from_user(&p.tearsync_info, (void __user *)arg,
sizeof(p.tearsync_info))) {
r = -EFAULT;
break;
}
if (!display->enable_te) {
r = -ENODEV;
break;
}
r = display->enable_te(display, !!p.tearsync_info.enabled);
break;
}
default:
dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd);
r = -EINVAL;
}
if (r < 0)
DBG("ioctl failed: %d\n", r);
return r;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,507 @@
/*
* 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 <linux/kernel.h>
#include <linux/mm.h>
#include <linux/omapfb.h>
#include <plat/display.h>
#include <plat/vrfb.h>
#include "omapfb.h"
static ssize_t show_rotate_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type);
}
static ssize_t store_rotate_type(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
enum omap_dss_rotation_type rot_type;
int r;
rot_type = simple_strtoul(buf, NULL, 0);
if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB)
return -EINVAL;
lock_fb_info(fbi);
r = 0;
if (rot_type == ofbi->rotation_type)
goto out;
if (ofbi->region.size) {
r = -EBUSY;
goto out;
}
ofbi->rotation_type = rot_type;
/*
* Since the VRAM for this FB is not allocated at the moment we don't
* need to do any further parameter checking at this point.
*/
out:
unlock_fb_info(fbi);
return r ? r : count;
}
static ssize_t show_mirror(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror);
}
static ssize_t store_mirror(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
bool mirror;
int r;
struct fb_var_screeninfo new_var;
mirror = simple_strtoul(buf, NULL, 0);
if (mirror != 0 && mirror != 1)
return -EINVAL;
lock_fb_info(fbi);
ofbi->mirror = mirror;
memcpy(&new_var, &fbi->var, sizeof(new_var));
r = check_fb_var(fbi, &new_var);
if (r)
goto out;
memcpy(&fbi->var, &new_var, sizeof(fbi->var));
set_fb_fix(fbi);
r = omapfb_apply_changes(fbi, 0);
if (r)
goto out;
r = count;
out:
unlock_fb_info(fbi);
return r;
}
static ssize_t show_overlays(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
ssize_t l = 0;
int t;
omapfb_lock(fbdev);
lock_fb_info(fbi);
for (t = 0; t < ofbi->num_overlays; t++) {
struct omap_overlay *ovl = ofbi->overlays[t];
int ovlnum;
for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum)
if (ovl == fbdev->overlays[ovlnum])
break;
l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
t == 0 ? "" : ",", ovlnum);
}
l += snprintf(buf + l, PAGE_SIZE - l, "\n");
unlock_fb_info(fbi);
omapfb_unlock(fbdev);
return l;
}
static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev,
struct omap_overlay *ovl)
{
int i, t;
for (i = 0; i < fbdev->num_fbs; i++) {
struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
for (t = 0; t < ofbi->num_overlays; t++) {
if (ofbi->overlays[t] == ovl)
return ofbi;
}
}
return NULL;
}
static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB];
struct omap_overlay *ovl;
int num_ovls, r, i;
int len;
bool added = false;
num_ovls = 0;
len = strlen(buf);
if (buf[len - 1] == '\n')
len = len - 1;
omapfb_lock(fbdev);
lock_fb_info(fbi);
if (len > 0) {
char *p = (char *)buf;
int ovlnum;
while (p < buf + len) {
int found;
if (num_ovls == OMAPFB_MAX_OVL_PER_FB) {
r = -EINVAL;
goto out;
}
ovlnum = simple_strtoul(p, &p, 0);
if (ovlnum > fbdev->num_overlays) {
r = -EINVAL;
goto out;
}
found = 0;
for (i = 0; i < num_ovls; ++i) {
if (ovls[i] == fbdev->overlays[ovlnum]) {
found = 1;
break;
}
}
if (!found)
ovls[num_ovls++] = fbdev->overlays[ovlnum];
p++;
}
}
for (i = 0; i < num_ovls; ++i) {
struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]);
if (ofbi2 && ofbi2 != ofbi) {
dev_err(fbdev->dev, "overlay already in use\n");
r = -EINVAL;
goto out;
}
}
/* detach unused overlays */
for (i = 0; i < ofbi->num_overlays; ++i) {
int t, found;
ovl = ofbi->overlays[i];
found = 0;
for (t = 0; t < num_ovls; ++t) {
if (ovl == ovls[t]) {
found = 1;
break;
}
}
if (found)
continue;
DBG("detaching %d\n", ofbi->overlays[i]->id);
omapfb_overlay_enable(ovl, 0);
if (ovl->manager)
ovl->manager->apply(ovl->manager);
for (t = i + 1; t < ofbi->num_overlays; t++) {
ofbi->rotation[t-1] = ofbi->rotation[t];
ofbi->overlays[t-1] = ofbi->overlays[t];
}
ofbi->num_overlays--;
i--;
}
for (i = 0; i < num_ovls; ++i) {
int t, found;
ovl = ovls[i];
found = 0;
for (t = 0; t < ofbi->num_overlays; ++t) {
if (ovl == ofbi->overlays[t]) {
found = 1;
break;
}
}
if (found)
continue;
ofbi->rotation[ofbi->num_overlays] = 0;
ofbi->overlays[ofbi->num_overlays++] = ovl;
added = true;
}
if (added) {
r = omapfb_apply_changes(fbi, 0);
if (r)
goto out;
}
r = count;
out:
unlock_fb_info(fbi);
omapfb_unlock(fbdev);
return r;
}
static ssize_t show_overlays_rotate(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
ssize_t l = 0;
int t;
lock_fb_info(fbi);
for (t = 0; t < ofbi->num_overlays; t++) {
l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
t == 0 ? "" : ",", ofbi->rotation[t]);
}
l += snprintf(buf + l, PAGE_SIZE - l, "\n");
unlock_fb_info(fbi);
return l;
}
static ssize_t store_overlays_rotate(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
int num_ovls = 0, r, i;
int len;
bool changed = false;
u8 rotation[OMAPFB_MAX_OVL_PER_FB];
len = strlen(buf);
if (buf[len - 1] == '\n')
len = len - 1;
lock_fb_info(fbi);
if (len > 0) {
char *p = (char *)buf;
while (p < buf + len) {
int rot;
if (num_ovls == ofbi->num_overlays) {
r = -EINVAL;
goto out;
}
rot = simple_strtoul(p, &p, 0);
if (rot < 0 || rot > 3) {
r = -EINVAL;
goto out;
}
if (ofbi->rotation[num_ovls] != rot)
changed = true;
rotation[num_ovls++] = rot;
p++;
}
}
if (num_ovls != ofbi->num_overlays) {
r = -EINVAL;
goto out;
}
if (changed) {
for (i = 0; i < num_ovls; ++i)
ofbi->rotation[i] = rotation[i];
r = omapfb_apply_changes(fbi, 0);
if (r)
goto out;
/* FIXME error handling? */
}
r = count;
out:
unlock_fb_info(fbi);
return r;
}
static ssize_t show_size(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size);
}
static ssize_t store_size(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
unsigned long size;
int r;
int i;
size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0));
lock_fb_info(fbi);
for (i = 0; i < ofbi->num_overlays; i++) {
if (ofbi->overlays[i]->info.enabled) {
r = -EBUSY;
goto out;
}
}
if (size != ofbi->region.size) {
r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type);
if (r) {
dev_err(dev, "realloc fbmem failed\n");
goto out;
}
}
r = count;
out:
unlock_fb_info(fbi);
return r;
}
static ssize_t show_phys(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr);
}
static ssize_t show_virt(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr);
}
static struct device_attribute omapfb_attrs[] = {
__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
store_rotate_type),
__ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror),
__ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size),
__ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays),
__ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate,
store_overlays_rotate),
__ATTR(phys_addr, S_IRUGO, show_phys, NULL),
__ATTR(virt_addr, S_IRUGO, show_virt, NULL),
};
int omapfb_create_sysfs(struct omapfb2_device *fbdev)
{
int i;
int r;
DBG("create sysfs for fbs\n");
for (i = 0; i < fbdev->num_fbs; i++) {
int t;
for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) {
r = device_create_file(fbdev->fbs[i]->dev,
&omapfb_attrs[t]);
if (r) {
dev_err(fbdev->dev, "failed to create sysfs "
"file\n");
return r;
}
}
}
return 0;
}
void omapfb_remove_sysfs(struct omapfb2_device *fbdev)
{
int i, t;
DBG("remove sysfs for fbs\n");
for (i = 0; i < fbdev->num_fbs; i++) {
for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++)
device_remove_file(fbdev->fbs[i]->dev,
&omapfb_attrs[t]);
}
}

View file

@ -0,0 +1,146 @@
/*
* 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_SUPPORT
#define DEBUG
#endif
#include <plat/display.h>
#ifdef DEBUG
extern unsigned int omapfb_debug;
#define DBG(format, ...) \
if (omapfb_debug) \
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
struct omapfb2_mem_region {
u32 paddr;
void __iomem *vaddr;
struct vrfb vrfb;
unsigned long size;
u8 type; /* OMAPFB_PLANE_MEM_* */
bool alloc; /* allocated by the driver */
bool map; /* kernel mapped by the driver */
};
/* appended to fb_info */
struct omapfb_info {
int id;
struct omapfb2_mem_region region;
atomic_t map_count;
int num_overlays;
struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
struct omapfb2_device *fbdev;
enum omap_dss_rotation_type rotation_type;
u8 rotation[OMAPFB_MAX_OVL_PER_FB];
bool mirror;
};
struct omapfb2_device {
struct device *dev;
struct mutex mtx;
u32 pseudo_palette[17];
int state;
unsigned num_fbs;
struct fb_info *fbs[10];
unsigned num_displays;
struct omap_dss_device *displays[10];
unsigned num_overlays;
struct omap_overlay *overlays[10];
unsigned num_managers;
struct omap_overlay_manager *managers[10];
};
struct omapfb_colormode {
enum omap_color_mode dssmode;
u32 bits_per_pixel;
u32 nonstd;
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
};
void set_fb_fix(struct fb_info *fbi);
int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var);
int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type);
int omapfb_apply_changes(struct fb_info *fbi, int init);
int 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);
int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
struct fb_var_screeninfo *var);
/* find the display connected to this fb, if any */
static inline struct omap_dss_device *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->device;
}
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);
}
static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
int enable)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = enable;
return ovl->set_overlay_info(ovl, &info);
}
#endif

View file

@ -24,6 +24,7 @@
#ifndef __LINUX_OMAPFB_H__
#define __LINUX_OMAPFB_H__
#include <linux/fb.h>
#include <linux/ioctl.h>
#include <linux/types.h>
@ -50,6 +51,12 @@
#define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window)
#define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info)
#define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info)
#define OMAPFB_WAITFORVSYNC OMAP_IO(57)
#define OMAPFB_MEMORY_READ OMAP_IOR(58, struct omapfb_memory_read)
#define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode)
#define OMAPFB_WAITFORGO OMAP_IO(60)
#define OMAPFB_GET_VRAM_INFO OMAP_IOR(61, struct omapfb_vram_info)
#define OMAPFB_SET_TEARSYNC OMAP_IOW(62, struct omapfb_tearsync_info)
#define OMAPFB_CAPS_GENERIC_MASK 0x00000fff
#define OMAPFB_CAPS_LCDC_MASK 0x00fff000
@ -87,6 +94,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 {
@ -158,6 +172,40 @@ enum omapfb_update_mode {
OMAPFB_MANUAL_UPDATE
};
struct omapfb_memory_read {
__u16 x;
__u16 y;
__u16 w;
__u16 h;
size_t buffer_size;
void __user *buffer;
};
struct omapfb_ovl_colormode {
__u8 overlay_idx;
__u8 mode_idx;
__u32 bits_per_pixel;
__u32 nonstd;
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
};
struct omapfb_vram_info {
__u32 total;
__u32 free;
__u32 largest_free_block;
__u32 reserved[5];
};
struct omapfb_tearsync_info {
__u8 enabled;
__u8 reserved1[3];
__u16 line;
__u16 reserved2;
};
#ifdef __KERNEL__
#include <plat/board.h>
@ -173,6 +221,11 @@ struct omapfb_mem_region {
void __iomem *vaddr;
unsigned long size;
u8 type; /* OMAPFB_PLANE_MEM_* */
enum omapfb_color_format format;/* OMAPFB_COLOR_* */
unsigned format_used:1; /* Must be set when format is set.
* Needed b/c of the badly chosen 0
* base for OMAPFB_COLOR_* values
*/
unsigned alloc:1; /* allocated by the driver */
unsigned map:1; /* kernel mapped by the driver */
};
@ -189,6 +242,7 @@ struct omapfb_platform_data {
};
/* in arch/arm/plat-omap/fb.c */
extern void omapfb_set_platform_data(struct omapfb_platform_data *data);
extern void omapfb_set_ctrl_platform_data(void *pdata);
extern void omapfb_reserve_sdram(void);