xref: /linux/drivers/gpu/drm/imx/dc/dc-plane.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1*711a3b87SLiu Ying // SPDX-License-Identifier: GPL-2.0+
2*711a3b87SLiu Ying /*
3*711a3b87SLiu Ying  * Copyright 2024 NXP
4*711a3b87SLiu Ying  */
5*711a3b87SLiu Ying 
6*711a3b87SLiu Ying #include <linux/container_of.h>
7*711a3b87SLiu Ying 
8*711a3b87SLiu Ying #include <drm/drm_atomic.h>
9*711a3b87SLiu Ying #include <drm/drm_atomic_helper.h>
10*711a3b87SLiu Ying #include <drm/drm_atomic_state_helper.h>
11*711a3b87SLiu Ying #include <drm/drm_crtc.h>
12*711a3b87SLiu Ying #include <drm/drm_drv.h>
13*711a3b87SLiu Ying #include <drm/drm_fb_dma_helper.h>
14*711a3b87SLiu Ying #include <drm/drm_fourcc.h>
15*711a3b87SLiu Ying #include <drm/drm_framebuffer.h>
16*711a3b87SLiu Ying #include <drm/drm_gem_atomic_helper.h>
17*711a3b87SLiu Ying #include <drm/drm_plane_helper.h>
18*711a3b87SLiu Ying #include <drm/drm_print.h>
19*711a3b87SLiu Ying 
20*711a3b87SLiu Ying #include "dc-drv.h"
21*711a3b87SLiu Ying #include "dc-fu.h"
22*711a3b87SLiu Ying #include "dc-kms.h"
23*711a3b87SLiu Ying 
24*711a3b87SLiu Ying #define DC_PLANE_MAX_PITCH	0x10000
25*711a3b87SLiu Ying #define DC_PLANE_MAX_PIX_CNT	8192
26*711a3b87SLiu Ying 
27*711a3b87SLiu Ying #define dc_plane_dbg(plane, fmt, ...)					\
28*711a3b87SLiu Ying do {									\
29*711a3b87SLiu Ying 	struct drm_plane *_plane = (plane);				\
30*711a3b87SLiu Ying 	drm_dbg_kms(_plane->dev, "[PLANE:%d:%s] " fmt,			\
31*711a3b87SLiu Ying 		    _plane->base.id, _plane->name, ##__VA_ARGS__);	\
32*711a3b87SLiu Ying } while (0)
33*711a3b87SLiu Ying 
34*711a3b87SLiu Ying static const uint32_t dc_plane_formats[] = {
35*711a3b87SLiu Ying 	DRM_FORMAT_XRGB8888,
36*711a3b87SLiu Ying };
37*711a3b87SLiu Ying 
38*711a3b87SLiu Ying static const struct drm_plane_funcs dc_plane_funcs = {
39*711a3b87SLiu Ying 	.update_plane		= drm_atomic_helper_update_plane,
40*711a3b87SLiu Ying 	.disable_plane		= drm_atomic_helper_disable_plane,
41*711a3b87SLiu Ying 	.destroy		= drm_plane_cleanup,
42*711a3b87SLiu Ying 	.reset			= drm_atomic_helper_plane_reset,
43*711a3b87SLiu Ying 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
44*711a3b87SLiu Ying 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
45*711a3b87SLiu Ying };
46*711a3b87SLiu Ying 
47*711a3b87SLiu Ying static inline struct dc_plane *to_dc_plane(struct drm_plane *plane)
48*711a3b87SLiu Ying {
49*711a3b87SLiu Ying 	return container_of(plane, struct dc_plane, base);
50*711a3b87SLiu Ying }
51*711a3b87SLiu Ying 
52*711a3b87SLiu Ying static int dc_plane_check_max_source_resolution(struct drm_plane_state *state)
53*711a3b87SLiu Ying {
54*711a3b87SLiu Ying 	int src_h = drm_rect_height(&state->src) >> 16;
55*711a3b87SLiu Ying 	int src_w = drm_rect_width(&state->src) >> 16;
56*711a3b87SLiu Ying 
57*711a3b87SLiu Ying 	if (src_w > DC_PLANE_MAX_PIX_CNT || src_h > DC_PLANE_MAX_PIX_CNT) {
58*711a3b87SLiu Ying 		dc_plane_dbg(state->plane, "invalid source resolution\n");
59*711a3b87SLiu Ying 		return -EINVAL;
60*711a3b87SLiu Ying 	}
61*711a3b87SLiu Ying 
62*711a3b87SLiu Ying 	return 0;
63*711a3b87SLiu Ying }
64*711a3b87SLiu Ying 
65*711a3b87SLiu Ying static int dc_plane_check_fb(struct drm_plane_state *state)
66*711a3b87SLiu Ying {
67*711a3b87SLiu Ying 	struct drm_framebuffer *fb = state->fb;
68*711a3b87SLiu Ying 	dma_addr_t baseaddr = drm_fb_dma_get_gem_addr(fb, state, 0);
69*711a3b87SLiu Ying 
70*711a3b87SLiu Ying 	/* base address alignment */
71*711a3b87SLiu Ying 	if (baseaddr & 0x3) {
72*711a3b87SLiu Ying 		dc_plane_dbg(state->plane, "fb bad baddr alignment\n");
73*711a3b87SLiu Ying 		return -EINVAL;
74*711a3b87SLiu Ying 	}
75*711a3b87SLiu Ying 
76*711a3b87SLiu Ying 	/* pitches[0] range */
77*711a3b87SLiu Ying 	if (fb->pitches[0] > DC_PLANE_MAX_PITCH) {
78*711a3b87SLiu Ying 		dc_plane_dbg(state->plane, "fb pitches[0] is out of range\n");
79*711a3b87SLiu Ying 		return -EINVAL;
80*711a3b87SLiu Ying 	}
81*711a3b87SLiu Ying 
82*711a3b87SLiu Ying 	/* pitches[0] alignment */
83*711a3b87SLiu Ying 	if (fb->pitches[0] & 0x3) {
84*711a3b87SLiu Ying 		dc_plane_dbg(state->plane, "fb bad pitches[0] alignment\n");
85*711a3b87SLiu Ying 		return -EINVAL;
86*711a3b87SLiu Ying 	}
87*711a3b87SLiu Ying 
88*711a3b87SLiu Ying 	return 0;
89*711a3b87SLiu Ying }
90*711a3b87SLiu Ying 
91*711a3b87SLiu Ying static int
92*711a3b87SLiu Ying dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
93*711a3b87SLiu Ying {
94*711a3b87SLiu Ying 	struct drm_plane_state *plane_state =
95*711a3b87SLiu Ying 				drm_atomic_get_new_plane_state(state, plane);
96*711a3b87SLiu Ying 	struct drm_crtc_state *crtc_state;
97*711a3b87SLiu Ying 	int ret;
98*711a3b87SLiu Ying 
99*711a3b87SLiu Ying 	/* ok to disable */
100*711a3b87SLiu Ying 	if (!plane_state->fb)
101*711a3b87SLiu Ying 		return 0;
102*711a3b87SLiu Ying 
103*711a3b87SLiu Ying 	if (!plane_state->crtc) {
104*711a3b87SLiu Ying 		dc_plane_dbg(plane, "no CRTC in plane state\n");
105*711a3b87SLiu Ying 		return -EINVAL;
106*711a3b87SLiu Ying 	}
107*711a3b87SLiu Ying 
108*711a3b87SLiu Ying 	crtc_state =
109*711a3b87SLiu Ying 		drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
110*711a3b87SLiu Ying 	if (WARN_ON(!crtc_state))
111*711a3b87SLiu Ying 		return -EINVAL;
112*711a3b87SLiu Ying 
113*711a3b87SLiu Ying 	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
114*711a3b87SLiu Ying 						  DRM_PLANE_NO_SCALING,
115*711a3b87SLiu Ying 						  DRM_PLANE_NO_SCALING,
116*711a3b87SLiu Ying 						  true, false);
117*711a3b87SLiu Ying 	if (ret) {
118*711a3b87SLiu Ying 		dc_plane_dbg(plane, "failed to check plane state: %d\n", ret);
119*711a3b87SLiu Ying 		return ret;
120*711a3b87SLiu Ying 	}
121*711a3b87SLiu Ying 
122*711a3b87SLiu Ying 	ret = dc_plane_check_max_source_resolution(plane_state);
123*711a3b87SLiu Ying 	if (ret)
124*711a3b87SLiu Ying 		return ret;
125*711a3b87SLiu Ying 
126*711a3b87SLiu Ying 	return dc_plane_check_fb(plane_state);
127*711a3b87SLiu Ying }
128*711a3b87SLiu Ying 
129*711a3b87SLiu Ying static void
130*711a3b87SLiu Ying dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
131*711a3b87SLiu Ying {
132*711a3b87SLiu Ying 	struct drm_plane_state *new_state =
133*711a3b87SLiu Ying 				drm_atomic_get_new_plane_state(state, plane);
134*711a3b87SLiu Ying 	struct dc_plane *dplane = to_dc_plane(plane);
135*711a3b87SLiu Ying 	struct drm_framebuffer *fb = new_state->fb;
136*711a3b87SLiu Ying 	const struct dc_fu_ops *fu_ops;
137*711a3b87SLiu Ying 	struct dc_lb *lb = dplane->lb;
138*711a3b87SLiu Ying 	struct dc_fu *fu = dplane->fu;
139*711a3b87SLiu Ying 	dma_addr_t baseaddr;
140*711a3b87SLiu Ying 	int src_w, src_h;
141*711a3b87SLiu Ying 	int idx;
142*711a3b87SLiu Ying 
143*711a3b87SLiu Ying 	if (!drm_dev_enter(plane->dev, &idx))
144*711a3b87SLiu Ying 		return;
145*711a3b87SLiu Ying 
146*711a3b87SLiu Ying 	src_w = drm_rect_width(&new_state->src) >> 16;
147*711a3b87SLiu Ying 	src_h = drm_rect_height(&new_state->src) >> 16;
148*711a3b87SLiu Ying 
149*711a3b87SLiu Ying 	baseaddr = drm_fb_dma_get_gem_addr(fb, new_state, 0);
150*711a3b87SLiu Ying 
151*711a3b87SLiu Ying 	fu_ops = dc_fu_get_ops(dplane->fu);
152*711a3b87SLiu Ying 
153*711a3b87SLiu Ying 	fu_ops->set_layerblend(fu, lb);
154*711a3b87SLiu Ying 	fu_ops->set_burstlength(fu, baseaddr);
155*711a3b87SLiu Ying 	fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, fb->pitches[0]);
156*711a3b87SLiu Ying 	fu_ops->set_src_buf_dimensions(fu, DC_FETCHUNIT_FRAC0, src_w, src_h);
157*711a3b87SLiu Ying 	fu_ops->set_fmt(fu, DC_FETCHUNIT_FRAC0, fb->format);
158*711a3b87SLiu Ying 	fu_ops->set_framedimensions(fu, src_w, src_h);
159*711a3b87SLiu Ying 	fu_ops->set_baseaddress(fu, DC_FETCHUNIT_FRAC0, baseaddr);
160*711a3b87SLiu Ying 	fu_ops->enable_src_buf(fu, DC_FETCHUNIT_FRAC0);
161*711a3b87SLiu Ying 
162*711a3b87SLiu Ying 	dc_plane_dbg(plane, "uses %s\n", fu_ops->get_name(fu));
163*711a3b87SLiu Ying 
164*711a3b87SLiu Ying 	dc_lb_pec_dynamic_prim_sel(lb, dc_cf_get_link_id(dplane->cf));
165*711a3b87SLiu Ying 	dc_lb_pec_dynamic_sec_sel(lb, fu_ops->get_link_id(fu));
166*711a3b87SLiu Ying 	dc_lb_mode(lb, LB_BLEND);
167*711a3b87SLiu Ying 	dc_lb_position(lb, new_state->dst.x1, new_state->dst.y1);
168*711a3b87SLiu Ying 	dc_lb_pec_clken(lb, CLKEN_AUTOMATIC);
169*711a3b87SLiu Ying 
170*711a3b87SLiu Ying 	dc_plane_dbg(plane, "uses LayerBlend%d\n", dc_lb_get_id(lb));
171*711a3b87SLiu Ying 
172*711a3b87SLiu Ying 	/* set ExtDst's source to LayerBlend */
173*711a3b87SLiu Ying 	dc_ed_pec_src_sel(dplane->ed, dc_lb_get_link_id(lb));
174*711a3b87SLiu Ying 
175*711a3b87SLiu Ying 	drm_dev_exit(idx);
176*711a3b87SLiu Ying }
177*711a3b87SLiu Ying 
178*711a3b87SLiu Ying static void dc_plane_atomic_disable(struct drm_plane *plane,
179*711a3b87SLiu Ying 				    struct drm_atomic_state *state)
180*711a3b87SLiu Ying {
181*711a3b87SLiu Ying 	struct dc_plane *dplane = to_dc_plane(plane);
182*711a3b87SLiu Ying 	const struct dc_fu_ops *fu_ops;
183*711a3b87SLiu Ying 	int idx;
184*711a3b87SLiu Ying 
185*711a3b87SLiu Ying 	if (!drm_dev_enter(plane->dev, &idx))
186*711a3b87SLiu Ying 		return;
187*711a3b87SLiu Ying 
188*711a3b87SLiu Ying 	/* disable fetchunit in shadow */
189*711a3b87SLiu Ying 	fu_ops = dc_fu_get_ops(dplane->fu);
190*711a3b87SLiu Ying 	fu_ops->disable_src_buf(dplane->fu, DC_FETCHUNIT_FRAC0);
191*711a3b87SLiu Ying 
192*711a3b87SLiu Ying 	/* set ExtDst's source to ConstFrame */
193*711a3b87SLiu Ying 	dc_ed_pec_src_sel(dplane->ed, dc_cf_get_link_id(dplane->cf));
194*711a3b87SLiu Ying 
195*711a3b87SLiu Ying 	drm_dev_exit(idx);
196*711a3b87SLiu Ying }
197*711a3b87SLiu Ying 
198*711a3b87SLiu Ying static const struct drm_plane_helper_funcs dc_plane_helper_funcs = {
199*711a3b87SLiu Ying 	.atomic_check = dc_plane_atomic_check,
200*711a3b87SLiu Ying 	.atomic_update = dc_plane_atomic_update,
201*711a3b87SLiu Ying 	.atomic_disable = dc_plane_atomic_disable,
202*711a3b87SLiu Ying };
203*711a3b87SLiu Ying 
204*711a3b87SLiu Ying int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane)
205*711a3b87SLiu Ying {
206*711a3b87SLiu Ying 	struct drm_plane *plane = &dc_plane->base;
207*711a3b87SLiu Ying 	int ret;
208*711a3b87SLiu Ying 
209*711a3b87SLiu Ying 	ret = drm_universal_plane_init(&dc_drm->base, plane, 0, &dc_plane_funcs,
210*711a3b87SLiu Ying 				       dc_plane_formats,
211*711a3b87SLiu Ying 				       ARRAY_SIZE(dc_plane_formats),
212*711a3b87SLiu Ying 				       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
213*711a3b87SLiu Ying 	if (ret)
214*711a3b87SLiu Ying 		return ret;
215*711a3b87SLiu Ying 
216*711a3b87SLiu Ying 	drm_plane_helper_add(plane, &dc_plane_helper_funcs);
217*711a3b87SLiu Ying 
218*711a3b87SLiu Ying 	dc_plane->fu = dc_drm->pe->fu_disp[plane->index];
219*711a3b87SLiu Ying 	dc_plane->cf = dc_drm->pe->cf_cont[plane->index];
220*711a3b87SLiu Ying 	dc_plane->lb = dc_drm->pe->lb[plane->index];
221*711a3b87SLiu Ying 	dc_plane->ed = dc_drm->pe->ed_cont[plane->index];
222*711a3b87SLiu Ying 
223*711a3b87SLiu Ying 	return 0;
224*711a3b87SLiu Ying }
225