3 * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
5 * This program is free software and is provided to you under the terms of the
6 * GNU General Public License version 2 as published by the Free Software
7 * Foundation, and any use by you of this program is subject to the terms
10 * A copy of the licence is included with the program, and can also be obtained
11 * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12 * Boston, MA 02110-1301, USA.
20 * Implementation of cursor functions for PL111 DRM
22 #include <linux/amba/bus.h>
23 #include <linux/amba/clcd.h>
24 #include <linux/version.h>
25 #include <linux/shmem_fs.h>
26 #include <linux/dma-buf.h>
27 #include <linux/module.h>
30 #include <drm/drm_crtc_helper.h>
31 #include "pl111_clcd_ext.h"
32 #include "pl111_drm.h"
34 #define PL111_MAX_CURSOR_WIDTH (64)
35 #define PL111_MAX_CURSOR_HEIGHT (64)
37 #define ARGB_2_LBBP_BINARY_THRESHOLD (1 << 7)
38 #define ARGB_ALPHA_SHIFT 24
39 #define ARGB_ALPHA_MASK (0xff << ARGB_ALPHA_SHIFT)
40 #define ARGB_RED_SHIFT 16
41 #define ARGB_RED_MASK (0xff << ARGB_RED_SHIFT)
42 #define ARGB_GREEN_SHIFT 8
43 #define ARGB_GREEN_MASK (0xff << ARGB_GREEN_SHIFT)
44 #define ARGB_BLUE_SHIFT 0
45 #define ARGB_BLUE_MASK (0xff << ARGB_BLUE_SHIFT)
48 void pl111_set_cursor_size(enum pl111_cursor_size size
)
50 u32 reg_data
= readl(priv
.regs
+ CLCD_CRSR_CONFIG
);
52 if (size
== CURSOR_64X64
)
53 reg_data
|= CRSR_CONFIG_CRSR_SIZE
;
55 reg_data
&= ~CRSR_CONFIG_CRSR_SIZE
;
57 writel(reg_data
, priv
.regs
+ CLCD_CRSR_CONFIG
);
60 void pl111_set_cursor_sync(enum pl111_cursor_sync sync
)
62 u32 reg_data
= readl(priv
.regs
+ CLCD_CRSR_CONFIG
);
64 if (sync
== CURSOR_SYNC_VSYNC
)
65 reg_data
|= CRSR_CONFIG_CRSR_FRAME_SYNC
;
67 reg_data
&= ~CRSR_CONFIG_CRSR_FRAME_SYNC
;
69 writel(reg_data
, priv
.regs
+ CLCD_CRSR_CONFIG
);
72 void pl111_set_cursor(u32 cursor
)
74 u32 reg_data
= readl(priv
.regs
+ CLCD_CRSR_CTRL
);
76 reg_data
&= ~(CRSR_CTRL_CRSR_MAX
<< CRSR_CTRL_CRSR_NUM_SHIFT
);
77 reg_data
|= (cursor
& CRSR_CTRL_CRSR_MAX
) << CRSR_CTRL_CRSR_NUM_SHIFT
;
79 writel(reg_data
, priv
.regs
+ CLCD_CRSR_CTRL
);
82 void pl111_set_cursor_enable(bool enable
)
84 u32 reg_data
= readl(priv
.regs
+ CLCD_CRSR_CTRL
);
87 reg_data
|= CRSR_CTRL_CRSR_ON
;
89 reg_data
&= ~CRSR_CTRL_CRSR_ON
;
91 writel(reg_data
, priv
.regs
+ CLCD_CRSR_CTRL
);
94 void pl111_set_cursor_position(u32 x
, u32 y
)
96 u32 reg_data
= (x
& CRSR_XY_MASK
) |
97 ((y
& CRSR_XY_MASK
) << CRSR_XY_Y_SHIFT
);
99 writel(reg_data
, priv
.regs
+ CLCD_CRSR_XY
);
102 void pl111_set_cursor_clipping(u32 x
, u32 y
)
107 * Do not allow setting clipping values larger than
108 * the cursor size since the cursor is already fully hidden
109 * when x,y = PL111_MAX_CURSOR_WIDTH.
111 if (x
> PL111_MAX_CURSOR_WIDTH
)
112 x
= PL111_MAX_CURSOR_WIDTH
;
113 if (y
> PL111_MAX_CURSOR_WIDTH
)
114 y
= PL111_MAX_CURSOR_WIDTH
;
116 reg_data
= (x
& CRSR_CLIP_MASK
) |
117 ((y
& CRSR_CLIP_MASK
) << CRSR_CLIP_Y_SHIFT
);
119 writel(reg_data
, priv
.regs
+ CLCD_CRSR_CLIP
);
122 void pl111_set_cursor_palette(u32 color0
, u32 color1
)
124 writel(color0
& CRSR_PALETTE_MASK
, priv
.regs
+ CLCD_CRSR_PALETTE_0
);
125 writel(color1
& CRSR_PALETTE_MASK
, priv
.regs
+ CLCD_CRSR_PALETTE_1
);
128 void pl111_cursor_enable(void)
130 pl111_set_cursor_sync(CURSOR_SYNC_VSYNC
);
131 pl111_set_cursor_size(CURSOR_64X64
);
132 pl111_set_cursor_palette(0x0, 0x00ffffff);
133 pl111_set_cursor_enable(true);
136 void pl111_cursor_disable(void)
138 pl111_set_cursor_enable(false);
141 /* shift required to locate pixel into the correct position in
142 * a cursor LBBP word, indexed by x mod 16.
144 static const unsigned char
145 x_mod_16_to_value_shift
[CLCD_CRSR_IMAGE_PIXELS_PER_WORD
] = {
146 6, 4, 2, 0, 14, 12, 10, 8, 22, 20, 18, 16, 30, 28, 26, 24
149 /* Pack the pixel value into its correct position in the buffer as specified
152 set_lbbp_pixel(uint32_t *buffer
, unsigned int x
, unsigned int y
,
155 u32
*cursor_ram
= priv
.regs
+ CLCD_CRSR_IMAGE
;
159 shift
= x_mod_16_to_value_shift
[x
% CLCD_CRSR_IMAGE_PIXELS_PER_WORD
];
161 /* Get the word containing this pixel */
162 cursor_ram
= cursor_ram
+ (x
>> CLCD_CRSR_IMAGE_WORDS_PER_LINE
) + (y
<< 2);
164 /* Update pixel in cursor RAM */
165 data
= readl(cursor_ram
);
166 data
&= ~(CLCD_CRSR_LBBP_COLOR_MASK
<< shift
);
167 data
|= value
<< shift
;
168 writel(data
, cursor_ram
);
171 static u32
pl111_argb_to_lbbp(u32 argb_pix
)
173 u32 lbbp_pix
= CLCD_CRSR_LBBP_TRANSPARENT
;
174 u32 alpha
= (argb_pix
& ARGB_ALPHA_MASK
) >> ARGB_ALPHA_SHIFT
;
175 u32 red
= (argb_pix
& ARGB_RED_MASK
) >> ARGB_RED_SHIFT
;
176 u32 green
= (argb_pix
& ARGB_GREEN_MASK
) >> ARGB_GREEN_SHIFT
;
177 u32 blue
= (argb_pix
& ARGB_BLUE_MASK
) >> ARGB_BLUE_SHIFT
;
180 * Converting from 8 pixel transparency to binary transparency
181 * it's the best we can achieve.
183 if (alpha
& ARGB_2_LBBP_BINARY_THRESHOLD
) {
187 * Convert to gray using the lightness method:
188 * gray = [max(R,G,B) + min(R,G,B)]/2
190 min
= min(red
, green
);
191 min
= min(min
, blue
);
192 max
= max(red
, green
);
193 max
= max(max
, blue
);
194 gray
= (min
+ max
) >> 1; /* divide by 2 */
195 /* Apply binary threshold to the gray value calculated */
196 if (gray
& ARGB_2_LBBP_BINARY_THRESHOLD
)
197 lbbp_pix
= CLCD_CRSR_LBBP_FOREGROUND
;
199 lbbp_pix
= CLCD_CRSR_LBBP_BACKGROUND
;
206 * The PL111 hardware cursor supports only LBBP which is a 2bpp format but
207 * the cursor format from userspace is ARGB8888 so we need to convert
210 static void pl111_set_cursor_image(u32
*data
)
212 #ifdef ARGB_LBBP_CONVERSION_DEBUG
213 /* Add 1 on width to insert trailing NULL */
214 char string_cursor
[PL111_MAX_CURSOR_WIDTH
+ 1];
215 #endif /* ARGB_LBBP_CONVERSION_DEBUG */
219 for (y
= 0; y
< PL111_MAX_CURSOR_HEIGHT
; y
++) {
220 for (x
= 0; x
< PL111_MAX_CURSOR_WIDTH
; x
++) {
221 u32 value
= pl111_argb_to_lbbp(*data
);
223 #ifdef ARGB_LBBP_CONVERSION_DEBUG
224 if (value
== CLCD_CRSR_LBBP_TRANSPARENT
)
225 string_cursor
[x
] = 'T';
226 else if (value
== CLCD_CRSR_LBBP_FOREGROUND
)
227 string_cursor
[x
] = 'F';
228 else if (value
== CLCD_CRSR_LBBP_INVERSE
)
229 string_cursor
[x
] = 'I';
231 string_cursor
[x
] = 'B';
233 #endif /* ARGB_LBBP_CONVERSION_DEBUG */
234 set_lbbp_pixel(data
, x
, y
, value
);
237 #ifdef ARGB_LBBP_CONVERSION_DEBUG
238 string_cursor
[PL111_MAX_CURSOR_WIDTH
] = '\0';
239 DRM_INFO("%s\n", string_cursor
);
240 #endif /* ARGB_LBBP_CONVERSION_DEBUG */
244 int pl111_crtc_cursor_set(struct drm_crtc
*crtc
,
245 struct drm_file
*file_priv
,
250 struct drm_gem_object
*obj
;
251 struct pl111_gem_bo
*bo
;
253 DRM_DEBUG_KMS("handle = %u, width = %u, height = %u\n",
254 handle
, width
, height
);
257 pl111_cursor_disable();
261 if ((width
!= PL111_MAX_CURSOR_WIDTH
) ||
262 (height
!= PL111_MAX_CURSOR_HEIGHT
))
265 obj
= drm_gem_object_lookup(crtc
->dev
, file_priv
, handle
);
267 DRM_ERROR("Cannot find cursor object for handle = %d\n",
273 * We expect a PL111_MAX_CURSOR_WIDTH x PL111_MAX_CURSOR_HEIGHT
274 * ARGB888 buffer object in the input.
277 if (obj
->size
< (PL111_MAX_CURSOR_WIDTH
* PL111_MAX_CURSOR_HEIGHT
* 4)) {
278 DRM_ERROR("Cannot set cursor with an obj size = %d\n",
280 drm_gem_object_unreference_unlocked(obj
);
284 bo
= PL111_BO_FROM_GEM(obj
);
285 if (!(bo
->type
& PL111_BOT_DMA
)) {
286 DRM_ERROR("Tried to set cursor with non DMA backed obj = %p\n",
288 drm_gem_object_unreference_unlocked(obj
);
292 pl111_set_cursor_image(bo
->backing_data
.dma
.fb_cpu_addr
);
295 * Since we copy the contents of the buffer to the HW cursor internal
296 * memory this GEM object is not needed anymore.
298 drm_gem_object_unreference_unlocked(obj
);
300 pl111_cursor_enable();
305 int pl111_crtc_cursor_move(struct drm_crtc
*crtc
,
311 DRM_DEBUG("x %d y %d\n", x
, y
);
314 * The cursor image is clipped automatically at the screen limits when
315 * it extends beyond the screen image to the right or bottom but
316 * we must clip it using pl111 HW features for negative values.
327 pl111_set_cursor_clipping(x_clip
, y_clip
);
328 pl111_set_cursor_position(x
, y
);