1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2019 NXP.
4 */
5
6#include <drm/drm_atomic.h>
7#include <drm/drm_atomic_helper.h>
8#include <drm/drm_blend.h>
9#include <drm/drm_fb_dma_helper.h>
10#include <drm/drm_framebuffer.h>
11#include <drm/drm_gem_atomic_helper.h>
12#include <drm/drm_gem_dma_helper.h>
13
14#include "dcss-dev.h"
15#include "dcss-kms.h"
16
17static const u32 dcss_common_formats[] = {
18	/* RGB */
19	DRM_FORMAT_ARGB8888,
20	DRM_FORMAT_XRGB8888,
21	DRM_FORMAT_ABGR8888,
22	DRM_FORMAT_XBGR8888,
23	DRM_FORMAT_RGBA8888,
24	DRM_FORMAT_RGBX8888,
25	DRM_FORMAT_BGRA8888,
26	DRM_FORMAT_BGRX8888,
27	DRM_FORMAT_XRGB2101010,
28	DRM_FORMAT_XBGR2101010,
29	DRM_FORMAT_RGBX1010102,
30	DRM_FORMAT_BGRX1010102,
31	DRM_FORMAT_ARGB2101010,
32	DRM_FORMAT_ABGR2101010,
33	DRM_FORMAT_RGBA1010102,
34	DRM_FORMAT_BGRA1010102,
35};
36
37static const u64 dcss_video_format_modifiers[] = {
38	DRM_FORMAT_MOD_LINEAR,
39	DRM_FORMAT_MOD_INVALID,
40};
41
42static const u64 dcss_graphics_format_modifiers[] = {
43	DRM_FORMAT_MOD_VIVANTE_TILED,
44	DRM_FORMAT_MOD_VIVANTE_SUPER_TILED,
45	DRM_FORMAT_MOD_LINEAR,
46	DRM_FORMAT_MOD_INVALID,
47};
48
49static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p)
50{
51	return container_of(p, struct dcss_plane, base);
52}
53
54static inline bool dcss_plane_fb_is_linear(const struct drm_framebuffer *fb)
55{
56	return ((fb->flags & DRM_MODE_FB_MODIFIERS) == 0) ||
57	       ((fb->flags & DRM_MODE_FB_MODIFIERS) != 0 &&
58		fb->modifier == DRM_FORMAT_MOD_LINEAR);
59}
60
61static void dcss_plane_destroy(struct drm_plane *plane)
62{
63	struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane,
64						     base);
65
66	drm_plane_cleanup(plane);
67	kfree(dcss_plane);
68}
69
70static bool dcss_plane_format_mod_supported(struct drm_plane *plane,
71					    u32 format,
72					    u64 modifier)
73{
74	switch (plane->type) {
75	case DRM_PLANE_TYPE_PRIMARY:
76		switch (format) {
77		case DRM_FORMAT_ARGB8888:
78		case DRM_FORMAT_XRGB8888:
79		case DRM_FORMAT_ARGB2101010:
80			return modifier == DRM_FORMAT_MOD_LINEAR ||
81			       modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
82			       modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED;
83		default:
84			return modifier == DRM_FORMAT_MOD_LINEAR;
85		}
86		break;
87	case DRM_PLANE_TYPE_OVERLAY:
88		return modifier == DRM_FORMAT_MOD_LINEAR;
89	default:
90		return false;
91	}
92}
93
94static const struct drm_plane_funcs dcss_plane_funcs = {
95	.update_plane		= drm_atomic_helper_update_plane,
96	.disable_plane		= drm_atomic_helper_disable_plane,
97	.destroy		= dcss_plane_destroy,
98	.reset			= drm_atomic_helper_plane_reset,
99	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
100	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
101	.format_mod_supported	= dcss_plane_format_mod_supported,
102};
103
104static bool dcss_plane_can_rotate(const struct drm_format_info *format,
105				  bool mod_present, u64 modifier,
106				  unsigned int rotation)
107{
108	bool linear_format = !mod_present || modifier == DRM_FORMAT_MOD_LINEAR;
109	u32 supported_rotation = DRM_MODE_ROTATE_0;
110
111	if (!format->is_yuv && linear_format)
112		supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
113				     DRM_MODE_REFLECT_MASK;
114	else if (!format->is_yuv &&
115		 (modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
116		  modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED))
117		supported_rotation = DRM_MODE_ROTATE_MASK |
118				     DRM_MODE_REFLECT_MASK;
119	else if (format->is_yuv && linear_format &&
120		 (format->format == DRM_FORMAT_NV12 ||
121		  format->format == DRM_FORMAT_NV21))
122		supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
123				     DRM_MODE_REFLECT_MASK;
124
125	return !!(rotation & supported_rotation);
126}
127
128static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt)
129{
130	if (src_w < 64 &&
131	    (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21))
132		return false;
133	else if (src_w < 32 &&
134		 (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY ||
135		  pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU))
136		return false;
137
138	return src_w >= 16 && src_h >= 8;
139}
140
141static int dcss_plane_atomic_check(struct drm_plane *plane,
142				   struct drm_atomic_state *state)
143{
144	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
145										 plane);
146	struct dcss_plane *dcss_plane = to_dcss_plane(plane);
147	struct dcss_dev *dcss = plane->dev->dev_private;
148	struct drm_framebuffer *fb = new_plane_state->fb;
149	bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY;
150	struct drm_gem_dma_object *dma_obj;
151	struct drm_crtc_state *crtc_state;
152	int hdisplay, vdisplay;
153	int min, max;
154	int ret;
155
156	if (!fb || !new_plane_state->crtc)
157		return 0;
158
159	dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
160	WARN_ON(!dma_obj);
161
162	crtc_state = drm_atomic_get_existing_crtc_state(state,
163							new_plane_state->crtc);
164
165	hdisplay = crtc_state->adjusted_mode.hdisplay;
166	vdisplay = crtc_state->adjusted_mode.vdisplay;
167
168	if (!dcss_plane_is_source_size_allowed(new_plane_state->src_w >> 16,
169					       new_plane_state->src_h >> 16,
170					       fb->format->format)) {
171		DRM_DEBUG_KMS("Source plane size is not allowed!\n");
172		return -EINVAL;
173	}
174
175	dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num,
176				       &min, &max);
177
178	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
179						  min, max, !is_primary_plane,
180						  false);
181	if (ret)
182		return ret;
183
184	if (!new_plane_state->visible)
185		return 0;
186
187	if (!dcss_plane_can_rotate(fb->format,
188				   !!(fb->flags & DRM_MODE_FB_MODIFIERS),
189				   fb->modifier,
190				   new_plane_state->rotation)) {
191		DRM_DEBUG_KMS("requested rotation is not allowed!\n");
192		return -EINVAL;
193	}
194
195	if ((new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0 ||
196	     new_plane_state->crtc_x + new_plane_state->crtc_w > hdisplay ||
197	     new_plane_state->crtc_y + new_plane_state->crtc_h > vdisplay) &&
198	    !dcss_plane_fb_is_linear(fb)) {
199		DRM_DEBUG_KMS("requested cropping operation is not allowed!\n");
200		return -EINVAL;
201	}
202
203	if ((fb->flags & DRM_MODE_FB_MODIFIERS) &&
204	    !plane->funcs->format_mod_supported(plane,
205				fb->format->format,
206				fb->modifier)) {
207		DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier);
208		return -EINVAL;
209	}
210
211	return 0;
212}
213
214static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane)
215{
216	struct drm_plane *plane = &dcss_plane->base;
217	struct drm_plane_state *state = plane->state;
218	struct dcss_dev *dcss = plane->dev->dev_private;
219	struct drm_framebuffer *fb = state->fb;
220	const struct drm_format_info *format = fb->format;
221	struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
222	unsigned long p1_ba = 0, p2_ba = 0;
223
224	if (!format->is_yuv ||
225	    format->format == DRM_FORMAT_NV12 ||
226	    format->format == DRM_FORMAT_NV21)
227		p1_ba = dma_obj->dma_addr + fb->offsets[0] +
228			fb->pitches[0] * (state->src.y1 >> 16) +
229			format->char_per_block[0] * (state->src.x1 >> 16);
230	else if (format->format == DRM_FORMAT_UYVY ||
231		 format->format == DRM_FORMAT_VYUY ||
232		 format->format == DRM_FORMAT_YUYV ||
233		 format->format == DRM_FORMAT_YVYU)
234		p1_ba = dma_obj->dma_addr + fb->offsets[0] +
235			fb->pitches[0] * (state->src.y1 >> 16) +
236			2 * format->char_per_block[0] * (state->src.x1 >> 17);
237
238	if (format->format == DRM_FORMAT_NV12 ||
239	    format->format == DRM_FORMAT_NV21)
240		p2_ba = dma_obj->dma_addr + fb->offsets[1] +
241			(((fb->pitches[1] >> 1) * (state->src.y1 >> 17) +
242			(state->src.x1 >> 17)) << 1);
243
244	dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba,
245			  fb->pitches[0]);
246}
247
248static bool dcss_plane_needs_setup(struct drm_plane_state *state,
249				   struct drm_plane_state *old_state)
250{
251	struct drm_framebuffer *fb = state->fb;
252	struct drm_framebuffer *old_fb = old_state->fb;
253
254	return state->crtc_x != old_state->crtc_x ||
255	       state->crtc_y != old_state->crtc_y ||
256	       state->crtc_w != old_state->crtc_w ||
257	       state->crtc_h != old_state->crtc_h ||
258	       state->src_x  != old_state->src_x  ||
259	       state->src_y  != old_state->src_y  ||
260	       state->src_w  != old_state->src_w  ||
261	       state->src_h  != old_state->src_h  ||
262	       fb->format->format != old_fb->format->format ||
263	       fb->modifier  != old_fb->modifier ||
264	       state->rotation != old_state->rotation ||
265	       state->scaling_filter != old_state->scaling_filter;
266}
267
268static void dcss_plane_atomic_update(struct drm_plane *plane,
269				     struct drm_atomic_state *state)
270{
271	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
272									   plane);
273	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
274									   plane);
275	struct dcss_plane *dcss_plane = to_dcss_plane(plane);
276	struct dcss_dev *dcss = plane->dev->dev_private;
277	struct drm_framebuffer *fb = new_state->fb;
278	struct drm_crtc_state *crtc_state;
279	bool modifiers_present;
280	u32 src_w, src_h, dst_w, dst_h;
281	struct drm_rect src, dst;
282	bool enable = true;
283	bool is_rotation_90_or_270;
284
285	if (!fb || !new_state->crtc || !new_state->visible)
286		return;
287
288	crtc_state = new_state->crtc->state;
289	modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS);
290
291	if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) &&
292	    !dcss_plane_needs_setup(new_state, old_state)) {
293		dcss_plane_atomic_set_base(dcss_plane);
294		return;
295	}
296
297	src = plane->state->src;
298	dst = plane->state->dst;
299
300	/*
301	 * The width and height after clipping.
302	 */
303	src_w = drm_rect_width(&src) >> 16;
304	src_h = drm_rect_height(&src) >> 16;
305	dst_w = drm_rect_width(&dst);
306	dst_h = drm_rect_height(&dst);
307
308	if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
309	    modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR)
310		modifiers_present = false;
311
312	dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num,
313			    new_state->fb->format,
314			    modifiers_present ? fb->modifier :
315						DRM_FORMAT_MOD_LINEAR);
316	dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h);
317	dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num,
318			      new_state->rotation);
319
320	dcss_plane_atomic_set_base(dcss_plane);
321
322	is_rotation_90_or_270 = new_state->rotation & (DRM_MODE_ROTATE_90 |
323						   DRM_MODE_ROTATE_270);
324
325	dcss_scaler_set_filter(dcss->scaler, dcss_plane->ch_num,
326			       new_state->scaling_filter);
327
328	dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num,
329			  new_state->fb->format,
330			  is_rotation_90_or_270 ? src_h : src_w,
331			  is_rotation_90_or_270 ? src_w : src_h,
332			  dst_w, dst_h,
333			  drm_mode_vrefresh(&crtc_state->mode));
334
335	dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
336			       dst.x1, dst.y1, dst_w, dst_h);
337	dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num,
338				 fb->format, new_state->alpha >> 8);
339
340	if (!dcss_plane->ch_num && (new_state->alpha >> 8) == 0)
341		enable = false;
342
343	dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable);
344	dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable);
345
346	if (!enable)
347		dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
348				       0, 0, 0, 0);
349
350	dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable);
351}
352
353static void dcss_plane_atomic_disable(struct drm_plane *plane,
354				      struct drm_atomic_state *state)
355{
356	struct dcss_plane *dcss_plane = to_dcss_plane(plane);
357	struct dcss_dev *dcss = plane->dev->dev_private;
358
359	dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false);
360	dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false);
361	dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0);
362	dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false);
363}
364
365static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = {
366	.atomic_check = dcss_plane_atomic_check,
367	.atomic_update = dcss_plane_atomic_update,
368	.atomic_disable = dcss_plane_atomic_disable,
369};
370
371struct dcss_plane *dcss_plane_init(struct drm_device *drm,
372				   unsigned int possible_crtcs,
373				   enum drm_plane_type type,
374				   unsigned int zpos)
375{
376	struct dcss_plane *dcss_plane;
377	const u64 *format_modifiers = dcss_video_format_modifiers;
378	int ret;
379
380	if (zpos > 2)
381		return ERR_PTR(-EINVAL);
382
383	dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL);
384	if (!dcss_plane) {
385		DRM_ERROR("failed to allocate plane\n");
386		return ERR_PTR(-ENOMEM);
387	}
388
389	if (type == DRM_PLANE_TYPE_PRIMARY)
390		format_modifiers = dcss_graphics_format_modifiers;
391
392	ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs,
393				       &dcss_plane_funcs, dcss_common_formats,
394				       ARRAY_SIZE(dcss_common_formats),
395				       format_modifiers, type, NULL);
396	if (ret) {
397		DRM_ERROR("failed to initialize plane\n");
398		kfree(dcss_plane);
399		return ERR_PTR(ret);
400	}
401
402	drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs);
403
404	ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos);
405	if (ret)
406		return ERR_PTR(ret);
407
408	drm_plane_create_scaling_filter_property(&dcss_plane->base,
409					BIT(DRM_SCALING_FILTER_DEFAULT) |
410					BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR));
411
412	drm_plane_create_rotation_property(&dcss_plane->base,
413					   DRM_MODE_ROTATE_0,
414					   DRM_MODE_ROTATE_0   |
415					   DRM_MODE_ROTATE_90  |
416					   DRM_MODE_ROTATE_180 |
417					   DRM_MODE_ROTATE_270 |
418					   DRM_MODE_REFLECT_X  |
419					   DRM_MODE_REFLECT_Y);
420
421	dcss_plane->ch_num = zpos;
422
423	return dcss_plane;
424}
425