diff options
author | Robert Morell <rmorell@nvidia.com> | 2011-02-18 15:51:38 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:48:12 -0800 |
commit | cd42c053c598d720c12ca8d2e13715ee490f6717 (patch) | |
tree | 60b8098504ecc8b1ee6524f5df22e1151072a570 | |
parent | 5197ed5e5f11b4f77bdcf9020a01bada5db45f1a (diff) |
video: tegra: Add cursor support to dc extensions
This change adds full support for specify the cursor image and
manipulating its position.
bug 818525
Original-Change-Id: I101a951aff358b0ac0998afc6fe5f6c5c4d37c64
Signed-off-by: Robert Morell <rmorell@nvidia.com>
Reviewed-on: http://git-master/r/40518
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: R93f0c68a14e4419f200a77d48a17eb8862f2e4e1
-rw-r--r-- | drivers/video/tegra/dc/dc_reg.h | 11 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/cursor.c | 184 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/dev.c | 27 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | 16 | ||||
-rw-r--r-- | include/video/tegra_dc_ext.h | 44 |
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 */ |