summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-08-31 16:45:55 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:36:05 -0800
commit9959643c6064aa0004d6de393dd9921bb6cc3cf2 (patch)
treeea21088e48234fc5d1a836bcbee3458bc98e0345
parentd91682bc73fa7b86a0db3a1d39e6735498df9ec8 (diff)
video: tegra: add host1x support to driver
includes changes by: Antti Hatala <ahatala@nvidia.com> (1) Gary King <gking@nvidia.com> (1) Erik Gilling <konkers@android.com> (2) Antti Hatala <ahatala@nvidia.com> video: tegra: dc: use nvhost driver for host1x power management Gary King <gking@nvidia.com> video: tegra: fb: add ioctl to flip dc windows to nvmap handles Erik Gilling <konkers@android.com> video: tegra: dc: increment syncpoints following window flips video: tegra: drain syncpt waits on display disable Original commit messages: ** video: tegra: dc: use nvhost driver for host1x power management Incrementing the frame done syncpoint value from the display interrupt requires that the host module is powered on. As the syncpoint state is saved and restored automatically by the host driver a cpu increment of a syncpoint in the powered down state will be lost. Also adds checks for host module being powered. ** video: tegra: fb: add ioctl to flip dc windows to nvmap handles tegra user-space graphics drivers may allocate framebuffers using nvmap rather than rendering to the common framebuffer, this may be done to support deeper buffer pipelining, color formats and pixel layouts other than the initial bootup framebuffer, etc. to use this ioctl, a caller must first specify an nvmap fd which is already open in the calling process so that the subsequent flip ioctls may be properly validated. flips are performed asynchronously, with flip completion notifications provided back to the caller via the host1x syncpoint mechanism based on earlier changes made by Antti Hatala <ahatala@nvidia.com> and Erik Gilling <konkers@android.com> Change-Id: I4e8a8bb92085a485d65fd87d89112b2969ee37ff Signed-off-by: Gary King <gking@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h7
-rw-r--r--drivers/video/tegra/dc/dc.c81
-rw-r--r--drivers/video/tegra/dc/dc_priv.h18
-rw-r--r--drivers/video/tegra/fb.c323
-rw-r--r--include/video/tegrafb.h83
5 files changed, 488 insertions, 24 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index ce562e5634f7..49c09a177d00 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -86,6 +86,7 @@ struct tegra_dc_out {
#define TEGRA_DC_ORDER_BLUE_RED 1
struct tegra_dc;
+struct nvmap_handle_ref;
struct tegra_dc_win {
u8 idx;
@@ -108,6 +109,8 @@ struct tegra_dc_win {
int dirty;
struct tegra_dc *dc;
+
+ struct nvmap_handle_ref *surface;
};
#define TEGRA_WIN_FLAG_ENABLED (1 << 0)
@@ -165,6 +168,10 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
void tegra_dc_enable(struct tegra_dc *dc);
void tegra_dc_disable(struct tegra_dc *dc);
+u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc);
+u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc);
+void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val);
+
/* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
* with differenct dcs in one call
*/
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index fadecf4796e1..16e6a454336d 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -102,6 +102,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
int i;
char buff[256];
+ tegra_dc_io_start(dc);
+
DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
DUMP_REG(DC_CMD_DISPLAY_COMMAND);
DUMP_REG(DC_CMD_SIGNAL_RAISE);
@@ -225,6 +227,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
DUMP_REG(DC_WINBUF_ADDR_H_OFFSET);
DUMP_REG(DC_WINBUF_ADDR_V_OFFSET);
}
+
+ tegra_dc_io_end(dc);
}
#undef DUMP_REG
@@ -285,7 +289,6 @@ static void tegra_dc_dbg_add(struct tegra_dc *dc)
snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->ndev->id);
(void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops);
-
}
#else
static void tegra_dc_dbg_add(struct tegra_dc *dc) {}
@@ -533,13 +536,40 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
}
tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
-
mutex_unlock(&dc->lock);
return 0;
}
EXPORT_SYMBOL(tegra_dc_update_windows);
+u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc)
+{
+ return dc->syncpt_id;
+}
+EXPORT_SYMBOL(tegra_dc_get_syncpt_id);
+
+u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc)
+{
+ u32 max;
+
+ mutex_lock(&dc->lock);
+ max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, dc->syncpt_id, 1);
+ dc->syncpt_max = max;
+ mutex_unlock(&dc->lock);
+
+ return max;
+}
+
+void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val)
+{
+ mutex_lock(&dc->lock);
+ while (dc->syncpt_min < val) {
+ dc->syncpt_min++;
+ nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id);
+ }
+ mutex_unlock(&dc->lock);
+}
+
static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
int n)
{
@@ -769,11 +799,18 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc)
static void tegra_dc_init(struct tegra_dc *dc)
{
+ u32 disp_syncpt;
+ u32 vblank_syncpt;
+
tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
- if (dc->ndev->id == 0)
- tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
- else
- tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC);
+ if (dc->ndev->id == 0) {
+ disp_syncpt = NVSYNCPT_DISP0;
+ vblank_syncpt = NVSYNCPT_VBLANK0;
+ } else if (dc->ndev->id == 1) {
+ disp_syncpt = NVSYNCPT_DISP1;
+ vblank_syncpt = NVSYNCPT_VBLANK1;
+ }
+ tegra_dc_writel(dc, 0x00000100 | vblank_syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE);
tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY);
tegra_dc_writel(dc, 0x00000020, DC_DISP_MEM_HIGH_PRIORITY);
@@ -786,18 +823,24 @@ static void tegra_dc_init(struct tegra_dc *dc)
tegra_dc_set_color_control(dc);
+ dc->syncpt_id = disp_syncpt;
+
+ dc->syncpt_min = dc->syncpt_max =
+ nvhost_syncpt_read(&dc->ndev->host->syncpt, disp_syncpt);
+
if (dc->mode.pclk)
tegra_dc_program_mode(dc, &dc->mode);
}
static void _tegra_dc_enable(struct tegra_dc *dc)
{
+ tegra_dc_io_start(dc);
+
if (dc->out && dc->out->enable)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
- clk_enable(dc->host1x_clk);
clk_enable(dc->clk);
tegra_periph_reset_deassert(dc->clk);
enable_irq(dc->irq);
@@ -831,10 +874,17 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
disable_irq(dc->irq);
tegra_periph_reset_assert(dc->clk);
clk_disable(dc->clk);
- clk_disable(dc->host1x_clk);
if (dc->out && dc->out->disable)
dc->out->disable();
+
+ /* flush any pending syncpt waits */
+ while (dc->syncpt_min < dc->syncpt_max) {
+ dc->syncpt_min++;
+ nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id);
+ }
+
+ tegra_dc_io_end(dc);
}
@@ -854,7 +904,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
{
struct tegra_dc *dc;
struct clk *clk;
- struct clk *host1x_clk;
struct resource *res;
struct resource *base_res;
struct resource *fb_mem = NULL;
@@ -904,23 +953,14 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
- host1x_clk = clk_get(&ndev->dev, "host1x");
- if (IS_ERR_OR_NULL(host1x_clk)) {
- dev_err(&ndev->dev, "can't get host1x clock\n");
- ret = -ENOENT;
- goto err_iounmap_reg;
- }
-
clk = clk_get(&ndev->dev, NULL);
if (IS_ERR_OR_NULL(clk)) {
dev_err(&ndev->dev, "can't get clock\n");
ret = -ENOENT;
-
- goto err_put_host1x_clk;
+ goto err_iounmap_reg;
}
dc->clk = clk;
- dc->host1x_clk = host1x_clk;
dc->base_res = base_res;
dc->base = base;
dc->irq = irq;
@@ -984,8 +1024,6 @@ err_free_irq:
free_irq(irq, dc);
err_put_clk:
clk_put(clk);
-err_put_host1x_clk:
- clk_put(host1x_clk);
err_iounmap_reg:
iounmap(base);
if (fb_mem)
@@ -1014,7 +1052,6 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
free_irq(dc->irq, dc);
clk_put(dc->clk);
- clk_put(dc->host1x_clk);
iounmap(dc->base);
if (dc->fb_mem)
release_resource(dc->base_res);
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 2297be40f030..39a03e8fb9c0 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -22,6 +22,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/wait.h>
+#include "../host/dev.h"
struct tegra_dc;
@@ -54,7 +55,6 @@ struct tegra_dc {
int irq;
struct clk *clk;
- struct clk *host1x_clk;
bool enabled;
@@ -74,17 +74,33 @@ struct tegra_dc {
struct resource *fb_mem;
struct tegra_fb_info *fb;
+
+ u32 syncpt_id;
+ u32 syncpt_min;
+ u32 syncpt_max;
};
+static inline void tegra_dc_io_start(struct tegra_dc *dc)
+{
+ nvhost_module_busy(&dc->ndev->host->mod);
+}
+
+static inline void tegra_dc_io_end(struct tegra_dc *dc)
+{
+ nvhost_module_idle(&dc->ndev->host->mod);
+}
+
static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
unsigned long reg)
{
+ BUG_ON(!nvhost_module_powered(&dc->ndev->host->mod));
return readl(dc->base + reg * 4);
}
static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
unsigned long reg)
{
+ BUG_ON(!nvhost_module_powered(&dc->ndev->host->mod));
writel(val, dc->base + reg * 4);
}
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
index 7e4ffa8ba0b9..1656a6e49279 100644
--- a/drivers/video/tegra/fb.c
+++ b/drivers/video/tegra/fb.c
@@ -23,13 +23,22 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
+#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/workqueue.h>
#include <asm/atomic.h>
+#include <video/tegrafb.h>
+
#include <mach/dc.h>
#include <mach/fb.h>
#include <mach/nvhost.h>
+#include <mach/nvmap.h>
+
+#include "host/dev.h"
+#include "nvmap/nvmap.h"
struct tegra_fb_info {
struct tegra_dc_win *win;
@@ -43,6 +52,24 @@ struct tegra_fb_info {
int yres;
atomic_t in_use;
+ struct nvmap_client *user_nvmap;
+ struct nvmap_client *fb_nvmap;
+
+ struct workqueue_struct *flip_wq;
+};
+
+struct tegra_fb_flip_win {
+ struct tegra_dc_win win_data;
+ struct tegra_dc_win *dc_win;
+ s32 pre_syncpt_id;
+ u32 pre_syncpt_val;
+};
+
+struct tegra_fb_flip_data {
+ struct work_struct work;
+ struct tegra_fb_info *fb;
+ struct tegra_fb_flip_win windows[TEGRA_FB_FLIP_N_WINDOWS];
+ u32 syncpt_max;
};
/* palette array used by the fbcon */
@@ -55,6 +82,8 @@ static int tegra_fb_open(struct fb_info *info, int user)
if (atomic_xchg(&tegra_fb->in_use, 1))
return -EBUSY;
+ tegra_fb->user_nvmap = NULL;
+
return 0;
}
@@ -62,6 +91,13 @@ static int tegra_fb_release(struct fb_info *info, int user)
{
struct tegra_fb_info *tegra_fb = info->par;
+ flush_workqueue(tegra_fb->flip_wq);
+
+ if (tegra_fb->user_nvmap) {
+ nvmap_client_put(tegra_fb->user_nvmap);
+ tegra_fb->user_nvmap = NULL;
+ }
+
WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
return 0;
@@ -215,6 +251,12 @@ static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
tegra_dc_update_windows(&tegra_fb->win, 1);
tegra_dc_sync_windows(&tegra_fb->win, 1);
+ if (WARN_ON(tegra_fb->win->surface)) {
+ nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->surface);
+ nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->surface);
+ tegra_fb->win->surface = NULL;
+ }
+
return 0;
}
@@ -236,6 +278,255 @@ static void tegra_fb_imageblit(struct fb_info *info,
cfb_imageblit(info, image);
}
+/* TODO: implement ALLOC, FREE, BLANK ioctls */
+
+static int tegra_fb_set_nvmap_fd(struct tegra_fb_info *tegra_fb, int fd)
+{
+ struct nvmap_client *nvmap = NULL;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ nvmap = nvmap_client_get_file(fd);
+ if (IS_ERR(nvmap))
+ return PTR_ERR(nvmap);
+
+ if (tegra_fb->user_nvmap)
+ nvmap_client_put(tegra_fb->user_nvmap);
+
+ tegra_fb->user_nvmap = nvmap;
+
+ return 0;
+}
+
+static int tegra_fb_set_windowattr(struct tegra_fb_info *tegra_fb,
+ struct tegra_dc_win *win,
+ const struct tegra_fb_windowattr *attr)
+{
+ struct nvmap_handle_ref *r_dupe;
+ struct nvmap_handle *h_win;
+
+ if (!attr->buff_id) {
+ win->flags = 0;
+ win->surface = NULL;
+ return 0;
+ }
+
+ h_win = nvmap_get_handle_id(tegra_fb->user_nvmap, attr->buff_id);
+ if (h_win == NULL) {
+ dev_err(&tegra_fb->ndev->dev, "%s: flip invalid "
+ "handle %08x\n", current->comm, attr->buff_id);
+ return -EPERM;
+ }
+
+ /* duplicate the new framebuffer's handle into the fb driver's
+ * nvmap context, to ensure that the handle won't be freed as
+ * long as it is in-use by the fb driver */
+ r_dupe = nvmap_duplicate_handle_id(tegra_fb->fb_nvmap, attr->buff_id);
+ nvmap_handle_put(h_win);
+
+ if (IS_ERR(r_dupe)) {
+ dev_err(&tegra_fb->ndev->dev, "couldn't duplicate handle\n");
+ return PTR_ERR(r_dupe);
+ }
+
+ win->surface = r_dupe;
+
+ win->flags = TEGRA_WIN_FLAG_ENABLED;
+ if (attr->blend == TEGRA_FB_WIN_BLEND_PREMULT)
+ win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT;
+ else if (attr->blend == TEGRA_FB_WIN_BLEND_COVERAGE)
+ win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE;
+ win->fmt = attr->pixformat;
+ win->x = attr->x;
+ win->y = attr->y;
+ win->w = attr->w;
+ win->h = attr->h;
+ win->out_x = attr->out_x;
+ win->out_y = attr->out_y;
+ win->out_w = attr->out_w;
+ win->out_h = attr->out_h;
+ win->z = attr->z;
+
+ win->phys_addr = nvmap_pin(tegra_fb->fb_nvmap, r_dupe);
+ if (IS_ERR((void *)win->phys_addr)) {
+ dev_err(&tegra_fb->ndev->dev, "couldn't pin handle\n");
+ nvmap_free(tegra_fb->fb_nvmap, r_dupe);
+ return (int)win->phys_addr;
+ }
+ /* STOPSHIP verify that this won't read outside of the surface */
+ win->phys_addr += attr->offset;
+ win->stride = attr->stride;
+
+ return 0;
+}
+
+static void tegra_fb_flip_work(struct work_struct *work)
+{
+ struct tegra_fb_flip_data *data;
+ struct tegra_dc_win *wins[TEGRA_FB_FLIP_N_WINDOWS];
+ struct nvmap_handle_ref *surfs[TEGRA_FB_FLIP_N_WINDOWS];
+ int i, nr_win = 0, nr_unpin = 0;
+
+ data = container_of(work, struct tegra_fb_flip_data, work);
+
+ for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
+ struct tegra_fb_flip_win *flip_win = &data->windows[i];
+
+ if (!flip_win->dc_win)
+ continue;
+
+ if (flip_win->dc_win->flags && flip_win->dc_win->surface)
+ surfs[nr_unpin++] = flip_win->dc_win->surface;
+
+ wins[nr_win++] = flip_win->dc_win;
+
+ flip_win->dc_win->flags = flip_win->win_data.flags;
+ if (!flip_win->dc_win->flags)
+ continue;
+
+ flip_win->dc_win->surface = flip_win->win_data.surface;
+ flip_win->dc_win->fmt = flip_win->win_data.fmt;
+ flip_win->dc_win->x = flip_win->win_data.x;
+ flip_win->dc_win->y = flip_win->win_data.y;
+ flip_win->dc_win->w = flip_win->win_data.w;
+ flip_win->dc_win->h = flip_win->win_data.h;
+ flip_win->dc_win->out_x = flip_win->win_data.out_x;
+ flip_win->dc_win->out_y = flip_win->win_data.out_y;
+ flip_win->dc_win->out_w = flip_win->win_data.out_w;
+ flip_win->dc_win->out_h = flip_win->win_data.out_h;
+ flip_win->dc_win->z = flip_win->win_data.z;
+ flip_win->dc_win->phys_addr = flip_win->win_data.phys_addr;
+ flip_win->dc_win->stride = flip_win->win_data.stride;
+
+ if (flip_win->pre_syncpt_id < 0)
+ continue;
+
+ nvhost_syncpt_wait_timeout(&data->fb->ndev->host->syncpt,
+ flip_win->pre_syncpt_id,
+ flip_win->pre_syncpt_val,
+ msecs_to_jiffies(500));
+ }
+
+ if (!nr_win)
+ goto free_data;
+
+ tegra_dc_update_windows(wins, nr_win);
+ /* TODO: implement swapinterval here */
+ tegra_dc_sync_windows(wins, nr_win);
+
+ tegra_dc_incr_syncpt_min(data->fb->win->dc, data->syncpt_max);
+
+ /* unpin and deref previous front buffers */
+ for (i = 0; i < nr_unpin; i++) {
+ nvmap_unpin(data->fb->fb_nvmap, surfs[i]);
+ nvmap_free(data->fb->fb_nvmap, surfs[i]);
+ }
+
+free_data:
+ kfree(data);
+}
+
+static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
+ struct tegra_fb_flip_args *args)
+{
+ struct tegra_fb_flip_data *data;
+ struct tegra_fb_flip_win *flip_win;
+ struct tegra_dc *dc = tegra_fb->win->dc;
+ u32 syncpt_max;
+ int i, err;
+
+ if (WARN_ON(!tegra_fb->user_nvmap))
+ return -EFAULT;
+
+ if (WARN_ON(!tegra_fb->ndev))
+ return -EFAULT;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&tegra_fb->ndev->dev, "no memory for flip\n");
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&data->work, tegra_fb_flip_work);
+ data->fb = tegra_fb;
+
+ for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
+
+ flip_win = &data->windows[i];
+ flip_win->dc_win = tegra_dc_get_window(dc, args->win[i].index);
+ flip_win->pre_syncpt_id = args->win[i].pre_syncpt_id;
+ flip_win->pre_syncpt_val = args->win[i].pre_syncpt_val;
+
+ if (!flip_win->dc_win)
+ continue;
+
+ err = tegra_fb_set_windowattr(tegra_fb, &flip_win->win_data,
+ &args->win[i]);
+ if (err) {
+ dev_err(&tegra_fb->ndev->dev, "error setting window "
+ "attributes\n");
+ goto surf_err;
+ }
+ }
+
+ syncpt_max = tegra_dc_incr_syncpt_max(dc);
+ data->syncpt_max = syncpt_max;
+
+ queue_work(tegra_fb->flip_wq, &data->work);
+
+ args->post_syncpt_val = syncpt_max;
+ args->post_syncpt_id = tegra_dc_get_syncpt_id(dc);
+
+ return 0;
+
+surf_err:
+ while (i--) {
+ if (data->windows[i].win_data.surface) {
+ nvmap_unpin(tegra_fb->fb_nvmap,
+ data->windows[i].win_data.surface);
+ nvmap_free(tegra_fb->fb_nvmap,
+ data->windows[i].win_data.surface);
+ }
+ }
+ kfree(data);
+ return err;
+}
+
+/* TODO: implement private window ioctls to set overlay x,y */
+
+static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+ struct tegra_fb_info *tegra_fb = info->par;
+ struct tegra_fb_flip_args flip_args;
+ int fd;
+ int ret;
+
+ switch (cmd) {
+ case FBIO_TEGRA_SET_NVMAP_FD:
+ if (copy_from_user(&fd, (void __user *)arg, sizeof(fd)))
+ return -EFAULT;
+
+ return tegra_fb_set_nvmap_fd(tegra_fb, fd);
+
+ case FBIO_TEGRA_FLIP:
+ if (copy_from_user(&flip_args, (void __user *)arg, sizeof(flip_args)))
+ return -EFAULT;
+
+ ret = tegra_fb_flip(tegra_fb, &flip_args);
+
+ if (copy_to_user((void __user *)arg, &flip_args, sizeof(flip_args)))
+ return -EFAULT;
+
+ return ret;
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
static struct fb_ops tegra_fb_ops = {
.owner = THIS_MODULE,
.fb_open = tegra_fb_open,
@@ -248,6 +539,7 @@ static struct fb_ops tegra_fb_ops = {
.fb_fillrect = tegra_fb_fillrect,
.fb_copyarea = tegra_fb_copyarea,
.fb_imageblit = tegra_fb_imageblit,
+ .fb_ioctl = tegra_fb_ioctl,
};
void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
@@ -316,8 +608,21 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
tegra_fb->fb_mem = fb_mem;
tegra_fb->xres = fb_data->xres;
tegra_fb->yres = fb_data->yres;
+ tegra_fb->fb_nvmap = nvmap_create_client(nvmap_dev);
+ if (!tegra_fb->fb_nvmap) {
+ dev_err(&ndev->dev, "couldn't create nvmap client\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
atomic_set(&tegra_fb->in_use, 0);
+ tegra_fb->flip_wq = create_singlethread_workqueue(dev_name(&ndev->dev));
+ if (!tegra_fb->flip_wq) {
+ dev_err(&ndev->dev, "couldn't create flip work-queue\n");
+ ret = -ENOMEM;
+ goto err_delete_wq;
+ }
+
if (fb_mem) {
fb_size = resource_size(fb_mem);
fb_phys = fb_mem->start;
@@ -325,7 +630,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
if (!fb_base) {
dev_err(&ndev->dev, "fb can't be mapped\n");
ret = -EBUSY;
- goto err_free;
+ goto err_put_client;
}
tegra_fb->valid = true;
}
@@ -396,6 +701,10 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
err_iounmap_fb:
iounmap(fb_base);
+err_put_client:
+ nvmap_client_put(tegra_fb->fb_nvmap);
+err_delete_wq:
+ destroy_workqueue(tegra_fb->flip_wq);
err_free:
framebuffer_release(info);
err:
@@ -406,7 +715,19 @@ void tegra_fb_unregister(struct tegra_fb_info *fb_info)
{
struct fb_info *info = fb_info->info;
+ if (fb_info->win->surface) {
+ nvmap_unpin(fb_info->fb_nvmap, fb_info->win->surface);
+ nvmap_free(fb_info->fb_nvmap, fb_info->win->surface);
+ }
+
+ if (fb_info->fb_nvmap)
+ nvmap_client_put(fb_info->fb_nvmap);
+
unregister_framebuffer(info);
+
+ flush_workqueue(fb_info->flip_wq);
+ destroy_workqueue(fb_info->flip_wq);
+
iounmap(info->screen_base);
framebuffer_release(info);
}
diff --git a/include/video/tegrafb.h b/include/video/tegrafb.h
new file mode 100644
index 000000000000..3d7a5a9d66e9
--- /dev/null
+++ b/include/video/tegrafb.h
@@ -0,0 +1,83 @@
+/*
+ * include/video/tegrafb.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_TEGRAFB_H_
+#define _LINUX_TEGRAFB_H_
+
+#include <linux/types.h>
+#include <asm/ioctl.h>
+
+#define TEGRA_FB_WIN_FMT_P1 0
+#define TEGRA_FB_WIN_FMT_P2 1
+#define TEGRA_FB_WIN_FMT_P4 2
+#define TEGRA_FB_WIN_FMT_P8 3
+#define TEGRA_FB_WIN_FMT_B4G4R4A4 4
+#define TEGRA_FB_WIN_FMT_B5G5R5A 5
+#define TEGRA_FB_WIN_FMT_B5G6R5 6
+#define TEGRA_FB_WIN_FMT_AB5G5R5 7
+#define TEGRA_FB_WIN_FMT_B8G8R8A8 12
+#define TEGRA_FB_WIN_FMT_R8G8B8A8 13
+#define TEGRA_FB_WIN_FMT_B6x2G6x2R6x2A8 14
+#define TEGRA_FB_WIN_FMT_R6x2G6x2B6x2A8 15
+#define TEGRA_FB_WIN_FMT_YCbCr422 16
+#define TEGRA_FB_WIN_FMT_YUV422 17
+#define TEGRA_FB_WIN_FMT_YCbCr420P 18
+#define TEGRA_FB_WIN_FMT_YUV420P 19
+#define TEGRA_FB_WIN_FMT_YCbCr422P 20
+#define TEGRA_FB_WIN_FMT_YUV422P 21
+#define TEGRA_FB_WIN_FMT_YCbCr422R 22
+#define TEGRA_FB_WIN_FMT_YUV422R 23
+#define TEGRA_FB_WIN_FMT_YCbCr422RA 24
+#define TEGRA_FB_WIN_FMT_YUV422RA 25
+
+#define TEGRA_FB_WIN_BLEND_NONE 0
+#define TEGRA_FB_WIN_BLEND_PREMULT 1
+#define TEGRA_FB_WIN_BLEND_COVERAGE 2
+
+/* set index to -1 to ignore window data */
+struct tegra_fb_windowattr {
+ __s32 index;
+ __u32 buff_id;
+ __u32 blend;
+ __u32 offset;
+ __u32 stride;
+ __u32 pixformat;
+ __u32 x;
+ __u32 y;
+ __u32 w;
+ __u32 h;
+ __u32 out_x;
+ __u32 out_y;
+ __u32 out_w;
+ __u32 out_h;
+ __u32 z;
+ __u32 pre_syncpt_id;
+ __u32 pre_syncpt_val;
+};
+
+#define TEGRA_FB_FLIP_N_WINDOWS 3
+
+struct tegra_fb_flip_args {
+ struct tegra_fb_windowattr win[TEGRA_FB_FLIP_N_WINDOWS];
+ __u32 post_syncpt_id;
+ __u32 post_syncpt_val;
+};
+
+#define FBIO_TEGRA_SET_NVMAP_FD _IOW('F', 0x40, __u32)
+#define FBIO_TEGRA_FLIP _IOW('F', 0x41, struct tegra_fb_flip_args)
+
+#endif