Commit | Line | Data |
---|---|---|
06c0dd96 | 1 | /* |
bef799fb | 2 | * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved. |
06c0dd96 RC |
3 | * Copyright (C) 2013 Red Hat |
4 | * Author: Rob Clark <robdclark@gmail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published by | |
8 | * the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "mdp5_kms.h" | |
20 | ||
06c0dd96 RC |
21 | struct mdp5_plane { |
22 | struct drm_plane base; | |
23 | const char *name; | |
24 | ||
25 | enum mdp5_pipe pipe; | |
26 | ||
0deed25b SV |
27 | spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ |
28 | uint32_t reg_offset; | |
3498409f | 29 | uint32_t caps; |
0deed25b SV |
30 | |
31 | uint32_t flush_mask; /* used to commit pipe registers */ | |
32 | ||
06c0dd96 RC |
33 | uint32_t nformats; |
34 | uint32_t formats[32]; | |
06c0dd96 RC |
35 | }; |
36 | #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) | |
37 | ||
ed851963 RC |
38 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
39 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
40 | int crtc_x, int crtc_y, | |
41 | unsigned int crtc_w, unsigned int crtc_h, | |
42 | uint32_t src_x, uint32_t src_y, | |
43 | uint32_t src_w, uint32_t src_h); | |
bef799fb | 44 | |
ed851963 RC |
45 | static void set_scanout_locked(struct drm_plane *plane, |
46 | struct drm_framebuffer *fb); | |
47 | ||
06c0dd96 RC |
48 | static struct mdp5_kms *get_kms(struct drm_plane *plane) |
49 | { | |
50 | struct msm_drm_private *priv = plane->dev->dev_private; | |
51 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
52 | } | |
53 | ||
ed851963 | 54 | static bool plane_enabled(struct drm_plane_state *state) |
06c0dd96 | 55 | { |
ed851963 | 56 | return state->fb && state->crtc; |
06c0dd96 RC |
57 | } |
58 | ||
06c0dd96 RC |
59 | static void mdp5_plane_destroy(struct drm_plane *plane) |
60 | { | |
61 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
62 | ||
ed851963 | 63 | drm_plane_helper_disable(plane); |
06c0dd96 RC |
64 | drm_plane_cleanup(plane); |
65 | ||
66 | kfree(mdp5_plane); | |
67 | } | |
68 | ||
8089082f | 69 | static void mdp5_plane_install_rotation_property(struct drm_device *dev, |
70 | struct drm_plane *plane) | |
71 | { | |
72 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
73 | ||
74 | if (!(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP) && | |
75 | !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) | |
76 | return; | |
77 | ||
78 | if (!dev->mode_config.rotation_property) | |
79 | dev->mode_config.rotation_property = | |
80 | drm_mode_create_rotation_property(dev, | |
31ad61e4 | 81 | DRM_REFLECT_X | DRM_REFLECT_Y); |
8089082f | 82 | |
83 | if (dev->mode_config.rotation_property) | |
84 | drm_object_attach_property(&plane->base, | |
85 | dev->mode_config.rotation_property, | |
86 | 0); | |
87 | } | |
88 | ||
06c0dd96 | 89 | /* helper to install properties which are common to planes and crtcs */ |
4ff696ea | 90 | static void mdp5_plane_install_properties(struct drm_plane *plane, |
06c0dd96 RC |
91 | struct drm_mode_object *obj) |
92 | { | |
12987781 | 93 | struct drm_device *dev = plane->dev; |
94 | struct msm_drm_private *dev_priv = dev->dev_private; | |
95 | struct drm_property *prop; | |
96 | ||
97 | #define INSTALL_PROPERTY(name, NAME, init_val, fnc, ...) do { \ | |
98 | prop = dev_priv->plane_property[PLANE_PROP_##NAME]; \ | |
99 | if (!prop) { \ | |
100 | prop = drm_property_##fnc(dev, 0, #name, \ | |
101 | ##__VA_ARGS__); \ | |
102 | if (!prop) { \ | |
103 | dev_warn(dev->dev, \ | |
104 | "Create property %s failed\n", \ | |
105 | #name); \ | |
106 | return; \ | |
107 | } \ | |
108 | dev_priv->plane_property[PLANE_PROP_##NAME] = prop; \ | |
109 | } \ | |
110 | drm_object_attach_property(&plane->base, prop, init_val); \ | |
111 | } while (0) | |
112 | ||
113 | #define INSTALL_RANGE_PROPERTY(name, NAME, min, max, init_val) \ | |
114 | INSTALL_PROPERTY(name, NAME, init_val, \ | |
115 | create_range, min, max) | |
116 | ||
117 | #define INSTALL_ENUM_PROPERTY(name, NAME, init_val) \ | |
118 | INSTALL_PROPERTY(name, NAME, init_val, \ | |
119 | create_enum, name##_prop_enum_list, \ | |
120 | ARRAY_SIZE(name##_prop_enum_list)) | |
121 | ||
122 | INSTALL_RANGE_PROPERTY(zpos, ZPOS, 1, 255, 1); | |
123 | ||
8089082f | 124 | mdp5_plane_install_rotation_property(dev, plane); |
125 | ||
12987781 | 126 | #undef INSTALL_RANGE_PROPERTY |
127 | #undef INSTALL_ENUM_PROPERTY | |
128 | #undef INSTALL_PROPERTY | |
06c0dd96 RC |
129 | } |
130 | ||
12987781 | 131 | static int mdp5_plane_atomic_set_property(struct drm_plane *plane, |
132 | struct drm_plane_state *state, struct drm_property *property, | |
133 | uint64_t val) | |
134 | { | |
135 | struct drm_device *dev = plane->dev; | |
136 | struct mdp5_plane_state *pstate; | |
137 | struct msm_drm_private *dev_priv = dev->dev_private; | |
138 | int ret = 0; | |
139 | ||
140 | pstate = to_mdp5_plane_state(state); | |
141 | ||
142 | #define SET_PROPERTY(name, NAME, type) do { \ | |
143 | if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ | |
144 | pstate->name = (type)val; \ | |
145 | DBG("Set property %s %d", #name, (type)val); \ | |
146 | goto done; \ | |
147 | } \ | |
148 | } while (0) | |
149 | ||
150 | SET_PROPERTY(zpos, ZPOS, uint8_t); | |
151 | ||
152 | dev_err(dev->dev, "Invalid property\n"); | |
153 | ret = -EINVAL; | |
154 | done: | |
155 | return ret; | |
156 | #undef SET_PROPERTY | |
157 | } | |
158 | ||
12987781 | 159 | static int mdp5_plane_atomic_get_property(struct drm_plane *plane, |
160 | const struct drm_plane_state *state, | |
161 | struct drm_property *property, uint64_t *val) | |
162 | { | |
163 | struct drm_device *dev = plane->dev; | |
164 | struct mdp5_plane_state *pstate; | |
165 | struct msm_drm_private *dev_priv = dev->dev_private; | |
166 | int ret = 0; | |
167 | ||
168 | pstate = to_mdp5_plane_state(state); | |
169 | ||
170 | #define GET_PROPERTY(name, NAME, type) do { \ | |
171 | if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ | |
172 | *val = pstate->name; \ | |
173 | DBG("Get property %s %lld", #name, *val); \ | |
174 | goto done; \ | |
175 | } \ | |
176 | } while (0) | |
177 | ||
178 | GET_PROPERTY(zpos, ZPOS, uint8_t); | |
179 | ||
180 | dev_err(dev->dev, "Invalid property\n"); | |
181 | ret = -EINVAL; | |
182 | done: | |
183 | return ret; | |
184 | #undef SET_PROPERTY | |
06c0dd96 RC |
185 | } |
186 | ||
ed851963 RC |
187 | static void mdp5_plane_reset(struct drm_plane *plane) |
188 | { | |
189 | struct mdp5_plane_state *mdp5_state; | |
190 | ||
191 | if (plane->state && plane->state->fb) | |
192 | drm_framebuffer_unreference(plane->state->fb); | |
193 | ||
194 | kfree(to_mdp5_plane_state(plane->state)); | |
195 | mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL); | |
196 | ||
12987781 | 197 | /* assign default blend parameters */ |
198 | mdp5_state->alpha = 255; | |
199 | mdp5_state->premultiplied = 0; | |
200 | ||
201 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) | |
202 | mdp5_state->zpos = STAGE_BASE; | |
203 | else | |
204 | mdp5_state->zpos = STAGE0 + drm_plane_index(plane); | |
205 | ||
07cc0ef6 | 206 | mdp5_state->base.plane = plane; |
ed851963 RC |
207 | |
208 | plane->state = &mdp5_state->base; | |
209 | } | |
210 | ||
211 | static struct drm_plane_state * | |
212 | mdp5_plane_duplicate_state(struct drm_plane *plane) | |
213 | { | |
214 | struct mdp5_plane_state *mdp5_state; | |
215 | ||
216 | if (WARN_ON(!plane->state)) | |
217 | return NULL; | |
218 | ||
219 | mdp5_state = kmemdup(to_mdp5_plane_state(plane->state), | |
220 | sizeof(*mdp5_state), GFP_KERNEL); | |
221 | ||
222 | if (mdp5_state && mdp5_state->base.fb) | |
223 | drm_framebuffer_reference(mdp5_state->base.fb); | |
224 | ||
225 | mdp5_state->mode_changed = false; | |
226 | mdp5_state->pending = false; | |
227 | ||
228 | return &mdp5_state->base; | |
229 | } | |
230 | ||
231 | static void mdp5_plane_destroy_state(struct drm_plane *plane, | |
232 | struct drm_plane_state *state) | |
233 | { | |
234 | if (state->fb) | |
235 | drm_framebuffer_unreference(state->fb); | |
236 | ||
237 | kfree(to_mdp5_plane_state(state)); | |
238 | } | |
239 | ||
06c0dd96 | 240 | static const struct drm_plane_funcs mdp5_plane_funcs = { |
ed851963 RC |
241 | .update_plane = drm_atomic_helper_update_plane, |
242 | .disable_plane = drm_atomic_helper_disable_plane, | |
06c0dd96 | 243 | .destroy = mdp5_plane_destroy, |
8089082f | 244 | .set_property = drm_atomic_helper_plane_set_property, |
12987781 | 245 | .atomic_set_property = mdp5_plane_atomic_set_property, |
246 | .atomic_get_property = mdp5_plane_atomic_get_property, | |
ed851963 RC |
247 | .reset = mdp5_plane_reset, |
248 | .atomic_duplicate_state = mdp5_plane_duplicate_state, | |
249 | .atomic_destroy_state = mdp5_plane_destroy_state, | |
06c0dd96 RC |
250 | }; |
251 | ||
ed851963 | 252 | static int mdp5_plane_prepare_fb(struct drm_plane *plane, |
1832040d | 253 | struct drm_plane_state *new_state) |
06c0dd96 | 254 | { |
ed851963 | 255 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); |
06c0dd96 | 256 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
844f9111 ML |
257 | struct drm_framebuffer *fb = new_state->fb; |
258 | ||
259 | if (!new_state->fb) | |
260 | return 0; | |
06c0dd96 | 261 | |
ed851963 RC |
262 | DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id); |
263 | return msm_framebuffer_prepare(fb, mdp5_kms->id); | |
0deed25b SV |
264 | } |
265 | ||
ed851963 | 266 | static void mdp5_plane_cleanup_fb(struct drm_plane *plane, |
1832040d | 267 | struct drm_plane_state *old_state) |
0deed25b SV |
268 | { |
269 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
270 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
844f9111 ML |
271 | struct drm_framebuffer *fb = old_state->fb; |
272 | ||
273 | if (!fb) | |
274 | return; | |
0deed25b | 275 | |
ed851963 RC |
276 | DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id); |
277 | msm_framebuffer_cleanup(fb, mdp5_kms->id); | |
278 | } | |
0deed25b | 279 | |
ed851963 RC |
280 | static int mdp5_plane_atomic_check(struct drm_plane *plane, |
281 | struct drm_plane_state *state) | |
282 | { | |
283 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
284 | struct drm_plane_state *old_state = plane->state; | |
3498409f | 285 | const struct mdp_format *format; |
8089082f | 286 | bool vflip, hflip; |
ed851963 RC |
287 | |
288 | DBG("%s: check (%d -> %d)", mdp5_plane->name, | |
289 | plane_enabled(old_state), plane_enabled(state)); | |
290 | ||
3498409f | 291 | if (plane_enabled(state)) { |
292 | format = to_mdp_format(msm_framebuffer_format(state->fb)); | |
293 | if (MDP_FORMAT_IS_YUV(format) && | |
294 | !pipe_supports_yuv(mdp5_plane->caps)) { | |
295 | dev_err(plane->dev->dev, | |
296 | "Pipe doesn't support YUV\n"); | |
297 | ||
298 | return -EINVAL; | |
299 | } | |
300 | ||
301 | if (!(mdp5_plane->caps & MDP_PIPE_CAP_SCALE) && | |
302 | (((state->src_w >> 16) != state->crtc_w) || | |
303 | ((state->src_h >> 16) != state->crtc_h))) { | |
304 | dev_err(plane->dev->dev, | |
305 | "Pipe doesn't support scaling (%dx%d -> %dx%d)\n", | |
306 | state->src_w >> 16, state->src_h >> 16, | |
307 | state->crtc_w, state->crtc_h); | |
308 | ||
309 | return -EINVAL; | |
310 | } | |
8089082f | 311 | |
31ad61e4 JL |
312 | hflip = !!(state->rotation & DRM_REFLECT_X); |
313 | vflip = !!(state->rotation & DRM_REFLECT_Y); | |
8089082f | 314 | if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) || |
315 | (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) { | |
316 | dev_err(plane->dev->dev, | |
317 | "Pipe doesn't support flip\n"); | |
318 | ||
319 | return -EINVAL; | |
320 | } | |
3498409f | 321 | } |
322 | ||
ed851963 RC |
323 | if (plane_enabled(state) && plane_enabled(old_state)) { |
324 | /* we cannot change SMP block configuration during scanout: */ | |
325 | bool full_modeset = false; | |
326 | if (state->fb->pixel_format != old_state->fb->pixel_format) { | |
327 | DBG("%s: pixel_format change!", mdp5_plane->name); | |
328 | full_modeset = true; | |
329 | } | |
330 | if (state->src_w != old_state->src_w) { | |
331 | DBG("%s: src_w change!", mdp5_plane->name); | |
332 | full_modeset = true; | |
333 | } | |
334 | if (to_mdp5_plane_state(old_state)->pending) { | |
335 | DBG("%s: still pending!", mdp5_plane->name); | |
336 | full_modeset = true; | |
337 | } | |
338 | if (full_modeset) { | |
339 | struct drm_crtc_state *crtc_state = | |
340 | drm_atomic_get_crtc_state(state->state, state->crtc); | |
341 | crtc_state->mode_changed = true; | |
342 | to_mdp5_plane_state(state)->mode_changed = true; | |
343 | } | |
344 | } else { | |
345 | to_mdp5_plane_state(state)->mode_changed = true; | |
346 | } | |
06c0dd96 | 347 | |
ed851963 RC |
348 | return 0; |
349 | } | |
350 | ||
f1c37e1a TR |
351 | static void mdp5_plane_atomic_update(struct drm_plane *plane, |
352 | struct drm_plane_state *old_state) | |
ed851963 RC |
353 | { |
354 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
355 | struct drm_plane_state *state = plane->state; | |
356 | ||
357 | DBG("%s: update", mdp5_plane->name); | |
0deed25b | 358 | |
ed851963 RC |
359 | if (!plane_enabled(state)) { |
360 | to_mdp5_plane_state(state)->pending = true; | |
ed851963 RC |
361 | } else if (to_mdp5_plane_state(state)->mode_changed) { |
362 | int ret; | |
363 | to_mdp5_plane_state(state)->pending = true; | |
364 | ret = mdp5_plane_mode_set(plane, | |
365 | state->crtc, state->fb, | |
366 | state->crtc_x, state->crtc_y, | |
367 | state->crtc_w, state->crtc_h, | |
368 | state->src_x, state->src_y, | |
369 | state->src_w, state->src_h); | |
370 | /* atomic_check should have ensured that this doesn't fail */ | |
371 | WARN_ON(ret < 0); | |
372 | } else { | |
373 | unsigned long flags; | |
374 | spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); | |
375 | set_scanout_locked(plane, state->fb); | |
376 | spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); | |
377 | } | |
0deed25b SV |
378 | } |
379 | ||
ed851963 RC |
380 | static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { |
381 | .prepare_fb = mdp5_plane_prepare_fb, | |
382 | .cleanup_fb = mdp5_plane_cleanup_fb, | |
383 | .atomic_check = mdp5_plane_atomic_check, | |
384 | .atomic_update = mdp5_plane_atomic_update, | |
385 | }; | |
386 | ||
387 | static void set_scanout_locked(struct drm_plane *plane, | |
0deed25b SV |
388 | struct drm_framebuffer *fb) |
389 | { | |
390 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
ed851963 RC |
391 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
392 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
06c0dd96 | 393 | |
ed851963 RC |
394 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), |
395 | MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | | |
396 | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); | |
0deed25b | 397 | |
ed851963 RC |
398 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), |
399 | MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | | |
400 | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); | |
401 | ||
402 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), | |
403 | msm_framebuffer_iova(fb, mdp5_kms->id, 0)); | |
404 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), | |
405 | msm_framebuffer_iova(fb, mdp5_kms->id, 1)); | |
406 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), | |
407 | msm_framebuffer_iova(fb, mdp5_kms->id, 2)); | |
408 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), | |
755c814a | 409 | msm_framebuffer_iova(fb, mdp5_kms->id, 3)); |
06c0dd96 RC |
410 | |
411 | plane->fb = fb; | |
412 | } | |
413 | ||
f8d9b515 SV |
414 | /* Note: mdp5_plane->pipe_lock must be locked */ |
415 | static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe) | |
416 | { | |
417 | uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) & | |
418 | ~MDP5_PIPE_OP_MODE_CSC_1_EN; | |
419 | ||
420 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value); | |
421 | } | |
422 | ||
423 | /* Note: mdp5_plane->pipe_lock must be locked */ | |
424 | static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, | |
425 | struct csc_cfg *csc) | |
426 | { | |
427 | uint32_t i, mode = 0; /* RGB, no CSC */ | |
428 | uint32_t *matrix; | |
429 | ||
430 | if (unlikely(!csc)) | |
431 | return; | |
432 | ||
433 | if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type)) | |
434 | mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV); | |
435 | if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type)) | |
436 | mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV); | |
437 | mode |= MDP5_PIPE_OP_MODE_CSC_1_EN; | |
438 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode); | |
439 | ||
440 | matrix = csc->matrix; | |
441 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe), | |
442 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) | | |
443 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1])); | |
444 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe), | |
445 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) | | |
446 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3])); | |
447 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe), | |
448 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) | | |
449 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5])); | |
450 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe), | |
451 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) | | |
452 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7])); | |
453 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe), | |
454 | MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8])); | |
455 | ||
456 | for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) { | |
457 | uint32_t *pre_clamp = csc->pre_clamp; | |
458 | uint32_t *post_clamp = csc->post_clamp; | |
459 | ||
460 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i), | |
461 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) | | |
462 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i])); | |
463 | ||
464 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i), | |
465 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) | | |
466 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i])); | |
467 | ||
468 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i), | |
469 | MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i])); | |
470 | ||
471 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i), | |
472 | MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i])); | |
473 | } | |
474 | } | |
475 | ||
476 | #define PHASE_STEP_SHIFT 21 | |
477 | #define DOWN_SCALE_RATIO_MAX 32 /* 2^(26-21) */ | |
478 | ||
479 | static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase) | |
480 | { | |
481 | uint32_t unit; | |
482 | ||
483 | if (src == 0 || dst == 0) | |
484 | return -EINVAL; | |
485 | ||
486 | /* | |
487 | * PHASE_STEP_X/Y is coded on 26 bits (25:0), | |
488 | * where 2^21 represents the unity "1" in fixed-point hardware design. | |
489 | * This leaves 5 bits for the integer part (downscale case): | |
490 | * -> maximum downscale ratio = 0b1_1111 = 31 | |
491 | */ | |
492 | if (src > (dst * DOWN_SCALE_RATIO_MAX)) | |
493 | return -EOVERFLOW; | |
494 | ||
495 | unit = 1 << PHASE_STEP_SHIFT; | |
496 | *out_phase = mult_frac(unit, src, dst); | |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
bef799fb SV |
501 | static int calc_scalex_steps(struct drm_plane *plane, |
502 | uint32_t pixel_format, uint32_t src, uint32_t dest, | |
95651cd9 | 503 | uint32_t phasex_steps[COMP_MAX]) |
f8d9b515 | 504 | { |
bef799fb SV |
505 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
506 | struct device *dev = mdp5_kms->dev->dev; | |
f8d9b515 SV |
507 | uint32_t phasex_step; |
508 | unsigned int hsub; | |
509 | int ret; | |
510 | ||
511 | ret = calc_phase_step(src, dest, &phasex_step); | |
bef799fb SV |
512 | if (ret) { |
513 | dev_err(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret); | |
f8d9b515 | 514 | return ret; |
bef799fb | 515 | } |
f8d9b515 SV |
516 | |
517 | hsub = drm_format_horz_chroma_subsampling(pixel_format); | |
518 | ||
95651cd9 SV |
519 | phasex_steps[COMP_0] = phasex_step; |
520 | phasex_steps[COMP_3] = phasex_step; | |
521 | phasex_steps[COMP_1_2] = phasex_step / hsub; | |
f8d9b515 SV |
522 | |
523 | return 0; | |
524 | } | |
525 | ||
bef799fb SV |
526 | static int calc_scaley_steps(struct drm_plane *plane, |
527 | uint32_t pixel_format, uint32_t src, uint32_t dest, | |
95651cd9 | 528 | uint32_t phasey_steps[COMP_MAX]) |
f8d9b515 | 529 | { |
bef799fb SV |
530 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
531 | struct device *dev = mdp5_kms->dev->dev; | |
f8d9b515 SV |
532 | uint32_t phasey_step; |
533 | unsigned int vsub; | |
534 | int ret; | |
535 | ||
536 | ret = calc_phase_step(src, dest, &phasey_step); | |
bef799fb SV |
537 | if (ret) { |
538 | dev_err(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret); | |
f8d9b515 | 539 | return ret; |
bef799fb | 540 | } |
f8d9b515 SV |
541 | |
542 | vsub = drm_format_vert_chroma_subsampling(pixel_format); | |
543 | ||
95651cd9 SV |
544 | phasey_steps[COMP_0] = phasey_step; |
545 | phasey_steps[COMP_3] = phasey_step; | |
546 | phasey_steps[COMP_1_2] = phasey_step / vsub; | |
f8d9b515 SV |
547 | |
548 | return 0; | |
549 | } | |
550 | ||
8e2930c6 SV |
551 | static uint32_t get_scale_config(const struct mdp_format *format, |
552 | uint32_t src, uint32_t dst, bool horz) | |
f8d9b515 | 553 | { |
8e2930c6 SV |
554 | bool scaling = format->is_yuv ? true : (src != dst); |
555 | uint32_t sub, pix_fmt = format->base.pixel_format; | |
556 | uint32_t ya_filter, uv_filter; | |
557 | bool yuv = format->is_yuv; | |
558 | ||
559 | if (!scaling) | |
560 | return 0; | |
561 | ||
562 | if (yuv) { | |
563 | sub = horz ? drm_format_horz_chroma_subsampling(pix_fmt) : | |
564 | drm_format_vert_chroma_subsampling(pix_fmt); | |
565 | uv_filter = ((src / sub) <= dst) ? | |
566 | SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
567 | } | |
568 | ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
569 | ||
570 | if (horz) | |
571 | return MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | | |
572 | MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) | | |
573 | MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) | | |
574 | COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter)); | |
575 | else | |
576 | return MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | | |
577 | MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) | | |
578 | MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) | | |
579 | COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter)); | |
580 | } | |
581 | ||
582 | static void calc_pixel_ext(const struct mdp_format *format, | |
583 | uint32_t src, uint32_t dst, uint32_t phase_step[2], | |
584 | int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX], | |
585 | bool horz) | |
586 | { | |
587 | bool scaling = format->is_yuv ? true : (src != dst); | |
588 | int i; | |
589 | ||
590 | /* | |
591 | * Note: | |
592 | * We assume here that: | |
593 | * 1. PCMN filter is used for downscale | |
594 | * 2. bilinear filter is used for upscale | |
595 | * 3. we are in a single pipe configuration | |
596 | */ | |
597 | ||
598 | for (i = 0; i < COMP_MAX; i++) { | |
599 | pix_ext_edge1[i] = 0; | |
600 | pix_ext_edge2[i] = scaling ? 1 : 0; | |
bef799fb | 601 | } |
8e2930c6 SV |
602 | } |
603 | ||
604 | static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, | |
605 | const struct mdp_format *format, | |
606 | uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX], | |
607 | uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX]) | |
608 | { | |
609 | uint32_t pix_fmt = format->base.pixel_format; | |
610 | uint32_t lr, tb, req; | |
611 | int i; | |
612 | ||
613 | for (i = 0; i < COMP_MAX; i++) { | |
614 | uint32_t roi_w = src_w; | |
615 | uint32_t roi_h = src_h; | |
616 | ||
617 | if (format->is_yuv && i == COMP_1_2) { | |
618 | roi_w /= drm_format_horz_chroma_subsampling(pix_fmt); | |
619 | roi_h /= drm_format_vert_chroma_subsampling(pix_fmt); | |
620 | } | |
f8d9b515 | 621 | |
8e2930c6 SV |
622 | lr = (pe_left[i] >= 0) ? |
623 | MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) : | |
624 | MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]); | |
625 | ||
626 | lr |= (pe_right[i] >= 0) ? | |
627 | MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) : | |
628 | MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]); | |
629 | ||
630 | tb = (pe_top[i] >= 0) ? | |
631 | MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) : | |
632 | MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]); | |
633 | ||
634 | tb |= (pe_bottom[i] >= 0) ? | |
635 | MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) : | |
636 | MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]); | |
637 | ||
638 | req = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w + | |
639 | pe_left[i] + pe_right[i]); | |
640 | ||
641 | req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h + | |
642 | pe_top[i] + pe_bottom[i]); | |
643 | ||
644 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr); | |
645 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb); | |
646 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req); | |
647 | ||
648 | DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i, | |
649 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT), | |
650 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT), | |
651 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF), | |
652 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF), | |
653 | FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT)); | |
654 | ||
655 | DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i, | |
656 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT), | |
657 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT), | |
658 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF), | |
659 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF), | |
660 | FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM)); | |
661 | } | |
f8d9b515 SV |
662 | } |
663 | ||
8e2930c6 | 664 | |
ed851963 | 665 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
06c0dd96 RC |
666 | struct drm_crtc *crtc, struct drm_framebuffer *fb, |
667 | int crtc_x, int crtc_y, | |
668 | unsigned int crtc_w, unsigned int crtc_h, | |
669 | uint32_t src_x, uint32_t src_y, | |
670 | uint32_t src_w, uint32_t src_h) | |
671 | { | |
672 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
8089082f | 673 | struct drm_plane_state *pstate = plane->state; |
06c0dd96 RC |
674 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
675 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
676 | const struct mdp_format *format; | |
677 | uint32_t nplanes, config = 0; | |
95651cd9 | 678 | uint32_t phasex_step[COMP_MAX] = {0,}, phasey_step[COMP_MAX] = {0,}; |
8e2930c6 SV |
679 | bool pe = mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT; |
680 | int pe_left[COMP_MAX], pe_right[COMP_MAX]; | |
681 | int pe_top[COMP_MAX], pe_bottom[COMP_MAX]; | |
06c0dd96 | 682 | uint32_t hdecm = 0, vdecm = 0; |
f8d9b515 | 683 | uint32_t pix_format; |
8089082f | 684 | bool vflip, hflip; |
0deed25b | 685 | unsigned long flags; |
bfcdfb0e | 686 | int ret; |
06c0dd96 RC |
687 | |
688 | nplanes = drm_format_num_planes(fb->pixel_format); | |
689 | ||
690 | /* bad formats should already be rejected: */ | |
691 | if (WARN_ON(nplanes > pipe2nclients(pipe))) | |
692 | return -EINVAL; | |
693 | ||
f8d9b515 SV |
694 | format = to_mdp_format(msm_framebuffer_format(fb)); |
695 | pix_format = format->base.pixel_format; | |
696 | ||
06c0dd96 RC |
697 | /* src values are in Q16 fixed point, convert to integer: */ |
698 | src_x = src_x >> 16; | |
699 | src_y = src_y >> 16; | |
700 | src_w = src_w >> 16; | |
701 | src_h = src_h >> 16; | |
702 | ||
703 | DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name, | |
704 | fb->base.id, src_x, src_y, src_w, src_h, | |
705 | crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); | |
706 | ||
bfcdfb0e | 707 | /* Request some memory from the SMP: */ |
d879eb5a SV |
708 | if (mdp5_kms->smp) { |
709 | ret = mdp5_smp_request(mdp5_kms->smp, | |
710 | mdp5_plane->pipe, format, src_w, false); | |
711 | if (ret) | |
712 | return ret; | |
713 | } | |
06c0dd96 RC |
714 | |
715 | /* | |
716 | * Currently we update the hw for allocations/requests immediately, | |
717 | * but once atomic modeset/pageflip is in place, the allocation | |
718 | * would move into atomic->check_plane_state(), while updating the | |
719 | * hw would remain here: | |
720 | */ | |
d879eb5a SV |
721 | if (mdp5_kms->smp) |
722 | mdp5_smp_configure(mdp5_kms->smp, pipe); | |
06c0dd96 | 723 | |
bef799fb SV |
724 | ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, phasex_step); |
725 | if (ret) | |
726 | return ret; | |
f8d9b515 | 727 | |
bef799fb SV |
728 | ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, phasey_step); |
729 | if (ret) | |
730 | return ret; | |
06c0dd96 | 731 | |
8e2930c6 SV |
732 | if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT) { |
733 | calc_pixel_ext(format, src_w, crtc_w, phasex_step, | |
734 | pe_left, pe_right, true); | |
735 | calc_pixel_ext(format, src_h, crtc_h, phasey_step, | |
736 | pe_top, pe_bottom, false); | |
737 | } | |
738 | ||
bef799fb SV |
739 | /* TODO calc hdecm, vdecm */ |
740 | ||
741 | /* SCALE is used to both scale and up-sample chroma components */ | |
8e2930c6 SV |
742 | config |= get_scale_config(format, src_w, crtc_w, true); |
743 | config |= get_scale_config(format, src_h, crtc_h, false); | |
bef799fb | 744 | DBG("scale config = %x", config); |
06c0dd96 | 745 | |
31ad61e4 JL |
746 | hflip = !!(pstate->rotation & DRM_REFLECT_X); |
747 | vflip = !!(pstate->rotation & DRM_REFLECT_Y); | |
8089082f | 748 | |
0deed25b SV |
749 | spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); |
750 | ||
06c0dd96 | 751 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), |
de31ea69 HL |
752 | MDP5_PIPE_SRC_IMG_SIZE_WIDTH(fb->width) | |
753 | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(fb->height)); | |
06c0dd96 RC |
754 | |
755 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), | |
756 | MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | | |
757 | MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); | |
758 | ||
759 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), | |
760 | MDP5_PIPE_SRC_XY_X(src_x) | | |
761 | MDP5_PIPE_SRC_XY_Y(src_y)); | |
762 | ||
763 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), | |
764 | MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | | |
765 | MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); | |
766 | ||
767 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), | |
768 | MDP5_PIPE_OUT_XY_X(crtc_x) | | |
769 | MDP5_PIPE_OUT_XY_Y(crtc_y)); | |
770 | ||
06c0dd96 RC |
771 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), |
772 | MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | | |
773 | MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | | |
774 | MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | | |
775 | MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | | |
776 | COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | | |
777 | MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | | |
778 | MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | | |
779 | COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | | |
2d3584eb | 780 | MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) | |
f8d9b515 | 781 | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); |
06c0dd96 RC |
782 | |
783 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), | |
784 | MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | | |
785 | MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | | |
786 | MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | | |
787 | MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); | |
788 | ||
789 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), | |
8089082f | 790 | (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) | |
791 | (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) | | |
8e2930c6 | 792 | COND(pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) | |
06c0dd96 RC |
793 | MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); |
794 | ||
795 | /* not using secure mode: */ | |
796 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); | |
797 | ||
8e2930c6 SV |
798 | if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT) |
799 | mdp5_write_pixel_ext(mdp5_kms, pipe, format, | |
800 | src_w, pe_left, pe_right, | |
801 | src_h, pe_top, pe_bottom); | |
802 | ||
3498409f | 803 | if (mdp5_plane->caps & MDP_PIPE_CAP_SCALE) { |
804 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), | |
95651cd9 | 805 | phasex_step[COMP_0]); |
3498409f | 806 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), |
95651cd9 | 807 | phasey_step[COMP_0]); |
3498409f | 808 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), |
95651cd9 | 809 | phasex_step[COMP_1_2]); |
3498409f | 810 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), |
95651cd9 | 811 | phasey_step[COMP_1_2]); |
3498409f | 812 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), |
813 | MDP5_PIPE_DECIMATION_VERT(vdecm) | | |
814 | MDP5_PIPE_DECIMATION_HORZ(hdecm)); | |
815 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config); | |
816 | } | |
817 | ||
818 | if (mdp5_plane->caps & MDP_PIPE_CAP_CSC) { | |
819 | if (MDP_FORMAT_IS_YUV(format)) | |
820 | csc_enable(mdp5_kms, pipe, | |
821 | mdp_get_default_csc_cfg(CSC_YUV2RGB)); | |
822 | else | |
823 | csc_disable(mdp5_kms, pipe); | |
824 | } | |
06c0dd96 | 825 | |
ed851963 | 826 | set_scanout_locked(plane, fb); |
0deed25b SV |
827 | |
828 | spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); | |
829 | ||
0deed25b | 830 | return ret; |
06c0dd96 RC |
831 | } |
832 | ||
833 | void mdp5_plane_complete_flip(struct drm_plane *plane) | |
834 | { | |
835 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
ed851963 RC |
836 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); |
837 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
838 | ||
839 | DBG("%s: complete flip", mdp5_plane->name); | |
06c0dd96 | 840 | |
d879eb5a SV |
841 | if (mdp5_kms->smp) |
842 | mdp5_smp_commit(mdp5_kms->smp, pipe); | |
ed851963 RC |
843 | |
844 | to_mdp5_plane_state(plane->state)->pending = false; | |
06c0dd96 RC |
845 | } |
846 | ||
847 | enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) | |
848 | { | |
849 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
850 | return mdp5_plane->pipe; | |
851 | } | |
852 | ||
0deed25b SV |
853 | uint32_t mdp5_plane_get_flush(struct drm_plane *plane) |
854 | { | |
855 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
856 | ||
857 | return mdp5_plane->flush_mask; | |
858 | } | |
859 | ||
657c63f0 WX |
860 | /* called after vsync in thread context */ |
861 | void mdp5_plane_complete_commit(struct drm_plane *plane, | |
862 | struct drm_plane_state *state) | |
863 | { | |
864 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
865 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
866 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
867 | ||
d879eb5a | 868 | if (!plane_enabled(plane->state) && mdp5_kms->smp) { |
657c63f0 WX |
869 | DBG("%s: free SMP", mdp5_plane->name); |
870 | mdp5_smp_release(mdp5_kms->smp, pipe); | |
871 | } | |
872 | } | |
873 | ||
06c0dd96 RC |
874 | /* initialize plane */ |
875 | struct drm_plane *mdp5_plane_init(struct drm_device *dev, | |
3498409f | 876 | enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset, |
877 | uint32_t caps) | |
06c0dd96 RC |
878 | { |
879 | struct drm_plane *plane = NULL; | |
880 | struct mdp5_plane *mdp5_plane; | |
881 | int ret; | |
2d82d188 | 882 | enum drm_plane_type type; |
06c0dd96 RC |
883 | |
884 | mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); | |
885 | if (!mdp5_plane) { | |
886 | ret = -ENOMEM; | |
887 | goto fail; | |
888 | } | |
889 | ||
890 | plane = &mdp5_plane->base; | |
891 | ||
892 | mdp5_plane->pipe = pipe; | |
893 | mdp5_plane->name = pipe2name(pipe); | |
3498409f | 894 | mdp5_plane->caps = caps; |
06c0dd96 | 895 | |
3498409f | 896 | mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, |
897 | ARRAY_SIZE(mdp5_plane->formats), | |
898 | !pipe_supports_yuv(mdp5_plane->caps)); | |
06c0dd96 | 899 | |
0deed25b SV |
900 | mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe); |
901 | mdp5_plane->reg_offset = reg_offset; | |
902 | spin_lock_init(&mdp5_plane->pipe_lock); | |
903 | ||
2d82d188 | 904 | type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; |
ed851963 | 905 | ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, |
2d82d188 | 906 | mdp5_plane->formats, mdp5_plane->nformats, |
b0b3b795 | 907 | type, NULL); |
ed851963 RC |
908 | if (ret) |
909 | goto fail; | |
910 | ||
911 | drm_plane_helper_add(plane, &mdp5_plane_helper_funcs); | |
06c0dd96 RC |
912 | |
913 | mdp5_plane_install_properties(plane, &plane->base); | |
914 | ||
915 | return plane; | |
916 | ||
917 | fail: | |
918 | if (plane) | |
919 | mdp5_plane_destroy(plane); | |
920 | ||
921 | return ERR_PTR(ret); | |
922 | } |