Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/ast/ast_cursor.c
26493 views
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
49
static unsigned long ast_cursor_vram_size(void)
50
{
51
return AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE;
52
}
53
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
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
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
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
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
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
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
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
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
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
316