1 // SPDX-License-Identifier: MIT
2 /*
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sub license, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
14 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17 * USE OR OTHER DEALINGS IN THE SOFTWARE.
18 *
19 * The above copyright notice and this permission notice (including the
20 * next paragraph) shall be included in all copies or substantial portions
21 * of the Software.
22 */
23
24 #include <linux/bits.h>
25 #include <linux/sizes.h>
26
27 #include <drm/drm_atomic.h>
28 #include <drm/drm_damage_helper.h>
29 #include <drm/drm_format_helper.h>
30 #include <drm/drm_gem_atomic_helper.h>
31 #include <drm/drm_print.h>
32
33 #include "ast_drv.h"
34
35 /*
36 * Hardware cursor
37 */
38
39 /* define for signature structure */
40 #define AST_HWC_SIGNATURE_SIZE SZ_32
41 #define AST_HWC_SIGNATURE_CHECKSUM 0x00
42 #define AST_HWC_SIGNATURE_SizeX 0x04
43 #define AST_HWC_SIGNATURE_SizeY 0x08
44 #define AST_HWC_SIGNATURE_X 0x0C
45 #define AST_HWC_SIGNATURE_Y 0x10
46 #define AST_HWC_SIGNATURE_HOTSPOTX 0x14
47 #define AST_HWC_SIGNATURE_HOTSPOTY 0x18
48
ast_cursor_vram_size(void)49 static unsigned long ast_cursor_vram_size(void)
50 {
51 return AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE;
52 }
53
ast_cursor_vram_offset(struct ast_device * ast)54 long ast_cursor_vram_offset(struct ast_device *ast)
55 {
56 unsigned long size = ast_cursor_vram_size();
57
58 if (size > ast->vram_size)
59 return -EINVAL;
60
61 return ALIGN_DOWN(ast->vram_size - size, SZ_8);
62 }
63
ast_cursor_calculate_checksum(const void * src,unsigned int width,unsigned int height)64 static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height)
65 {
66 u32 csum = 0;
67 unsigned int one_pixel_copy = width & BIT(0);
68 unsigned int two_pixel_copy = width - one_pixel_copy;
69 unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16);
70 unsigned int x, y;
71
72 for (y = 0; y < height; y++) {
73 for (x = 0; x < two_pixel_copy; x += 2) {
74 const u32 *src32 = (const u32 *)src;
75
76 csum += *src32;
77 src += SZ_4;
78 }
79 if (one_pixel_copy) {
80 const u16 *src16 = (const u16 *)src;
81
82 csum += *src16;
83 src += SZ_2;
84 }
85 src += trailing_bytes;
86 }
87
88 return csum;
89 }
90
ast_set_cursor_image(struct ast_device * ast,const u8 * src,unsigned int width,unsigned int height)91 static void ast_set_cursor_image(struct ast_device *ast, const u8 *src,
92 unsigned int width, unsigned int height)
93 {
94 u8 __iomem *dst = ast_plane_vaddr(&ast->cursor_plane.base);
95 u32 csum;
96
97 csum = ast_cursor_calculate_checksum(src, width, height);
98
99 /* write pixel data */
100 memcpy_toio(dst, src, AST_HWC_SIZE);
101
102 /* write checksum + signature */
103 dst += AST_HWC_SIZE;
104 writel(csum, dst);
105 writel(width, dst + AST_HWC_SIGNATURE_SizeX);
106 writel(height, dst + AST_HWC_SIGNATURE_SizeY);
107 writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
108 writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
109 }
110
ast_set_cursor_base(struct ast_device * ast,u64 address)111 static void ast_set_cursor_base(struct ast_device *ast, u64 address)
112 {
113 u8 addr0 = (address >> 3) & 0xff;
114 u8 addr1 = (address >> 11) & 0xff;
115 u8 addr2 = (address >> 19) & 0xff;
116
117 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0);
118 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1);
119 ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2);
120 }
121
ast_set_cursor_location(struct ast_device * ast,u16 x,u16 y,u8 x_offset,u8 y_offset)122 static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y,
123 u8 x_offset, u8 y_offset)
124 {
125 u8 x0 = (x & 0x00ff);
126 u8 x1 = (x & 0x0f00) >> 8;
127 u8 y0 = (y & 0x00ff);
128 u8 y1 = (y & 0x0700) >> 8;
129
130 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset);
131 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset);
132 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0);
133 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1);
134 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0);
135 ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1);
136 }
137
ast_set_cursor_enabled(struct ast_device * ast,bool enabled)138 static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled)
139 {
140 static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP |
141 AST_IO_VGACRCB_HWC_ENABLED);
142
143 u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP;
144
145 if (enabled)
146 vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED;
147
148 ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb);
149 }
150
151 /*
152 * Cursor plane
153 */
154
155 static const uint32_t ast_cursor_plane_formats[] = {
156 DRM_FORMAT_ARGB4444,
157 DRM_FORMAT_ARGB8888,
158 };
159
ast_cursor_plane_helper_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)160 static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane,
161 struct drm_atomic_state *state)
162 {
163 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
164 struct drm_framebuffer *new_fb = new_plane_state->fb;
165 struct drm_crtc_state *new_crtc_state = NULL;
166 int ret;
167
168 if (new_plane_state->crtc)
169 new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
170
171 ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
172 DRM_PLANE_NO_SCALING,
173 DRM_PLANE_NO_SCALING,
174 true, true);
175 if (ret || !new_plane_state->visible)
176 return ret;
177
178 if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT)
179 return -EINVAL;
180
181 return 0;
182 }
183
ast_cursor_plane_helper_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)184 static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
185 struct drm_atomic_state *state)
186 {
187 struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane);
188 struct ast_plane *ast_plane = to_ast_plane(plane);
189 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
190 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
191 struct drm_framebuffer *fb = plane_state->fb;
192 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
193 struct ast_device *ast = to_ast_device(plane->dev);
194 struct drm_rect damage;
195 u64 dst_off = ast_plane->offset;
196 u8 __iomem *dst = ast_plane_vaddr(ast_plane); /* TODO: Use mapping abstraction properly */
197 u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */
198 unsigned int offset_x, offset_y;
199 u16 x, y;
200 u8 x_offset, y_offset;
201
202 /*
203 * Do data transfer to hardware buffer and point the scanout
204 * engine to the offset.
205 */
206
207 if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) {
208 u8 *argb4444;
209
210 switch (fb->format->format) {
211 case DRM_FORMAT_ARGB4444:
212 argb4444 = shadow_plane_state->data[0].vaddr;
213 break;
214 default:
215 argb4444 = ast_cursor_plane->argb4444;
216 {
217 struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = {
218 IOSYS_MAP_INIT_VADDR(argb4444),
219 };
220 unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
221 AST_HWC_PITCH,
222 };
223
224 drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch,
225 shadow_plane_state->data, fb, &damage,
226 &shadow_plane_state->fmtcnv_state);
227 }
228 break;
229 }
230 ast_set_cursor_image(ast, argb4444, fb->width, fb->height);
231 ast_set_cursor_base(ast, dst_off);
232 }
233
234 /*
235 * Update location in HWC signature and registers.
236 */
237
238 writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X);
239 writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y);
240
241 offset_x = AST_MAX_HWC_WIDTH - fb->width;
242 offset_y = AST_MAX_HWC_HEIGHT - fb->height;
243
244 if (plane_state->crtc_x < 0) {
245 x_offset = (-plane_state->crtc_x) + offset_x;
246 x = 0;
247 } else {
248 x_offset = offset_x;
249 x = plane_state->crtc_x;
250 }
251 if (plane_state->crtc_y < 0) {
252 y_offset = (-plane_state->crtc_y) + offset_y;
253 y = 0;
254 } else {
255 y_offset = offset_y;
256 y = plane_state->crtc_y;
257 }
258
259 ast_set_cursor_location(ast, x, y, x_offset, y_offset);
260
261 /* Dummy write to enable HWC and make the HW pick-up the changes. */
262 ast_set_cursor_enabled(ast, true);
263 }
264
ast_cursor_plane_helper_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)265 static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane,
266 struct drm_atomic_state *state)
267 {
268 struct ast_device *ast = to_ast_device(plane->dev);
269
270 ast_set_cursor_enabled(ast, false);
271 }
272
273 static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = {
274 DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
275 .atomic_check = ast_cursor_plane_helper_atomic_check,
276 .atomic_update = ast_cursor_plane_helper_atomic_update,
277 .atomic_disable = ast_cursor_plane_helper_atomic_disable,
278 };
279
280 static const struct drm_plane_funcs ast_cursor_plane_funcs = {
281 .update_plane = drm_atomic_helper_update_plane,
282 .disable_plane = drm_atomic_helper_disable_plane,
283 .destroy = drm_plane_cleanup,
284 DRM_GEM_SHADOW_PLANE_FUNCS,
285 };
286
ast_cursor_plane_init(struct ast_device * ast)287 int ast_cursor_plane_init(struct ast_device *ast)
288 {
289 struct drm_device *dev = &ast->base;
290 struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane;
291 struct ast_plane *ast_plane = &ast_cursor_plane->base;
292 struct drm_plane *cursor_plane = &ast_plane->base;
293 unsigned long size;
294 long offset;
295 int ret;
296
297 size = ast_cursor_vram_size();
298 offset = ast_cursor_vram_offset(ast);
299 if (offset < 0)
300 return offset;
301
302 ret = ast_plane_init(dev, ast_plane, offset, size,
303 0x01, &ast_cursor_plane_funcs,
304 ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats),
305 NULL, DRM_PLANE_TYPE_CURSOR);
306 if (ret) {
307 drm_err(dev, "ast_plane_init() failed: %d\n", ret);
308 return ret;
309 }
310 drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs);
311 drm_plane_enable_fb_damage_clips(cursor_plane);
312
313 return 0;
314 }
315