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