summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/tegra/dc/dc_reg.h11
-rw-r--r--drivers/video/tegra/dc/ext/Makefile1
-rw-r--r--drivers/video/tegra/dc/ext/cursor.c184
-rw-r--r--drivers/video/tegra/dc/ext/dev.c27
-rw-r--r--drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h16
-rw-r--r--include/video/tegra_dc_ext.h44
6 files changed, 283 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
index 4419fb514781..d5fb4fd7c547 100644
--- a/drivers/video/tegra/dc/dc_reg.h
+++ b/drivers/video/tegra/dc/dc_reg.h
@@ -338,11 +338,22 @@
#define DC_DISP_COLOR_KEY0_UPPER 0x437
#define DC_DISP_COLOR_KEY1_LOWER 0x438
#define DC_DISP_COLOR_KEY1_UPPER 0x439
+
#define DC_DISP_CURSOR_FOREGROUND 0x43c
#define DC_DISP_CURSOR_BACKGROUND 0x43d
+#define CURSOR_COLOR(_r, _g, _b) ((_r) | ((_g) << 8) | ((_b) << 16))
+
#define DC_DISP_CURSOR_START_ADDR 0x43e
#define DC_DISP_CURSOR_START_ADDR_NS 0x43f
+#define CURSOR_START_ADDR_MASK (((1 << 22) - 1) << 10)
+#define CURSOR_START_ADDR(_addr) ((_addr) >> 10)
+#define CURSOR_SIZE_64 (1 << 24)
+
#define DC_DISP_CURSOR_POSITION 0x440
+#define CURSOR_POSITION(_x, _y) \
+ (((_x) & ((1 << 16) - 1)) | \
+ (((_y) & ((1 << 16) - 1)) << 16))
+
#define DC_DISP_CURSOR_POSITION_NS 0x441
#define DC_DISP_INIT_SEQ_CONTROL 0x442
#define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443
diff --git a/drivers/video/tegra/dc/ext/Makefile b/drivers/video/tegra/dc/ext/Makefile
index bd530c2f8164..1a202f95f391 100644
--- a/drivers/video/tegra/dc/ext/Makefile
+++ b/drivers/video/tegra/dc/ext/Makefile
@@ -1,2 +1,3 @@
obj-y += dev.o
obj-y += util.o
+obj-y += cursor.o
diff --git a/drivers/video/tegra/dc/ext/cursor.c b/drivers/video/tegra/dc/ext/cursor.c
new file mode 100644
index 000000000000..e25ca0fd3fc2
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/cursor.c
@@ -0,0 +1,184 @@
+/*
+ * drivers/video/tegra/dc/ext/cursor.c
+ *
+ * Copyright (C) 2011, NVIDIA Corporation
+ *
+ * Author: Robert Morell <rmorell@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <video/tegra_dc_ext.h>
+
+#include "tegra_dc_ext_priv.h"
+
+/* ugh */
+#include "../dc_priv.h"
+#include "../dc_reg.h"
+
+int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user)
+{
+ struct tegra_dc_ext *ext = user->ext;
+ int ret = 0;
+
+ mutex_lock(&ext->cursor.lock);
+
+ if (!ext->cursor.user)
+ ext->cursor.user = user;
+ else if (ext->cursor.user != user)
+ ret = -EBUSY;
+
+ mutex_unlock(&ext->cursor.lock);
+
+ return ret;
+}
+
+int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user)
+{
+ struct tegra_dc_ext *ext = user->ext;
+ int ret = 0;
+
+ mutex_lock(&ext->cursor.lock);
+
+ if (ext->cursor.user == user)
+ ext->cursor.user = 0;
+ else
+ ret = -EACCES;
+
+ mutex_unlock(&ext->cursor.lock);
+
+ return ret;
+}
+
+static void set_cursor_image_hw(struct tegra_dc *dc,
+ struct tegra_dc_ext_cursor_image *args,
+ dma_addr_t phys_addr)
+{
+ tegra_dc_writel(dc,
+ CURSOR_COLOR(args->foreground.r,
+ args->foreground.g,
+ args->foreground.b),
+ DC_DISP_CURSOR_FOREGROUND);
+ tegra_dc_writel(dc,
+ CURSOR_COLOR(args->background.r,
+ args->background.g,
+ args->background.b),
+ DC_DISP_CURSOR_BACKGROUND);
+
+ BUG_ON(phys_addr & ~CURSOR_START_ADDR_MASK);
+
+ tegra_dc_writel(dc,
+ CURSOR_START_ADDR(((unsigned long) phys_addr)) |
+ ((args->flags & TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64) ?
+ CURSOR_SIZE_64 : 0),
+ DC_DISP_CURSOR_START_ADDR);
+}
+
+int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
+ struct tegra_dc_ext_cursor_image *args)
+{
+ struct tegra_dc_ext *ext = user->ext;
+ struct tegra_dc *dc = ext->dc;
+ struct nvmap_handle_ref *handle, *old_handle;
+ dma_addr_t phys_addr;
+ u32 size;
+ int ret;
+
+ if (!user->nvmap)
+ return -EFAULT;
+
+ size = args->flags & (TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 |
+ TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64);
+
+ if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 &&
+ size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64)
+ return -EINVAL;
+
+ mutex_lock(&ext->cursor.lock);
+
+ if (ext->cursor.user != user) {
+ mutex_unlock(&ext->cursor.lock);
+ return -EACCES;
+ }
+
+ old_handle = ext->cursor.cur_handle;
+
+ ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr);
+ if (ret) {
+ mutex_unlock(&ext->cursor.lock);
+ return -EACCES;
+ }
+
+ ext->cursor.cur_handle = handle;
+
+ mutex_lock(&dc->lock);
+
+ set_cursor_image_hw(dc, args, phys_addr);
+
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ /* XXX sync here? */
+
+ mutex_unlock(&dc->lock);
+
+ mutex_unlock(&ext->cursor.lock);
+
+ if (old_handle) {
+ nvmap_unpin(ext->nvmap, old_handle);
+ nvmap_free(ext->nvmap, old_handle);
+ }
+
+ return 0;
+}
+
+int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
+ struct tegra_dc_ext_cursor *args)
+{
+ struct tegra_dc_ext *ext = user->ext;
+ struct tegra_dc *dc = ext->dc;
+ u32 win_options;
+ bool enable;
+
+ mutex_lock(&ext->cursor.lock);
+
+ if (ext->cursor.user != user) {
+ mutex_unlock(&ext->cursor.lock);
+ return -EACCES;
+ }
+
+ enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE);
+
+ mutex_lock(&dc->lock);
+
+ win_options = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ if (!!(win_options & CURSOR_ENABLE) != enable) {
+ win_options &= ~CURSOR_ENABLE;
+ if (enable)
+ win_options |= CURSOR_ENABLE;
+ tegra_dc_writel(dc, win_options, DC_DISP_DISP_WIN_OPTIONS);
+ }
+
+ tegra_dc_writel(dc, CURSOR_POSITION(args->x, args->y),
+ DC_DISP_CURSOR_POSITION);
+
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ /* TODO: need to sync here? hopefully can avoid this, but need to
+ * figure out interaction w/ rest of GENERAL_ACT_REQ */
+
+ mutex_unlock(&dc->lock);
+
+ mutex_unlock(&ext->cursor.lock);
+
+ return 0;
+}
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
index 2fa11d9da189..b51b9378c551 100644
--- a/drivers/video/tegra/dc/ext/dev.c
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -426,6 +426,29 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
return ret;
}
+ case TEGRA_DC_EXT_GET_CURSOR:
+ return tegra_dc_ext_get_cursor(user);
+ case TEGRA_DC_EXT_PUT_CURSOR:
+ return tegra_dc_ext_put_cursor(user);
+ case TEGRA_DC_EXT_SET_CURSOR_IMAGE:
+ {
+ struct tegra_dc_ext_cursor_image args;
+
+ if (copy_from_user(&args, user_arg, sizeof(args)))
+ return -EFAULT;
+
+ return tegra_dc_ext_set_cursor_image(user, &args);
+ }
+ case TEGRA_DC_EXT_SET_CURSOR:
+ {
+ struct tegra_dc_ext_cursor args;
+
+ if (copy_from_user(&args, user_arg, sizeof(args)))
+ return -EFAULT;
+
+ return tegra_dc_ext_set_cursor(user, &args);
+ }
+
default:
return -EINVAL;
}
@@ -458,6 +481,8 @@ static int tegra_dc_release(struct inode *inode, struct file *filp)
if (ext->win[i].user == user)
tegra_dc_ext_put_window(user, i);
}
+ if (ext->cursor.user == user)
+ tegra_dc_ext_put_cursor(user);
if (user->nvmap)
nvmap_client_put(user->nvmap);
@@ -550,6 +575,8 @@ struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
if (ret)
goto cleanup_nvmap;
+ mutex_init(&ext->cursor.lock);
+
tegra_dc_ext_devno++;
return ext;
diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
index d7d5e5506718..3040cf7fc601 100644
--- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
+++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
@@ -22,8 +22,11 @@
#include <linux/cdev.h>
#include <linux/mutex.h>
+#include <mach/dc.h>
#include <mach/nvmap.h>
+#include <video/tegra_dc_ext.h>
+
struct tegra_dc_ext;
struct tegra_dc_ext_user {
@@ -54,10 +57,23 @@ struct tegra_dc_ext {
struct nvmap_client *nvmap;
struct tegra_dc_ext_win win[DC_N_WINDOWS];
+
+ struct {
+ struct tegra_dc_ext_user *user;
+ struct nvmap_handle_ref *cur_handle;
+ struct mutex lock;
+ } cursor;
};
extern int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id,
struct nvmap_handle_ref **handle,
dma_addr_t *phys_addr);
+extern int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user);
+extern int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user);
+extern int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
+ struct tegra_dc_ext_cursor_image *);
+extern int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
+ struct tegra_dc_ext_cursor *);
+
#endif /* __TEGRA_DC_EXT_PRIV_H */
diff --git a/include/video/tegra_dc_ext.h b/include/video/tegra_dc_ext.h
index 346825aa4147..f9935ef8c04f 100644
--- a/include/video/tegra_dc_ext.h
+++ b/include/video/tegra_dc_ext.h
@@ -89,6 +89,42 @@ struct tegra_dc_ext_flip {
__u32 post_syncpt_val;
};
+/*
+ * Cursor image format:
+ * - Tegra hardware supports two colors: foreground and background, specified
+ * by the client in RGB8.
+ * - The image should be specified as two 1bpp bitmaps immediately following
+ * each other in memory. Each pixel in the final cursor will be constructed
+ * from the bitmaps with the following logic:
+ * bitmap1 bitmap0
+ * (mask) (color)
+ * 1 0 transparent
+ * 1 1 inverted
+ * 0 0 background color
+ * 0 1 foreground color
+ * - Exactly one of the SIZE flags must be specified.
+ */
+#define TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 1
+#define TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64 2
+struct tegra_dc_ext_cursor_image {
+ struct {
+ __u8 r;
+ __u8 g;
+ __u8 b;
+ } foreground, background;
+ __u32 buff_id;
+ __u32 flags;
+};
+
+/* Possible flags for struct nvdc_cursor's flags field */
+#define TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE 1
+
+struct tegra_dc_ext_cursor {
+ __s16 x;
+ __s16 y;
+ __u32 flags;
+};
+
#define TEGRA_DC_EXT_SET_NVMAP_FD \
_IOW('D', 0x00, __s32)
@@ -100,5 +136,13 @@ struct tegra_dc_ext_flip {
#define TEGRA_DC_EXT_FLIP \
_IOWR('D', 0x03, struct tegra_dc_ext_flip)
+#define TEGRA_DC_EXT_GET_CURSOR \
+ _IO('D', 0x04)
+#define TEGRA_DC_EXT_PUT_CURSOR \
+ _IO('D', 0x05)
+#define TEGRA_DC_EXT_SET_CURSOR_IMAGE \
+ _IOW('D', 0x06, struct tegra_dc_ext_cursor_image)
+#define TEGRA_DC_EXT_SET_CURSOR \
+ _IOW('D', 0x07, struct tegra_dc_ext_cursor)
#endif /* __TEGRA_DC_EXT_H */