summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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