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