video: tegra: Add cursor support to dc extensions
Robert Morell [Fri, 18 Feb 2011 23:51:38 +0000 (15:51 -0800)]
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

drivers/video/tegra/dc/dc_reg.h
drivers/video/tegra/dc/ext/Makefile
drivers/video/tegra/dc/ext/cursor.c [new file with mode: 0644]
drivers/video/tegra/dc/ext/dev.c
drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
include/video/tegra_dc_ext.h

index 4419fb5..d5fb4fd 100644 (file)
 #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
index bd530c2..1a202f9 100644 (file)
@@ -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 (file)
index 0000000..e25ca0f
--- /dev/null
@@ -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;
+}
index 9e70e6e..bcb7ba4 100644 (file)
@@ -427,6 +427,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;
        }
@@ -459,6 +482,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);
@@ -551,6 +576,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;
index d7d5e55..3040cf7 100644 (file)
 #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 */
index 346825a..f9935ef 100644 (file)
@@ -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 */