summaryrefslogtreecommitdiff
path: root/driver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c')
-rwxr-xr-xdriver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/driver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c b/driver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c
new file mode 100755
index 0000000..4bf20fe
--- /dev/null
+++ b/driver/product/kernel/drivers/gpu/drm/pl111/pl111_drm_cursor.c
@@ -0,0 +1,331 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained
+ * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+
+/**
+ * pl111_drm_cursor.c
+ * Implementation of cursor functions for PL111 DRM
+ */
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "pl111_clcd_ext.h"
+#include "pl111_drm.h"
+
+#define PL111_MAX_CURSOR_WIDTH (64)
+#define PL111_MAX_CURSOR_HEIGHT (64)
+
+#define ARGB_2_LBBP_BINARY_THRESHOLD (1 << 7)
+#define ARGB_ALPHA_SHIFT 24
+#define ARGB_ALPHA_MASK (0xff << ARGB_ALPHA_SHIFT)
+#define ARGB_RED_SHIFT 16
+#define ARGB_RED_MASK (0xff << ARGB_RED_SHIFT)
+#define ARGB_GREEN_SHIFT 8
+#define ARGB_GREEN_MASK (0xff << ARGB_GREEN_SHIFT)
+#define ARGB_BLUE_SHIFT 0
+#define ARGB_BLUE_MASK (0xff << ARGB_BLUE_SHIFT)
+
+
+void pl111_set_cursor_size(enum pl111_cursor_size size)
+{
+ u32 reg_data = readl(priv.regs + CLCD_CRSR_CONFIG);
+
+ if (size == CURSOR_64X64)
+ reg_data |= CRSR_CONFIG_CRSR_SIZE;
+ else
+ reg_data &= ~CRSR_CONFIG_CRSR_SIZE;
+
+ writel(reg_data, priv.regs + CLCD_CRSR_CONFIG);
+}
+
+void pl111_set_cursor_sync(enum pl111_cursor_sync sync)
+{
+ u32 reg_data = readl(priv.regs + CLCD_CRSR_CONFIG);
+
+ if (sync == CURSOR_SYNC_VSYNC)
+ reg_data |= CRSR_CONFIG_CRSR_FRAME_SYNC;
+ else
+ reg_data &= ~CRSR_CONFIG_CRSR_FRAME_SYNC;
+
+ writel(reg_data, priv.regs + CLCD_CRSR_CONFIG);
+}
+
+void pl111_set_cursor(u32 cursor)
+{
+ u32 reg_data = readl(priv.regs + CLCD_CRSR_CTRL);
+
+ reg_data &= ~(CRSR_CTRL_CRSR_MAX << CRSR_CTRL_CRSR_NUM_SHIFT);
+ reg_data |= (cursor & CRSR_CTRL_CRSR_MAX) << CRSR_CTRL_CRSR_NUM_SHIFT;
+
+ writel(reg_data, priv.regs + CLCD_CRSR_CTRL);
+}
+
+void pl111_set_cursor_enable(bool enable)
+{
+ u32 reg_data = readl(priv.regs + CLCD_CRSR_CTRL);
+
+ if (enable)
+ reg_data |= CRSR_CTRL_CRSR_ON;
+ else
+ reg_data &= ~CRSR_CTRL_CRSR_ON;
+
+ writel(reg_data, priv.regs + CLCD_CRSR_CTRL);
+}
+
+void pl111_set_cursor_position(u32 x, u32 y)
+{
+ u32 reg_data = (x & CRSR_XY_MASK) |
+ ((y & CRSR_XY_MASK) << CRSR_XY_Y_SHIFT);
+
+ writel(reg_data, priv.regs + CLCD_CRSR_XY);
+}
+
+void pl111_set_cursor_clipping(u32 x, u32 y)
+{
+ u32 reg_data;
+
+ /*
+ * Do not allow setting clipping values larger than
+ * the cursor size since the cursor is already fully hidden
+ * when x,y = PL111_MAX_CURSOR_WIDTH.
+ */
+ if (x > PL111_MAX_CURSOR_WIDTH)
+ x = PL111_MAX_CURSOR_WIDTH;
+ if (y > PL111_MAX_CURSOR_WIDTH)
+ y = PL111_MAX_CURSOR_WIDTH;
+
+ reg_data = (x & CRSR_CLIP_MASK) |
+ ((y & CRSR_CLIP_MASK) << CRSR_CLIP_Y_SHIFT);
+
+ writel(reg_data, priv.regs + CLCD_CRSR_CLIP);
+}
+
+void pl111_set_cursor_palette(u32 color0, u32 color1)
+{
+ writel(color0 & CRSR_PALETTE_MASK, priv.regs + CLCD_CRSR_PALETTE_0);
+ writel(color1 & CRSR_PALETTE_MASK, priv.regs + CLCD_CRSR_PALETTE_1);
+}
+
+void pl111_cursor_enable(void)
+{
+ pl111_set_cursor_sync(CURSOR_SYNC_VSYNC);
+ pl111_set_cursor_size(CURSOR_64X64);
+ pl111_set_cursor_palette(0x0, 0x00ffffff);
+ pl111_set_cursor_enable(true);
+}
+
+void pl111_cursor_disable(void)
+{
+ pl111_set_cursor_enable(false);
+}
+
+/* shift required to locate pixel into the correct position in
+ * a cursor LBBP word, indexed by x mod 16.
+ */
+static const unsigned char
+x_mod_16_to_value_shift[CLCD_CRSR_IMAGE_PIXELS_PER_WORD] = {
+ 6, 4, 2, 0, 14, 12, 10, 8, 22, 20, 18, 16, 30, 28, 26, 24
+};
+
+/* Pack the pixel value into its correct position in the buffer as specified
+ * for LBBP */
+static inline void
+set_lbbp_pixel(uint32_t *buffer, unsigned int x, unsigned int y,
+ uint32_t value)
+{
+ u32 *cursor_ram = priv.regs + CLCD_CRSR_IMAGE;
+ uint32_t shift;
+ uint32_t data;
+
+ shift = x_mod_16_to_value_shift[x % CLCD_CRSR_IMAGE_PIXELS_PER_WORD];
+
+ /* Get the word containing this pixel */
+ cursor_ram = cursor_ram + (x >> CLCD_CRSR_IMAGE_WORDS_PER_LINE) + (y << 2);
+
+ /* Update pixel in cursor RAM */
+ data = readl(cursor_ram);
+ data &= ~(CLCD_CRSR_LBBP_COLOR_MASK << shift);
+ data |= value << shift;
+ writel(data, cursor_ram);
+}
+
+static u32 pl111_argb_to_lbbp(u32 argb_pix)
+{
+ u32 lbbp_pix = CLCD_CRSR_LBBP_TRANSPARENT;
+ u32 alpha = (argb_pix & ARGB_ALPHA_MASK) >> ARGB_ALPHA_SHIFT;
+ u32 red = (argb_pix & ARGB_RED_MASK) >> ARGB_RED_SHIFT;
+ u32 green = (argb_pix & ARGB_GREEN_MASK) >> ARGB_GREEN_SHIFT;
+ u32 blue = (argb_pix & ARGB_BLUE_MASK) >> ARGB_BLUE_SHIFT;
+
+ /*
+ * Converting from 8 pixel transparency to binary transparency
+ * it's the best we can achieve.
+ */
+ if (alpha & ARGB_2_LBBP_BINARY_THRESHOLD) {
+ u32 gray, max, min;
+
+ /*
+ * Convert to gray using the lightness method:
+ * gray = [max(R,G,B) + min(R,G,B)]/2
+ */
+ min = min(red, green);
+ min = min(min, blue);
+ max = max(red, green);
+ max = max(max, blue);
+ gray = (min + max) >> 1; /* divide by 2 */
+ /* Apply binary threshold to the gray value calculated */
+ if (gray & ARGB_2_LBBP_BINARY_THRESHOLD)
+ lbbp_pix = CLCD_CRSR_LBBP_FOREGROUND;
+ else
+ lbbp_pix = CLCD_CRSR_LBBP_BACKGROUND;
+ }
+
+ return lbbp_pix;
+}
+
+/*
+ * The PL111 hardware cursor supports only LBBP which is a 2bpp format but
+ * the cursor format from userspace is ARGB8888 so we need to convert
+ * to LBBP here.
+ */
+static void pl111_set_cursor_image(u32 *data)
+{
+#ifdef ARGB_LBBP_CONVERSION_DEBUG
+ /* Add 1 on width to insert trailing NULL */
+ char string_cursor[PL111_MAX_CURSOR_WIDTH + 1];
+#endif /* ARGB_LBBP_CONVERSION_DEBUG */
+ unsigned int x;
+ unsigned int y;
+
+ for (y = 0; y < PL111_MAX_CURSOR_HEIGHT; y++) {
+ for (x = 0; x < PL111_MAX_CURSOR_WIDTH; x++) {
+ u32 value = pl111_argb_to_lbbp(*data);
+
+#ifdef ARGB_LBBP_CONVERSION_DEBUG
+ if (value == CLCD_CRSR_LBBP_TRANSPARENT)
+ string_cursor[x] = 'T';
+ else if (value == CLCD_CRSR_LBBP_FOREGROUND)
+ string_cursor[x] = 'F';
+ else if (value == CLCD_CRSR_LBBP_INVERSE)
+ string_cursor[x] = 'I';
+ else
+ string_cursor[x] = 'B';
+
+#endif /* ARGB_LBBP_CONVERSION_DEBUG */
+ set_lbbp_pixel(data, x, y, value);
+ ++data;
+ }
+#ifdef ARGB_LBBP_CONVERSION_DEBUG
+ string_cursor[PL111_MAX_CURSOR_WIDTH] = '\0';
+ DRM_INFO("%s\n", string_cursor);
+#endif /* ARGB_LBBP_CONVERSION_DEBUG */
+ }
+}
+
+int pl111_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width,
+ uint32_t height)
+{
+ struct drm_gem_object *obj;
+ struct pl111_gem_bo *bo;
+
+ DRM_DEBUG_KMS("handle = %u, width = %u, height = %u\n",
+ handle, width, height);
+
+ if (!handle) {
+ pl111_cursor_disable();
+ return 0;
+ }
+
+ if ((width != PL111_MAX_CURSOR_WIDTH) ||
+ (height != PL111_MAX_CURSOR_HEIGHT))
+ return -EINVAL;
+
+ obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("Cannot find cursor object for handle = %d\n",
+ handle);
+ return -ENOENT;
+ }
+
+ /*
+ * We expect a PL111_MAX_CURSOR_WIDTH x PL111_MAX_CURSOR_HEIGHT
+ * ARGB888 buffer object in the input.
+ *
+ */
+ if (obj->size < (PL111_MAX_CURSOR_WIDTH * PL111_MAX_CURSOR_HEIGHT * 4)) {
+ DRM_ERROR("Cannot set cursor with an obj size = %d\n",
+ obj->size);
+ drm_gem_object_unreference_unlocked(obj);
+ return -EINVAL;
+ }
+
+ bo = PL111_BO_FROM_GEM(obj);
+ if (!(bo->type & PL111_BOT_DMA)) {
+ DRM_ERROR("Tried to set cursor with non DMA backed obj = %p\n",
+ obj);
+ drm_gem_object_unreference_unlocked(obj);
+ return -EINVAL;
+ }
+
+ pl111_set_cursor_image(bo->backing_data.dma.fb_cpu_addr);
+
+ /*
+ * Since we copy the contents of the buffer to the HW cursor internal
+ * memory this GEM object is not needed anymore.
+ */
+ drm_gem_object_unreference_unlocked(obj);
+
+ pl111_cursor_enable();
+
+ return 0;
+}
+
+int pl111_crtc_cursor_move(struct drm_crtc *crtc,
+ int x, int y)
+{
+ int x_clip = 0;
+ int y_clip = 0;
+
+ DRM_DEBUG("x %d y %d\n", x, y);
+
+ /*
+ * The cursor image is clipped automatically at the screen limits when
+ * it extends beyond the screen image to the right or bottom but
+ * we must clip it using pl111 HW features for negative values.
+ */
+ if (x < 0) {
+ x_clip = -x;
+ x = 0;
+ }
+ if (y < 0) {
+ y_clip = -y;
+ y = 0;
+ }
+
+ pl111_set_cursor_clipping(x_clip, y_clip);
+ pl111_set_cursor_position(x, y);
+
+ return 0;
+}