drm/tegra: Add hardware cursor support
authorThierry Reding <treding@nvidia.com>
Fri, 20 Dec 2013 12:58:33 +0000 (13:58 +0100)
committerThierry Reding <treding@nvidia.com>
Thu, 5 Jun 2014 21:14:47 +0000 (23:14 +0200)
Enable hardware cursor support on Tegra124. Earlier generations support
the hardware cursor to some degree as well, but not in a way that can be
generically exposed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dc.h

index 3232167..ef40381 100644 (file)
@@ -17,6 +17,7 @@
 
 struct tegra_dc_soc_info {
        bool supports_interlacing;
+       bool supports_cursor;
 };
 
 struct tegra_plane {
@@ -477,6 +478,109 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc)
        spin_unlock_irqrestore(&dc->lock, flags);
 }
 
+static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file,
+                               uint32_t handle, uint32_t width,
+                               uint32_t height, int32_t hot_x, int32_t hot_y)
+{
+       unsigned long value = CURSOR_CLIP_DISPLAY;
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+       struct drm_gem_object *gem;
+       struct tegra_bo *bo = NULL;
+
+       if (!dc->soc->supports_cursor)
+               return -ENXIO;
+
+       if (width != height)
+               return -EINVAL;
+
+       switch (width) {
+       case 32:
+               value |= CURSOR_SIZE_32x32;
+               break;
+
+       case 64:
+               value |= CURSOR_SIZE_64x64;
+               break;
+
+       case 128:
+               value |= CURSOR_SIZE_128x128;
+
+       case 256:
+               value |= CURSOR_SIZE_256x256;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (handle) {
+               gem = drm_gem_object_lookup(crtc->dev, file, handle);
+               if (!gem)
+                       return -ENOENT;
+
+               bo = to_tegra_bo(gem);
+       }
+
+       if (bo) {
+               unsigned long addr = (bo->paddr & 0xfffffc00) >> 10;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+               unsigned long high = (bo->paddr & 0xfffffffc) >> 32;
+#endif
+
+               tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR);
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+               tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI);
+#endif
+
+               value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+               value |= CURSOR_ENABLE;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+               value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
+               value &= ~CURSOR_DST_BLEND_MASK;
+               value &= ~CURSOR_SRC_BLEND_MASK;
+               value |= CURSOR_MODE_NORMAL;
+               value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
+               value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
+               value |= CURSOR_ALPHA;
+               tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
+       } else {
+               value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+               value &= ~CURSOR_ENABLE;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+       }
+
+       tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       return 0;
+}
+
+static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+       unsigned long value;
+
+       if (!dc->soc->supports_cursor)
+               return -ENXIO;
+
+       value = ((y & 0x3fff) << 16) | (x & 0x3fff);
+       tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
+
+       tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       /* XXX: only required on generations earlier than Tegra124? */
+       tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       return 0;
+}
+
 static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
 {
        struct drm_device *drm = dc->base.dev;
@@ -553,6 +657,8 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
 }
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
+       .cursor_set2 = tegra_dc_cursor_set2,
+       .cursor_move = tegra_dc_cursor_move,
        .page_flip = tegra_dc_page_flip,
        .set_config = drm_crtc_helper_set_config,
        .destroy = tegra_dc_destroy,
@@ -999,6 +1105,8 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
        DUMP_REG(DC_DISP_SD_BL_CONTROL);
        DUMP_REG(DC_DISP_SD_HW_K_VALUES);
        DUMP_REG(DC_DISP_SD_MAN_K_VALUES);
+       DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI);
+       DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL);
        DUMP_REG(DC_WIN_WIN_OPTIONS);
        DUMP_REG(DC_WIN_BYTE_SWAP);
        DUMP_REG(DC_WIN_BUFFER_CONTROL);
@@ -1168,14 +1276,17 @@ static const struct host1x_client_ops dc_client_ops = {
 
 static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
        .supports_interlacing = false,
+       .supports_cursor = false,
 };
 
 static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
        .supports_interlacing = false,
+       .supports_cursor = false,
 };
 
 static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
        .supports_interlacing = true,
+       .supports_cursor = true,
 };
 
 static const struct of_device_id tegra_dc_of_match[] = {
index 44e31ae..78c5fef 100644 (file)
 #define WIN_A_ACT_REQ   (1 <<  1)
 #define WIN_B_ACT_REQ   (1 <<  2)
 #define WIN_C_ACT_REQ   (1 <<  3)
+#define CURSOR_ACT_REQ  (1 <<  7)
 #define GENERAL_UPDATE  (1 <<  8)
 #define WIN_A_UPDATE    (1 <<  9)
 #define WIN_B_UPDATE    (1 << 10)
 #define WIN_C_UPDATE    (1 << 11)
+#define CURSOR_UPDATE   (1 << 15)
 #define NC_HOST_TRIG    (1 << 24)
 
 #define DC_CMD_DISPLAY_WINDOW_HEADER           0x042
 #define DC_DISP_DISP_SIGNAL_OPTIONS1           0x401
 
 #define DC_DISP_DISP_WIN_OPTIONS               0x402
-#define HDMI_ENABLE (1 << 30)
-#define DSI_ENABLE  (1 << 29)
-#define SOR_ENABLE  (1 << 25)
+#define HDMI_ENABLE    (1 << 30)
+#define DSI_ENABLE     (1 << 29)
+#define SOR_ENABLE     (1 << 25)
+#define CURSOR_ENABLE  (1 << 16)
 
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY         0x403
 #define CURSOR_THRESHOLD(x)   (((x) & 0x03) << 24)
 #define DC_DISP_CURSOR_BACKGROUND              0x43d
 
 #define DC_DISP_CURSOR_START_ADDR              0x43e
+#define CURSOR_CLIP_DISPLAY    (0 << 28)
+#define CURSOR_CLIP_WIN_A      (1 << 28)
+#define CURSOR_CLIP_WIN_B      (2 << 28)
+#define CURSOR_CLIP_WIN_C      (3 << 28)
+#define CURSOR_SIZE_32x32      (0 << 24)
+#define CURSOR_SIZE_64x64      (1 << 24)
+#define CURSOR_SIZE_128x128    (2 << 24)
+#define CURSOR_SIZE_256x256    (3 << 24)
 #define DC_DISP_CURSOR_START_ADDR_NS           0x43f
 
 #define DC_DISP_CURSOR_POSITION                        0x440
 #define  INTERLACE_START  (1 << 1)
 #define  INTERLACE_ENABLE (1 << 0)
 
+#define DC_DISP_CURSOR_START_ADDR_HI           0x4ec
+#define DC_DISP_BLEND_CURSOR_CONTROL           0x4f1
+#define CURSOR_MODE_LEGACY                     (0 << 24)
+#define CURSOR_MODE_NORMAL                     (1 << 24)
+#define CURSOR_DST_BLEND_ZERO                  (0 << 16)
+#define CURSOR_DST_BLEND_K1                    (1 << 16)
+#define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC      (2 << 16)
+#define CURSOR_DST_BLEND_MASK                  (3 << 16)
+#define CURSOR_SRC_BLEND_K1                    (0 << 8)
+#define CURSOR_SRC_BLEND_K1_TIMES_SRC          (1 << 8)
+#define CURSOR_SRC_BLEND_MASK                  (3 << 8)
+#define CURSOR_ALPHA                           0xff
+
 #define DC_WIN_CSC_YOF                         0x611
 #define DC_WIN_CSC_KYRGB                       0x612
 #define DC_WIN_CSC_KUR                         0x613