xref: /linux/drivers/gpu/drm/verisilicon/vs_cursor_plane.c (revision 40288c9206c17eb66a603262e06a58d300d0f279)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)
4  *
5  * Authors:
6  * Icenowy Zheng <zhengxingda@iscas.ac.cn>
7  */
8 
9 #include <linux/log2.h>
10 #include <linux/regmap.h>
11 
12 #include <drm/drm_atomic.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_blend.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_fourcc.h>
17 #include <drm/drm_framebuffer.h>
18 #include <drm/drm_gem_atomic_helper.h>
19 #include <drm/drm_modeset_helper_vtables.h>
20 #include <drm/drm_plane.h>
21 #include <drm/drm_print.h>
22 
23 #include "vs_crtc.h"
24 #include "vs_plane.h"
25 #include "vs_dc.h"
26 #include "vs_hwdb.h"
27 #include "vs_cursor_plane_regs.h"
28 
29 #define VSDC_MIN_CURSOR_SIZE 32
30 #define VSDC_MAX_CURSOR_SIZE 256
31 
32 #define VSDC_CURSOR_LOCATION_MAX_POSITIVE BIT_MASK(15)
33 #define VSDC_CURSOR_LOCATION_MAX_NEGATIVE BIT_MASK(5)
34 
vs_cursor_plane_check_coord(int32_t coord)35 static bool vs_cursor_plane_check_coord(int32_t coord)
36 {
37 	if (coord >= 0)
38 		return coord <= VSDC_CURSOR_LOCATION_MAX_POSITIVE;
39 	else
40 		return (-coord) <= VSDC_CURSOR_LOCATION_MAX_NEGATIVE;
41 }
42 
vs_cursor_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_commit * state)43 static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
44 					struct drm_atomic_commit *state)
45 {
46 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
47 										 plane);
48 	struct drm_crtc *crtc = new_plane_state->crtc;
49 	struct drm_framebuffer *fb = new_plane_state->fb;
50 	struct drm_crtc_state *crtc_state = NULL;
51 	struct vs_crtc *vcrtc;
52 	struct vs_dc *dc;
53 	int ret;
54 
55 	if (crtc)
56 		crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
57 
58 	ret = drm_atomic_helper_check_plane_state(new_plane_state,
59 						  crtc_state,
60 						  DRM_PLANE_NO_SCALING,
61 						  DRM_PLANE_NO_SCALING,
62 						  true, true);
63 	if (ret)
64 		return ret;
65 
66 	if (!new_plane_state->visible)
67 		return 0; /* Skip validity check */
68 
69 	vcrtc = drm_crtc_to_vs_crtc(crtc);
70 	dc = vcrtc->dc;
71 
72 	/* Only certain PoT square sizes is supported. */
73 	if (!is_power_of_2(new_plane_state->crtc_w) ||
74 	    new_plane_state->crtc_w < VSDC_MIN_CURSOR_SIZE ||
75 	    new_plane_state->crtc_w > dc->identity.max_cursor_size)
76 		return -EINVAL;
77 
78 	if (new_plane_state->crtc_w != new_plane_state->crtc_h)
79 		return -EINVAL;
80 
81 	/* Check if the cursor is inside the register fields' range */
82 	if (!vs_cursor_plane_check_coord(new_plane_state->crtc_x) ||
83 	    !vs_cursor_plane_check_coord(new_plane_state->crtc_y))
84 		return -EINVAL;
85 
86 	/* Extra line padding isn't supported */
87 	if (fb->pitches[0] !=
88 	    drm_format_info_min_pitch(fb->format, 0, new_plane_state->crtc_w))
89 		return -EINVAL;
90 
91 	return 0;
92 }
93 
vs_cursor_plane_commit(struct vs_dc * dc,unsigned int output)94 static void vs_cursor_plane_commit(struct vs_dc *dc, unsigned int output)
95 {
96 	regmap_set_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
97 			VSDC_CURSOR_CONFIG_COMMIT |
98 			VSDC_CURSOR_CONFIG_IMG_UPDATE);
99 }
100 
vs_cursor_plane_atomic_enable(struct drm_plane * plane,struct drm_atomic_commit * atomic_state)101 static void vs_cursor_plane_atomic_enable(struct drm_plane *plane,
102 					   struct drm_atomic_commit *atomic_state)
103 {
104 	struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state,
105 								       plane);
106 	struct drm_crtc *crtc = state->crtc;
107 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
108 	unsigned int output = vcrtc->id;
109 	struct vs_dc *dc = vcrtc->dc;
110 
111 	regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
112 			   VSDC_CURSOR_CONFIG_FMT_MASK,
113 			   VSDC_CURSOR_CONFIG_FMT_ARGB8888);
114 
115 	vs_cursor_plane_commit(dc, output);
116 }
117 
vs_cursor_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_commit * atomic_state)118 static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
119 					    struct drm_atomic_commit *atomic_state)
120 {
121 	struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state,
122 								       plane);
123 	struct drm_crtc *crtc = state->crtc;
124 	struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
125 	unsigned int output = vcrtc->id;
126 	struct vs_dc *dc = vcrtc->dc;
127 
128 	regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
129 			   VSDC_CURSOR_CONFIG_FMT_MASK,
130 			   VSDC_CURSOR_CONFIG_FMT_OFF);
131 
132 	vs_cursor_plane_commit(dc, output);
133 }
134 
vs_cursor_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_commit * atomic_state)135 static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
136 					   struct drm_atomic_commit *atomic_state)
137 {
138 	struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state,
139 								       plane);
140 	struct drm_framebuffer *fb = state->fb;
141 	struct drm_crtc *crtc = state->crtc;
142 	struct vs_dc *dc;
143 	struct vs_crtc *vcrtc;
144 	unsigned int output;
145 	dma_addr_t dma_addr;
146 
147 	if (!state->visible) {
148 		vs_cursor_plane_atomic_disable(plane, atomic_state);
149 		return;
150 	}
151 
152 	vcrtc = drm_crtc_to_vs_crtc(crtc);
153 	output = vcrtc->id;
154 	dc = vcrtc->dc;
155 
156 	/* Other sizes should be rejected by atomic_check */
157 	switch (state->crtc_w) {
158 	case 32:
159 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
160 				   VSDC_CURSOR_CONFIG_SIZE_MASK,
161 				   VSDC_CURSOR_CONFIG_SIZE_32);
162 		break;
163 	case 64:
164 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
165 				   VSDC_CURSOR_CONFIG_SIZE_MASK,
166 				   VSDC_CURSOR_CONFIG_SIZE_64);
167 		break;
168 	case 128:
169 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
170 				   VSDC_CURSOR_CONFIG_SIZE_MASK,
171 				   VSDC_CURSOR_CONFIG_SIZE_128);
172 		break;
173 	case 256:
174 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
175 				   VSDC_CURSOR_CONFIG_SIZE_MASK,
176 				   VSDC_CURSOR_CONFIG_SIZE_256);
177 		break;
178 	}
179 
180 	dma_addr = vs_fb_get_dma_addr(fb, &state->src);
181 
182 	regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output),
183 		     lower_32_bits(dma_addr));
184 
185 	/*
186 	 * The X_OFF and Y_OFF fields define which point does the LOCATION
187 	 * register represent in the cursor image, and LOCATION register
188 	 * values are unsigned. To for positive left-top  coordinates the
189 	 * offset is set to 0 and the location is set to the coordinate, for
190 	 * negative coordinates the location is set to 0 and the offset
191 	 * is set to the opposite number of the coordinate to offset the
192 	 * cursor image partly off-screen.
193 	 */
194 	if (state->crtc_x >= 0) {
195 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
196 				   VSDC_CURSOR_CONFIG_X_OFF_MASK, 0);
197 		regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
198 				   VSDC_CURSOR_LOCATION_X_MASK,
199 				   VSDC_CURSOR_LOCATION_X(state->crtc_x));
200 	} else {
201 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
202 				   VSDC_CURSOR_CONFIG_X_OFF_MASK,
203 				   -state->crtc_x);
204 		regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
205 				   VSDC_CURSOR_LOCATION_X_MASK, 0);
206 	}
207 
208 	if (state->crtc_y >= 0) {
209 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
210 				   VSDC_CURSOR_CONFIG_Y_OFF_MASK, 0);
211 		regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
212 				   VSDC_CURSOR_LOCATION_Y_MASK,
213 				   VSDC_CURSOR_LOCATION_Y(state->crtc_y));
214 	} else {
215 		regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
216 				   VSDC_CURSOR_CONFIG_Y_OFF_MASK,
217 				   -state->crtc_y);
218 		regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
219 				   VSDC_CURSOR_LOCATION_Y_MASK, 0);
220 	}
221 
222 	vs_cursor_plane_commit(dc, output);
223 }
224 
225 static const struct drm_plane_helper_funcs vs_cursor_plane_helper_funcs = {
226 	.atomic_check	= vs_cursor_plane_atomic_check,
227 	.atomic_update	= vs_cursor_plane_atomic_update,
228 	.atomic_enable	= vs_cursor_plane_atomic_enable,
229 	.atomic_disable	= vs_cursor_plane_atomic_disable,
230 };
231 
232 static const struct drm_plane_funcs vs_cursor_plane_funcs = {
233 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
234 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
235 	.disable_plane		= drm_atomic_helper_disable_plane,
236 	.reset			= drm_atomic_helper_plane_reset,
237 	.update_plane		= drm_atomic_helper_update_plane,
238 };
239 
240 static const u32 vs_cursor_plane_formats[] = {
241 	DRM_FORMAT_ARGB8888,
242 };
243 
244 static const u64 vs_cursor_plane_modifiers[] = {
245 	DRM_FORMAT_MOD_LINEAR,
246 	DRM_FORMAT_MOD_INVALID, /* sentinel */
247 };
248 
vs_cursor_plane_init(struct drm_device * drm_dev,struct vs_dc * dc)249 struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev,
250 				       struct vs_dc *dc)
251 {
252 	int32_t max_cursor_size = dc->identity.max_cursor_size;
253 	struct drm_plane *plane;
254 
255 	if (drm_WARN_ON_ONCE(drm_dev, max_cursor_size < VSDC_MIN_CURSOR_SIZE ||
256 				      max_cursor_size > VSDC_MAX_CURSOR_SIZE))
257 		return ERR_PTR(-EINVAL);
258 
259 	plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0,
260 					   &vs_cursor_plane_funcs,
261 					   vs_cursor_plane_formats,
262 					   ARRAY_SIZE(vs_cursor_plane_formats),
263 					   vs_cursor_plane_modifiers,
264 					   DRM_PLANE_TYPE_CURSOR,
265 					   NULL);
266 
267 	if (IS_ERR(plane))
268 		return plane;
269 
270 	drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs);
271 	drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
272 
273 	return plane;
274 }
275