Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v3.1.
  1/*
  2 * i.MX IPUv3 DP Overlay Planes
  3 *
  4 * Copyright (C) 2013 Philipp Zabel, Pengutronix
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version 2
  9 * of the License, or (at your option) any later version.
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 */
 15
 16#include <drm/drmP.h>
 17#include <drm/drm_atomic.h>
 18#include <drm/drm_atomic_helper.h>
 19#include <drm/drm_fb_cma_helper.h>
 20#include <drm/drm_gem_cma_helper.h>
 21#include <drm/drm_plane_helper.h>
 22
 23#include "video/imx-ipu-v3.h"
 24#include "ipuv3-plane.h"
 25
 26static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
 27{
 28	return container_of(p, struct ipu_plane, base);
 29}
 30
 31static const uint32_t ipu_plane_formats[] = {
 32	DRM_FORMAT_ARGB1555,
 33	DRM_FORMAT_XRGB1555,
 34	DRM_FORMAT_ABGR1555,
 35	DRM_FORMAT_XBGR1555,
 36	DRM_FORMAT_RGBA5551,
 37	DRM_FORMAT_BGRA5551,
 38	DRM_FORMAT_ARGB4444,
 39	DRM_FORMAT_ARGB8888,
 40	DRM_FORMAT_XRGB8888,
 41	DRM_FORMAT_ABGR8888,
 42	DRM_FORMAT_XBGR8888,
 43	DRM_FORMAT_RGBA8888,
 44	DRM_FORMAT_RGBX8888,
 45	DRM_FORMAT_BGRA8888,
 46	DRM_FORMAT_BGRA8888,
 47	DRM_FORMAT_UYVY,
 48	DRM_FORMAT_VYUY,
 49	DRM_FORMAT_YUYV,
 50	DRM_FORMAT_YVYU,
 51	DRM_FORMAT_YUV420,
 52	DRM_FORMAT_YVU420,
 53	DRM_FORMAT_YUV422,
 54	DRM_FORMAT_YVU422,
 55	DRM_FORMAT_YUV444,
 56	DRM_FORMAT_YVU444,
 57	DRM_FORMAT_NV12,
 58	DRM_FORMAT_NV16,
 59	DRM_FORMAT_RGB565,
 60};
 61
 62int ipu_plane_irq(struct ipu_plane *ipu_plane)
 63{
 64	return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
 65				     IPU_IRQ_EOF);
 66}
 67
 68static inline unsigned long
 69drm_plane_state_to_eba(struct drm_plane_state *state)
 70{
 71	struct drm_framebuffer *fb = state->fb;
 72	struct drm_gem_cma_object *cma_obj;
 73	int x = state->src_x >> 16;
 74	int y = state->src_y >> 16;
 75
 76	cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 77	BUG_ON(!cma_obj);
 78
 79	return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y +
 80	       drm_format_plane_cpp(fb->pixel_format, 0) * x;
 81}
 82
 83static inline unsigned long
 84drm_plane_state_to_ubo(struct drm_plane_state *state)
 85{
 86	struct drm_framebuffer *fb = state->fb;
 87	struct drm_gem_cma_object *cma_obj;
 88	unsigned long eba = drm_plane_state_to_eba(state);
 89	int x = state->src_x >> 16;
 90	int y = state->src_y >> 16;
 91
 92	cma_obj = drm_fb_cma_get_gem_obj(fb, 1);
 93	BUG_ON(!cma_obj);
 94
 95	x /= drm_format_horz_chroma_subsampling(fb->pixel_format);
 96	y /= drm_format_vert_chroma_subsampling(fb->pixel_format);
 97
 98	return cma_obj->paddr + fb->offsets[1] + fb->pitches[1] * y +
 99	       drm_format_plane_cpp(fb->pixel_format, 1) * x - eba;
100}
101
102static inline unsigned long
103drm_plane_state_to_vbo(struct drm_plane_state *state)
104{
105	struct drm_framebuffer *fb = state->fb;
106	struct drm_gem_cma_object *cma_obj;
107	unsigned long eba = drm_plane_state_to_eba(state);
108	int x = state->src_x >> 16;
109	int y = state->src_y >> 16;
110
111	cma_obj = drm_fb_cma_get_gem_obj(fb, 2);
112	BUG_ON(!cma_obj);
113
114	x /= drm_format_horz_chroma_subsampling(fb->pixel_format);
115	y /= drm_format_vert_chroma_subsampling(fb->pixel_format);
116
117	return cma_obj->paddr + fb->offsets[2] + fb->pitches[2] * y +
118	       drm_format_plane_cpp(fb->pixel_format, 2) * x - eba;
119}
120
121void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
122{
123	if (!IS_ERR_OR_NULL(ipu_plane->dp))
124		ipu_dp_put(ipu_plane->dp);
125	if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
126		ipu_dmfc_put(ipu_plane->dmfc);
127	if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
128		ipu_idmac_put(ipu_plane->ipu_ch);
129}
130
131int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
132{
133	int ret;
134
135	ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
136	if (IS_ERR(ipu_plane->ipu_ch)) {
137		ret = PTR_ERR(ipu_plane->ipu_ch);
138		DRM_ERROR("failed to get idmac channel: %d\n", ret);
139		return ret;
140	}
141
142	ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
143	if (IS_ERR(ipu_plane->dmfc)) {
144		ret = PTR_ERR(ipu_plane->dmfc);
145		DRM_ERROR("failed to get dmfc: ret %d\n", ret);
146		goto err_out;
147	}
148
149	if (ipu_plane->dp_flow >= 0) {
150		ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
151		if (IS_ERR(ipu_plane->dp)) {
152			ret = PTR_ERR(ipu_plane->dp);
153			DRM_ERROR("failed to get dp flow: %d\n", ret);
154			goto err_out;
155		}
156	}
157
158	return 0;
159err_out:
160	ipu_plane_put_resources(ipu_plane);
161
162	return ret;
163}
164
165static void ipu_plane_enable(struct ipu_plane *ipu_plane)
166{
167	if (ipu_plane->dp)
168		ipu_dp_enable(ipu_plane->ipu);
169	ipu_dmfc_enable_channel(ipu_plane->dmfc);
170	ipu_idmac_enable_channel(ipu_plane->ipu_ch);
171	if (ipu_plane->dp)
172		ipu_dp_enable_channel(ipu_plane->dp);
173}
174
175static int ipu_disable_plane(struct drm_plane *plane)
176{
177	struct ipu_plane *ipu_plane = to_ipu_plane(plane);
178
179	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
180
181	ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
182
183	if (ipu_plane->dp)
184		ipu_dp_disable_channel(ipu_plane->dp);
185	ipu_idmac_disable_channel(ipu_plane->ipu_ch);
186	ipu_dmfc_disable_channel(ipu_plane->dmfc);
187	if (ipu_plane->dp)
188		ipu_dp_disable(ipu_plane->ipu);
189
190	return 0;
191}
192
193static void ipu_plane_destroy(struct drm_plane *plane)
194{
195	struct ipu_plane *ipu_plane = to_ipu_plane(plane);
196
197	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
198
199	drm_plane_cleanup(plane);
200	kfree(ipu_plane);
201}
202
203static const struct drm_plane_funcs ipu_plane_funcs = {
204	.update_plane	= drm_atomic_helper_update_plane,
205	.disable_plane	= drm_atomic_helper_disable_plane,
206	.destroy	= ipu_plane_destroy,
207	.reset		= drm_atomic_helper_plane_reset,
208	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
209	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
210};
211
212static int ipu_plane_atomic_check(struct drm_plane *plane,
213				  struct drm_plane_state *state)
214{
215	struct drm_plane_state *old_state = plane->state;
216	struct drm_crtc_state *crtc_state;
217	struct device *dev = plane->dev->dev;
218	struct drm_framebuffer *fb = state->fb;
219	struct drm_framebuffer *old_fb = old_state->fb;
220	unsigned long eba, ubo, vbo, old_ubo, old_vbo;
221	int hsub, vsub;
222
223	/* Ok to disable */
224	if (!fb)
225		return 0;
226
227	if (!state->crtc)
228		return -EINVAL;
229
230	crtc_state =
231		drm_atomic_get_existing_crtc_state(state->state, state->crtc);
232	if (WARN_ON(!crtc_state))
233		return -EINVAL;
234
235	/* CRTC should be enabled */
236	if (!crtc_state->enable)
237		return -EINVAL;
238
239	/* no scaling */
240	if (state->src_w >> 16 != state->crtc_w ||
241	    state->src_h >> 16 != state->crtc_h)
242		return -EINVAL;
243
244	switch (plane->type) {
245	case DRM_PLANE_TYPE_PRIMARY:
246		/* full plane doesn't support partial off screen */
247		if (state->crtc_x || state->crtc_y ||
248		    state->crtc_w != crtc_state->adjusted_mode.hdisplay ||
249		    state->crtc_h != crtc_state->adjusted_mode.vdisplay)
250			return -EINVAL;
251
252		/* full plane minimum width is 13 pixels */
253		if (state->crtc_w < 13)
254			return -EINVAL;
255		break;
256	case DRM_PLANE_TYPE_OVERLAY:
257		if (state->crtc_x < 0 || state->crtc_y < 0)
258			return -EINVAL;
259
260		if (state->crtc_x + state->crtc_w >
261		    crtc_state->adjusted_mode.hdisplay)
262			return -EINVAL;
263		if (state->crtc_y + state->crtc_h >
264		    crtc_state->adjusted_mode.vdisplay)
265			return -EINVAL;
266		break;
267	default:
268		dev_warn(dev, "Unsupported plane type\n");
269		return -EINVAL;
270	}
271
272	if (state->crtc_h < 2)
273		return -EINVAL;
274
275	/*
276	 * We support resizing active plane or changing its format by
277	 * forcing CRTC mode change in plane's ->atomic_check callback
278	 * and disabling all affected active planes in CRTC's ->atomic_disable
279	 * callback.  The planes will be reenabled in plane's ->atomic_update
280	 * callback.
281	 */
282	if (old_fb && (state->src_w != old_state->src_w ||
283			      state->src_h != old_state->src_h ||
284			      fb->pixel_format != old_fb->pixel_format))
285		crtc_state->mode_changed = true;
286
287	eba = drm_plane_state_to_eba(state);
288
289	if (eba & 0x7)
290		return -EINVAL;
291
292	if (fb->pitches[0] < 1 || fb->pitches[0] > 16384)
293		return -EINVAL;
294
295	if (old_fb && fb->pitches[0] != old_fb->pitches[0])
296		crtc_state->mode_changed = true;
297
298	switch (fb->pixel_format) {
299	case DRM_FORMAT_YUV420:
300	case DRM_FORMAT_YVU420:
301	case DRM_FORMAT_YUV422:
302	case DRM_FORMAT_YVU422:
303	case DRM_FORMAT_YUV444:
304	case DRM_FORMAT_YVU444:
305		/*
306		 * Multiplanar formats have to meet the following restrictions:
307		 * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
308		 * - EBA, UBO and VBO are a multiple of 8
309		 * - UBO and VBO are unsigned and not larger than 0xfffff8
310		 * - Only EBA may be changed while scanout is active
311		 * - The strides of U and V planes must be identical.
312		 */
313		vbo = drm_plane_state_to_vbo(state);
314
315		if (vbo & 0x7 || vbo > 0xfffff8)
316			return -EINVAL;
317
318		if (old_fb && (fb->pixel_format == old_fb->pixel_format)) {
319			old_vbo = drm_plane_state_to_vbo(old_state);
320			if (vbo != old_vbo)
321				crtc_state->mode_changed = true;
322		}
323
324		if (fb->pitches[1] != fb->pitches[2])
325			return -EINVAL;
326
327		/* fall-through */
328	case DRM_FORMAT_NV12:
329	case DRM_FORMAT_NV16:
330		ubo = drm_plane_state_to_ubo(state);
331
332		if (ubo & 0x7 || ubo > 0xfffff8)
333			return -EINVAL;
334
335		if (old_fb && (fb->pixel_format == old_fb->pixel_format)) {
336			old_ubo = drm_plane_state_to_ubo(old_state);
337			if (ubo != old_ubo)
338				crtc_state->mode_changed = true;
339		}
340
341		if (fb->pitches[1] < 1 || fb->pitches[1] > 16384)
342			return -EINVAL;
343
344		if (old_fb && old_fb->pitches[1] != fb->pitches[1])
345			crtc_state->mode_changed = true;
346
347		/*
348		 * The x/y offsets must be even in case of horizontal/vertical
349		 * chroma subsampling.
350		 */
351		hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
352		vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
353		if (((state->src_x >> 16) & (hsub - 1)) ||
354		    ((state->src_y >> 16) & (vsub - 1)))
355			return -EINVAL;
356	}
357
358	return 0;
359}
360
361static void ipu_plane_atomic_disable(struct drm_plane *plane,
362				     struct drm_plane_state *old_state)
363{
364	ipu_disable_plane(plane);
365}
366
367static void ipu_plane_atomic_update(struct drm_plane *plane,
368				    struct drm_plane_state *old_state)
369{
370	struct ipu_plane *ipu_plane = to_ipu_plane(plane);
371	struct drm_plane_state *state = plane->state;
372	struct drm_crtc_state *crtc_state = state->crtc->state;
373	struct drm_framebuffer *fb = state->fb;
374	unsigned long eba, ubo, vbo;
375	enum ipu_color_space ics;
376	int active;
377
378	eba = drm_plane_state_to_eba(state);
379
380	if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) {
381		active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
382		ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
383		ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
384		return;
385	}
386
387	switch (ipu_plane->dp_flow) {
388	case IPU_DP_FLOW_SYNC_BG:
389		ipu_dp_setup_channel(ipu_plane->dp,
390					IPUV3_COLORSPACE_RGB,
391					IPUV3_COLORSPACE_RGB);
392		ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
393		break;
394	case IPU_DP_FLOW_SYNC_FG:
395		ics = ipu_drm_fourcc_to_colorspace(state->fb->pixel_format);
396		ipu_dp_setup_channel(ipu_plane->dp, ics,
397					IPUV3_COLORSPACE_UNKNOWN);
398		ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x,
399					state->crtc_y);
400		/* Enable local alpha on partial plane */
401		switch (state->fb->pixel_format) {
402		case DRM_FORMAT_ARGB1555:
403		case DRM_FORMAT_ABGR1555:
404		case DRM_FORMAT_RGBA5551:
405		case DRM_FORMAT_BGRA5551:
406		case DRM_FORMAT_ARGB4444:
407		case DRM_FORMAT_ARGB8888:
408		case DRM_FORMAT_ABGR8888:
409		case DRM_FORMAT_RGBA8888:
410		case DRM_FORMAT_BGRA8888:
411			ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
412			break;
413		default:
414			ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
415			break;
416		}
417	}
418
419	ipu_dmfc_config_wait4eot(ipu_plane->dmfc, state->crtc_w);
420
421	ipu_cpmem_zero(ipu_plane->ipu_ch);
422	ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16,
423					state->src_h >> 16);
424	ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->pixel_format);
425	ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
426	ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
427	ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]);
428	switch (fb->pixel_format) {
429	case DRM_FORMAT_YUV420:
430	case DRM_FORMAT_YVU420:
431	case DRM_FORMAT_YUV422:
432	case DRM_FORMAT_YVU422:
433	case DRM_FORMAT_YUV444:
434	case DRM_FORMAT_YVU444:
435		ubo = drm_plane_state_to_ubo(state);
436		vbo = drm_plane_state_to_vbo(state);
437		if (fb->pixel_format == DRM_FORMAT_YVU420 ||
438		    fb->pixel_format == DRM_FORMAT_YVU422 ||
439		    fb->pixel_format == DRM_FORMAT_YVU444)
440			swap(ubo, vbo);
441
442		ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
443					      fb->pitches[1], ubo, vbo);
444
445		dev_dbg(ipu_plane->base.dev->dev,
446			"phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo,
447			state->src_x >> 16, state->src_y >> 16);
448		break;
449	case DRM_FORMAT_NV12:
450	case DRM_FORMAT_NV16:
451		ubo = drm_plane_state_to_ubo(state);
452
453		ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
454					      fb->pitches[1], ubo, ubo);
455
456		dev_dbg(ipu_plane->base.dev->dev,
457			"phy = %lu %lu, x = %d, y = %d", eba, ubo,
458			state->src_x >> 16, state->src_y >> 16);
459		break;
460	default:
461		dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d",
462			eba, state->src_x >> 16, state->src_y >> 16);
463		break;
464	}
465	ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
466	ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
467	ipu_plane_enable(ipu_plane);
468}
469
470static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
471	.prepare_fb = drm_fb_cma_prepare_fb,
472	.atomic_check = ipu_plane_atomic_check,
473	.atomic_disable = ipu_plane_atomic_disable,
474	.atomic_update = ipu_plane_atomic_update,
475};
476
477struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
478				 int dma, int dp, unsigned int possible_crtcs,
479				 enum drm_plane_type type)
480{
481	struct ipu_plane *ipu_plane;
482	int ret;
483
484	DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
485		      dma, dp, possible_crtcs);
486
487	ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
488	if (!ipu_plane) {
489		DRM_ERROR("failed to allocate plane\n");
490		return ERR_PTR(-ENOMEM);
491	}
492
493	ipu_plane->ipu = ipu;
494	ipu_plane->dma = dma;
495	ipu_plane->dp_flow = dp;
496
497	ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
498				       &ipu_plane_funcs, ipu_plane_formats,
499				       ARRAY_SIZE(ipu_plane_formats), type,
500				       NULL);
501	if (ret) {
502		DRM_ERROR("failed to initialize plane\n");
503		kfree(ipu_plane);
504		return ERR_PTR(ret);
505	}
506
507	drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
508
509	return ipu_plane;
510}