xref: /linux/drivers/gpu/drm/mediatek/mtk_plane.c (revision 3f1c07fc21c68bd3bd2df9d2c9441f6485e934d9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2015 MediaTek Inc.
4  * Author: CK Hu <ck.hu@mediatek.com>
5  */
6 
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_atomic_uapi.h>
10 #include <drm/drm_blend.h>
11 #include <drm/drm_fourcc.h>
12 #include <drm/drm_framebuffer.h>
13 #include <drm/drm_gem_atomic_helper.h>
14 #include <drm/drm_print.h>
15 #include <linux/align.h>
16 
17 #include "mtk_crtc.h"
18 #include "mtk_ddp_comp.h"
19 #include "mtk_drm_drv.h"
20 #include "mtk_gem.h"
21 #include "mtk_plane.h"
22 
23 static const u64 modifiers[] = {
24 	DRM_FORMAT_MOD_LINEAR,
25 	DRM_FORMAT_MOD_INVALID,
26 };
27 
mtk_plane_reset(struct drm_plane * plane)28 static void mtk_plane_reset(struct drm_plane *plane)
29 {
30 	struct mtk_plane_state *state;
31 
32 	if (plane->state) {
33 		__drm_atomic_helper_plane_destroy_state(plane->state);
34 
35 		state = to_mtk_plane_state(plane->state);
36 		memset(state, 0, sizeof(*state));
37 	} else {
38 		state = kzalloc(sizeof(*state), GFP_KERNEL);
39 		if (!state)
40 			return;
41 	}
42 
43 	__drm_atomic_helper_plane_reset(plane, &state->base);
44 
45 	state->base.plane = plane;
46 	state->pending.format = DRM_FORMAT_RGB565;
47 	state->pending.modifier = DRM_FORMAT_MOD_LINEAR;
48 }
49 
mtk_plane_duplicate_state(struct drm_plane * plane)50 static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
51 {
52 	struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
53 	struct mtk_plane_state *state;
54 
55 	state = kmalloc(sizeof(*state), GFP_KERNEL);
56 	if (!state)
57 		return NULL;
58 
59 	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
60 
61 	WARN_ON(state->base.plane != plane);
62 
63 	state->pending = old_state->pending;
64 
65 	return &state->base;
66 }
67 
mtk_plane_format_mod_supported(struct drm_plane * plane,uint32_t format,uint64_t modifier)68 static bool mtk_plane_format_mod_supported(struct drm_plane *plane,
69 					   uint32_t format,
70 					   uint64_t modifier)
71 {
72 	return modifier == DRM_FORMAT_MOD_LINEAR;
73 }
74 
mtk_plane_destroy_state(struct drm_plane * plane,struct drm_plane_state * state)75 static void mtk_plane_destroy_state(struct drm_plane *plane,
76 				    struct drm_plane_state *state)
77 {
78 	__drm_atomic_helper_plane_destroy_state(state);
79 	kfree(to_mtk_plane_state(state));
80 }
81 
mtk_plane_atomic_async_check(struct drm_plane * plane,struct drm_atomic_state * state,bool flip)82 static int mtk_plane_atomic_async_check(struct drm_plane *plane,
83 					struct drm_atomic_state *state, bool flip)
84 {
85 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
86 										 plane);
87 	struct drm_crtc_state *crtc_state;
88 	int ret;
89 
90 	if (plane != new_plane_state->crtc->cursor)
91 		return -EINVAL;
92 
93 	if (!plane->state)
94 		return -EINVAL;
95 
96 	if (!plane->state->fb)
97 		return -EINVAL;
98 
99 	ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,
100 				   to_mtk_plane_state(new_plane_state));
101 	if (ret)
102 		return ret;
103 
104 	crtc_state = drm_atomic_get_new_crtc_state(state,
105 						   new_plane_state->crtc);
106 
107 	return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
108 						   DRM_PLANE_NO_SCALING,
109 						   DRM_PLANE_NO_SCALING,
110 						   true, true);
111 }
112 
mtk_plane_update_new_state(struct drm_plane_state * new_state,struct mtk_plane_state * mtk_plane_state)113 static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
114 				       struct mtk_plane_state *mtk_plane_state)
115 {
116 	struct drm_framebuffer *fb = new_state->fb;
117 	struct drm_gem_object *gem;
118 	struct mtk_gem_obj *mtk_gem;
119 	unsigned int pitch, format;
120 	u64 modifier;
121 	dma_addr_t addr;
122 	dma_addr_t hdr_addr = 0;
123 	unsigned int hdr_pitch = 0;
124 	int offset;
125 
126 	gem = fb->obj[0];
127 	mtk_gem = to_mtk_gem_obj(gem);
128 	addr = mtk_gem->dma_addr;
129 	pitch = fb->pitches[0];
130 	format = fb->format->format;
131 	modifier = fb->modifier;
132 
133 	if (modifier == DRM_FORMAT_MOD_LINEAR) {
134 		/*
135 		 * Using dma_addr_t variable to calculate with multiplier of different types,
136 		 * for example: addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];
137 		 * may cause coverity issue with unintentional overflow.
138 		 */
139 		offset = (new_state->src.x1 >> 16) * fb->format->cpp[0];
140 		addr += offset;
141 		offset = (new_state->src.y1 >> 16) * pitch;
142 		addr += offset;
143 	} else {
144 		int width_in_blocks = ALIGN(fb->width, AFBC_DATA_BLOCK_WIDTH)
145 				      / AFBC_DATA_BLOCK_WIDTH;
146 		int height_in_blocks = ALIGN(fb->height, AFBC_DATA_BLOCK_HEIGHT)
147 				       / AFBC_DATA_BLOCK_HEIGHT;
148 		int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;
149 		int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;
150 		int hdr_size, hdr_offset;
151 
152 		hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;
153 		pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *
154 			AFBC_DATA_BLOCK_HEIGHT * fb->format->cpp[0];
155 
156 		hdr_size = ALIGN(hdr_pitch * height_in_blocks, AFBC_HEADER_ALIGNMENT);
157 		hdr_offset = hdr_pitch * y_offset_in_blocks +
158 			AFBC_HEADER_BLOCK_SIZE * x_offset_in_blocks;
159 
160 		/*
161 		 * Using dma_addr_t variable to calculate with multiplier of different types,
162 		 * for example: addr += hdr_pitch * y_offset_in_blocks;
163 		 * may cause coverity issue with unintentional overflow.
164 		 */
165 		hdr_addr = addr + hdr_offset;
166 
167 		/* The data plane is offset by 1 additional block. */
168 		offset = pitch * y_offset_in_blocks +
169 			 AFBC_DATA_BLOCK_WIDTH * AFBC_DATA_BLOCK_HEIGHT *
170 			 fb->format->cpp[0] * (x_offset_in_blocks + 1);
171 
172 		/*
173 		 * Using dma_addr_t variable to calculate with multiplier of different types,
174 		 * for example: addr += pitch * y_offset_in_blocks;
175 		 * may cause coverity issue with unintentional overflow.
176 		 */
177 		addr = addr + hdr_size + offset;
178 	}
179 
180 	mtk_plane_state->pending.enable = true;
181 	mtk_plane_state->pending.pitch = pitch;
182 	mtk_plane_state->pending.hdr_pitch = hdr_pitch;
183 	mtk_plane_state->pending.format = format;
184 	mtk_plane_state->pending.modifier = modifier;
185 	mtk_plane_state->pending.addr = addr;
186 	mtk_plane_state->pending.hdr_addr = hdr_addr;
187 	mtk_plane_state->pending.x = new_state->dst.x1;
188 	mtk_plane_state->pending.y = new_state->dst.y1;
189 	mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);
190 	mtk_plane_state->pending.height = drm_rect_height(&new_state->dst);
191 	mtk_plane_state->pending.rotation = new_state->rotation;
192 	mtk_plane_state->pending.color_encoding = new_state->color_encoding;
193 }
194 
mtk_plane_atomic_async_update(struct drm_plane * plane,struct drm_atomic_state * state)195 static void mtk_plane_atomic_async_update(struct drm_plane *plane,
196 					  struct drm_atomic_state *state)
197 {
198 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
199 									   plane);
200 	struct mtk_plane_state *new_plane_state = to_mtk_plane_state(plane->state);
201 
202 	plane->state->crtc_x = new_state->crtc_x;
203 	plane->state->crtc_y = new_state->crtc_y;
204 	plane->state->crtc_h = new_state->crtc_h;
205 	plane->state->crtc_w = new_state->crtc_w;
206 	plane->state->src_x = new_state->src_x;
207 	plane->state->src_y = new_state->src_y;
208 	plane->state->src_h = new_state->src_h;
209 	plane->state->src_w = new_state->src_w;
210 	plane->state->dst.x1 = new_state->dst.x1;
211 	plane->state->dst.y1 = new_state->dst.y1;
212 
213 	mtk_plane_update_new_state(new_state, new_plane_state);
214 	swap(plane->state->fb, new_state->fb);
215 	wmb(); /* Make sure the above parameters are set before update */
216 	new_plane_state->pending.async_dirty = true;
217 	mtk_crtc_async_update(new_state->crtc, plane, state);
218 }
219 
220 static const struct drm_plane_funcs mtk_plane_funcs = {
221 	.update_plane = drm_atomic_helper_update_plane,
222 	.disable_plane = drm_atomic_helper_disable_plane,
223 	.destroy = drm_plane_cleanup,
224 	.reset = mtk_plane_reset,
225 	.atomic_duplicate_state = mtk_plane_duplicate_state,
226 	.atomic_destroy_state = mtk_plane_destroy_state,
227 	.format_mod_supported = mtk_plane_format_mod_supported,
228 };
229 
mtk_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)230 static int mtk_plane_atomic_check(struct drm_plane *plane,
231 				  struct drm_atomic_state *state)
232 {
233 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
234 										 plane);
235 	struct drm_framebuffer *fb = new_plane_state->fb;
236 	struct drm_crtc_state *crtc_state;
237 	int ret;
238 
239 	if (!fb)
240 		return 0;
241 
242 	if (WARN_ON(!new_plane_state->crtc))
243 		return 0;
244 
245 	ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,
246 				   to_mtk_plane_state(new_plane_state));
247 	if (ret)
248 		return ret;
249 
250 	crtc_state = drm_atomic_get_crtc_state(state,
251 					       new_plane_state->crtc);
252 	if (IS_ERR(crtc_state))
253 		return PTR_ERR(crtc_state);
254 
255 	return drm_atomic_helper_check_plane_state(new_plane_state,
256 						   crtc_state,
257 						   DRM_PLANE_NO_SCALING,
258 						   DRM_PLANE_NO_SCALING,
259 						   true, true);
260 }
261 
mtk_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)262 static void mtk_plane_atomic_disable(struct drm_plane *plane,
263 				     struct drm_atomic_state *state)
264 {
265 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
266 									   plane);
267 	struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);
268 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
269 									   plane);
270 
271 	mtk_plane_state->pending.enable = false;
272 	wmb(); /* Make sure the above parameter is set before update */
273 	mtk_plane_state->pending.dirty = true;
274 
275 	if (old_state && old_state->crtc)
276 		mtk_crtc_plane_disable(old_state->crtc, plane);
277 }
278 
mtk_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)279 static void mtk_plane_atomic_update(struct drm_plane *plane,
280 				    struct drm_atomic_state *state)
281 {
282 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
283 									   plane);
284 	struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);
285 
286 	if (!new_state->crtc || WARN_ON(!new_state->fb))
287 		return;
288 
289 	if (!new_state->visible) {
290 		mtk_plane_atomic_disable(plane, state);
291 		return;
292 	}
293 
294 	mtk_plane_update_new_state(new_state, mtk_plane_state);
295 	wmb(); /* Make sure the above parameters are set before update */
296 	mtk_plane_state->pending.dirty = true;
297 }
298 
299 static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
300 	.atomic_check = mtk_plane_atomic_check,
301 	.atomic_update = mtk_plane_atomic_update,
302 	.atomic_disable = mtk_plane_atomic_disable,
303 	.atomic_async_update = mtk_plane_atomic_async_update,
304 	.atomic_async_check = mtk_plane_atomic_async_check,
305 };
306 
mtk_plane_init(struct drm_device * dev,struct drm_plane * plane,unsigned long possible_crtcs,enum drm_plane_type type,unsigned int supported_rotations,const u32 blend_modes,const u32 * formats,size_t num_formats,bool supports_afbc,unsigned int plane_idx)307 int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
308 		   unsigned long possible_crtcs, enum drm_plane_type type,
309 		   unsigned int supported_rotations, const u32 blend_modes,
310 		   const u32 *formats, size_t num_formats,
311 		   bool supports_afbc, unsigned int plane_idx)
312 {
313 	int err;
314 
315 	if (!formats || !num_formats) {
316 		DRM_ERROR("no formats for plane\n");
317 		return -EINVAL;
318 	}
319 
320 	err = drm_universal_plane_init(dev, plane, possible_crtcs,
321 				       &mtk_plane_funcs, formats,
322 				       num_formats,
323 				       supports_afbc ? modifiers : NULL,
324 				       type, NULL);
325 	if (err) {
326 		DRM_ERROR("failed to initialize plane\n");
327 		return err;
328 	}
329 
330 	/*
331 	 * The hardware does not support repositioning planes by muxing: their
332 	 * Z-position is infact fixed and the only way to change the actual
333 	 * order is to swap the contents of the entire register set of one
334 	 * overlay with another, which may be more expensive than desired.
335 	 *
336 	 * With no repositioning, the caller of this function guarantees that
337 	 * the plane_idx is correct. This means that, for example, the PRIMARY
338 	 * plane fed to this function will always have plane_idx zero.
339 	 */
340 	err = drm_plane_create_zpos_immutable_property(plane, plane_idx);
341 	if (err) {
342 		DRM_ERROR("Failed to create zpos property for plane %u\n", plane_idx);
343 		return err;
344 	}
345 
346 	if (supported_rotations) {
347 		err = drm_plane_create_rotation_property(plane,
348 							 DRM_MODE_ROTATE_0,
349 							 supported_rotations);
350 		if (err)
351 			DRM_INFO("Create rotation property failed\n");
352 	}
353 
354 	err = drm_plane_create_alpha_property(plane);
355 	if (err)
356 		DRM_ERROR("failed to create property: alpha\n");
357 
358 	if (blend_modes) {
359 		err = drm_plane_create_blend_mode_property(plane, blend_modes);
360 		if (err)
361 			DRM_ERROR("failed to create property: blend_mode\n");
362 	}
363 
364 	drm_plane_helper_add(plane, &mtk_plane_helper_funcs);
365 
366 	return 0;
367 }
368