1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include "mdp5_kms.h"
20
21#define MAX_PLANE 4
22
23struct mdp5_plane {
24 struct drm_plane base;
25 const char *name;
26
27 enum mdp5_pipe pipe;
28
29 spinlock_t pipe_lock;
30 uint32_t reg_offset;
31
32 uint32_t flush_mask;
33
34 uint32_t nformats;
35 uint32_t formats[32];
36
37 bool enabled;
38};
39#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)
40
41static int mdp5_plane_mode_set(struct drm_plane *plane,
42 struct drm_crtc *crtc, struct drm_framebuffer *fb,
43 int crtc_x, int crtc_y,
44 unsigned int crtc_w, unsigned int crtc_h,
45 uint32_t src_x, uint32_t src_y,
46 uint32_t src_w, uint32_t src_h);
47static void set_scanout_locked(struct drm_plane *plane,
48 struct drm_framebuffer *fb);
49
50static struct mdp5_kms *get_kms(struct drm_plane *plane)
51{
52 struct msm_drm_private *priv = plane->dev->dev_private;
53 return to_mdp5_kms(to_mdp_kms(priv->kms));
54}
55
56static bool plane_enabled(struct drm_plane_state *state)
57{
58 return state->fb && state->crtc;
59}
60
61static int mdp5_plane_disable(struct drm_plane *plane)
62{
63 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
64 struct mdp5_kms *mdp5_kms = get_kms(plane);
65 enum mdp5_pipe pipe = mdp5_plane->pipe;
66
67 DBG("%s: disable", mdp5_plane->name);
68
69 if (mdp5_kms) {
70
71 mdp5_smp_release(mdp5_kms->smp, pipe);
72 }
73
74 return 0;
75}
76
77static void mdp5_plane_destroy(struct drm_plane *plane)
78{
79 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
80
81 drm_plane_helper_disable(plane);
82 drm_plane_cleanup(plane);
83
84 kfree(mdp5_plane);
85}
86
87
88void mdp5_plane_install_properties(struct drm_plane *plane,
89 struct drm_mode_object *obj)
90{
91
92}
93
94int mdp5_plane_set_property(struct drm_plane *plane,
95 struct drm_property *property, uint64_t val)
96{
97
98 return -EINVAL;
99}
100
101static void mdp5_plane_reset(struct drm_plane *plane)
102{
103 struct mdp5_plane_state *mdp5_state;
104
105 if (plane->state && plane->state->fb)
106 drm_framebuffer_unreference(plane->state->fb);
107
108 kfree(to_mdp5_plane_state(plane->state));
109 mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);
110
111 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
112 mdp5_state->zpos = 0;
113 } else {
114 mdp5_state->zpos = 1 + drm_plane_index(plane);
115 }
116
117 plane->state = &mdp5_state->base;
118}
119
120static struct drm_plane_state *
121mdp5_plane_duplicate_state(struct drm_plane *plane)
122{
123 struct mdp5_plane_state *mdp5_state;
124
125 if (WARN_ON(!plane->state))
126 return NULL;
127
128 mdp5_state = kmemdup(to_mdp5_plane_state(plane->state),
129 sizeof(*mdp5_state), GFP_KERNEL);
130
131 if (mdp5_state && mdp5_state->base.fb)
132 drm_framebuffer_reference(mdp5_state->base.fb);
133
134 mdp5_state->mode_changed = false;
135 mdp5_state->pending = false;
136
137 return &mdp5_state->base;
138}
139
140static void mdp5_plane_destroy_state(struct drm_plane *plane,
141 struct drm_plane_state *state)
142{
143 if (state->fb)
144 drm_framebuffer_unreference(state->fb);
145
146 kfree(to_mdp5_plane_state(state));
147}
148
149static const struct drm_plane_funcs mdp5_plane_funcs = {
150 .update_plane = drm_atomic_helper_update_plane,
151 .disable_plane = drm_atomic_helper_disable_plane,
152 .destroy = mdp5_plane_destroy,
153 .set_property = mdp5_plane_set_property,
154 .reset = mdp5_plane_reset,
155 .atomic_duplicate_state = mdp5_plane_duplicate_state,
156 .atomic_destroy_state = mdp5_plane_destroy_state,
157};
158
159static int mdp5_plane_prepare_fb(struct drm_plane *plane,
160 struct drm_framebuffer *fb)
161{
162 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
163 struct mdp5_kms *mdp5_kms = get_kms(plane);
164
165 DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id);
166 return msm_framebuffer_prepare(fb, mdp5_kms->id);
167}
168
169static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
170 struct drm_framebuffer *fb)
171{
172 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
173 struct mdp5_kms *mdp5_kms = get_kms(plane);
174
175 DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id);
176 msm_framebuffer_cleanup(fb, mdp5_kms->id);
177}
178
179static int mdp5_plane_atomic_check(struct drm_plane *plane,
180 struct drm_plane_state *state)
181{
182 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
183 struct drm_plane_state *old_state = plane->state;
184
185 DBG("%s: check (%d -> %d)", mdp5_plane->name,
186 plane_enabled(old_state), plane_enabled(state));
187
188 if (plane_enabled(state) && plane_enabled(old_state)) {
189
190 bool full_modeset = false;
191 if (state->fb->pixel_format != old_state->fb->pixel_format) {
192 DBG("%s: pixel_format change!", mdp5_plane->name);
193 full_modeset = true;
194 }
195 if (state->src_w != old_state->src_w) {
196 DBG("%s: src_w change!", mdp5_plane->name);
197 full_modeset = true;
198 }
199 if (to_mdp5_plane_state(old_state)->pending) {
200 DBG("%s: still pending!", mdp5_plane->name);
201 full_modeset = true;
202 }
203 if (full_modeset) {
204 struct drm_crtc_state *crtc_state =
205 drm_atomic_get_crtc_state(state->state, state->crtc);
206 crtc_state->mode_changed = true;
207 to_mdp5_plane_state(state)->mode_changed = true;
208 }
209 } else {
210 to_mdp5_plane_state(state)->mode_changed = true;
211 }
212
213 return 0;
214}
215
216static void mdp5_plane_atomic_update(struct drm_plane *plane,
217 struct drm_plane_state *old_state)
218{
219 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
220 struct drm_plane_state *state = plane->state;
221
222 DBG("%s: update", mdp5_plane->name);
223
224 if (!plane_enabled(state)) {
225 to_mdp5_plane_state(state)->pending = true;
226 mdp5_plane_disable(plane);
227 } else if (to_mdp5_plane_state(state)->mode_changed) {
228 int ret;
229 to_mdp5_plane_state(state)->pending = true;
230 ret = mdp5_plane_mode_set(plane,
231 state->crtc, state->fb,
232 state->crtc_x, state->crtc_y,
233 state->crtc_w, state->crtc_h,
234 state->src_x, state->src_y,
235 state->src_w, state->src_h);
236
237 WARN_ON(ret < 0);
238 } else {
239 unsigned long flags;
240 spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
241 set_scanout_locked(plane, state->fb);
242 spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);
243 }
244}
245
246static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {
247 .prepare_fb = mdp5_plane_prepare_fb,
248 .cleanup_fb = mdp5_plane_cleanup_fb,
249 .atomic_check = mdp5_plane_atomic_check,
250 .atomic_update = mdp5_plane_atomic_update,
251};
252
253static void set_scanout_locked(struct drm_plane *plane,
254 struct drm_framebuffer *fb)
255{
256 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
257 struct mdp5_kms *mdp5_kms = get_kms(plane);
258 enum mdp5_pipe pipe = mdp5_plane->pipe;
259
260 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),
261 MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
262 MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
263
264 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),
265 MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
266 MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
267
268 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe),
269 msm_framebuffer_iova(fb, mdp5_kms->id, 0));
270 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe),
271 msm_framebuffer_iova(fb, mdp5_kms->id, 1));
272 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),
273 msm_framebuffer_iova(fb, mdp5_kms->id, 2));
274 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),
275 msm_framebuffer_iova(fb, mdp5_kms->id, 4));
276
277 plane->fb = fb;
278}
279
280static int mdp5_plane_mode_set(struct drm_plane *plane,
281 struct drm_crtc *crtc, struct drm_framebuffer *fb,
282 int crtc_x, int crtc_y,
283 unsigned int crtc_w, unsigned int crtc_h,
284 uint32_t src_x, uint32_t src_y,
285 uint32_t src_w, uint32_t src_h)
286{
287 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
288 struct mdp5_kms *mdp5_kms = get_kms(plane);
289 enum mdp5_pipe pipe = mdp5_plane->pipe;
290 const struct mdp_format *format;
291 uint32_t nplanes, config = 0;
292 uint32_t phasex_step = 0, phasey_step = 0;
293 uint32_t hdecm = 0, vdecm = 0;
294 unsigned long flags;
295 int ret;
296
297 nplanes = drm_format_num_planes(fb->pixel_format);
298
299
300 if (WARN_ON(nplanes > pipe2nclients(pipe)))
301 return -EINVAL;
302
303
304 src_x = src_x >> 16;
305 src_y = src_y >> 16;
306 src_w = src_w >> 16;
307 src_h = src_h >> 16;
308
309 DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name,
310 fb->base.id, src_x, src_y, src_w, src_h,
311 crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
312
313
314 ret = mdp5_smp_request(mdp5_kms->smp,
315 mdp5_plane->pipe, fb->pixel_format, src_w);
316 if (ret)
317 return ret;
318
319
320
321
322
323
324
325 mdp5_smp_configure(mdp5_kms->smp, pipe);
326
327 if (src_w != crtc_w) {
328 config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN;
329
330 }
331
332 if (src_h != crtc_h) {
333 config |= MDP5_PIPE_SCALE_CONFIG_SCALEY_EN;
334
335 }
336
337 spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
338
339 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),
340 MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) |
341 MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h));
342
343 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe),
344 MDP5_PIPE_SRC_SIZE_WIDTH(src_w) |
345 MDP5_PIPE_SRC_SIZE_HEIGHT(src_h));
346
347 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe),
348 MDP5_PIPE_SRC_XY_X(src_x) |
349 MDP5_PIPE_SRC_XY_Y(src_y));
350
351 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe),
352 MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) |
353 MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h));
354
355 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe),
356 MDP5_PIPE_OUT_XY_X(crtc_x) |
357 MDP5_PIPE_OUT_XY_Y(crtc_y));
358
359 format = to_mdp_format(msm_framebuffer_format(fb));
360
361 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),
362 MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
363 MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
364 MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
365 MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
366 COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
367 MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
368 MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
369 COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |
370 MDP5_PIPE_SRC_FORMAT_NUM_PLANES(nplanes - 1) |
371 MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(CHROMA_RGB));
372
373 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),
374 MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
375 MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
376 MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
377 MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
378
379 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),
380 MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));
381
382
383 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);
384
385 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step);
386 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), phasey_step);
387 mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),
388 MDP5_PIPE_DECIMATION_VERT(vdecm) |
389 MDP5_PIPE_DECIMATION_HORZ(hdecm));
390 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe),
391 MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(SCALE_FILTER_NEAREST) |
392 MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(SCALE_FILTER_NEAREST) |
393 MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(SCALE_FILTER_NEAREST) |
394 MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(SCALE_FILTER_NEAREST) |
395 MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) |
396 MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST));
397
398 set_scanout_locked(plane, fb);
399
400 spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);
401
402 return ret;
403}
404
405void mdp5_plane_complete_flip(struct drm_plane *plane)
406{
407 struct mdp5_kms *mdp5_kms = get_kms(plane);
408 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
409 enum mdp5_pipe pipe = mdp5_plane->pipe;
410
411 DBG("%s: complete flip", mdp5_plane->name);
412
413 mdp5_smp_commit(mdp5_kms->smp, pipe);
414
415 to_mdp5_plane_state(plane->state)->pending = false;
416}
417
418enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)
419{
420 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
421 return mdp5_plane->pipe;
422}
423
424uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
425{
426 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
427
428 return mdp5_plane->flush_mask;
429}
430
431
432struct drm_plane *mdp5_plane_init(struct drm_device *dev,
433 enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset)
434{
435 struct drm_plane *plane = NULL;
436 struct mdp5_plane *mdp5_plane;
437 int ret;
438 enum drm_plane_type type;
439
440 mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL);
441 if (!mdp5_plane) {
442 ret = -ENOMEM;
443 goto fail;
444 }
445
446 plane = &mdp5_plane->base;
447
448 mdp5_plane->pipe = pipe;
449 mdp5_plane->name = pipe2name(pipe);
450
451 mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats,
452 ARRAY_SIZE(mdp5_plane->formats));
453
454 mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
455 mdp5_plane->reg_offset = reg_offset;
456 spin_lock_init(&mdp5_plane->pipe_lock);
457
458 type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
459 ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
460 mdp5_plane->formats, mdp5_plane->nformats,
461 type);
462 if (ret)
463 goto fail;
464
465 drm_plane_helper_add(plane, &mdp5_plane_helper_funcs);
466
467 mdp5_plane_install_properties(plane, &plane->base);
468
469 return plane;
470
471fail:
472 if (plane)
473 mdp5_plane_destroy(plane);
474
475 return ERR_PTR(ret);
476}
477