Linux Audio

Check our new training course

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