]>
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 | ||
dd701ae9 | 19 | #include <drm/drm_print.h> |
06c0dd96 RC |
20 | #include "mdp5_kms.h" |
21 | ||
06c0dd96 RC |
22 | struct mdp5_plane { |
23 | struct drm_plane base; | |
06c0dd96 | 24 | |
06c0dd96 RC |
25 | uint32_t nformats; |
26 | uint32_t formats[32]; | |
06c0dd96 RC |
27 | }; |
28 | #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) | |
29 | ||
ed851963 RC |
30 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
31 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
3b6acf14 | 32 | struct drm_rect *src, struct drm_rect *dest); |
bef799fb | 33 | |
10967a06 AT |
34 | static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, |
35 | struct drm_crtc *crtc, | |
36 | struct drm_framebuffer *fb, | |
37 | int crtc_x, int crtc_y, | |
38 | unsigned int crtc_w, unsigned int crtc_h, | |
39 | uint32_t src_x, uint32_t src_y, | |
34a2ab5e DV |
40 | uint32_t src_w, uint32_t src_h, |
41 | struct drm_modeset_acquire_ctx *ctx); | |
10967a06 | 42 | |
06c0dd96 RC |
43 | static struct mdp5_kms *get_kms(struct drm_plane *plane) |
44 | { | |
45 | struct msm_drm_private *priv = plane->dev->dev_private; | |
46 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
47 | } | |
48 | ||
ed851963 | 49 | static bool plane_enabled(struct drm_plane_state *state) |
06c0dd96 | 50 | { |
3b6acf14 | 51 | return state->visible; |
06c0dd96 RC |
52 | } |
53 | ||
06c0dd96 RC |
54 | static void mdp5_plane_destroy(struct drm_plane *plane) |
55 | { | |
56 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
57 | ||
ed851963 | 58 | drm_plane_helper_disable(plane); |
06c0dd96 RC |
59 | drm_plane_cleanup(plane); |
60 | ||
61 | kfree(mdp5_plane); | |
62 | } | |
63 | ||
8089082f | 64 | static void mdp5_plane_install_rotation_property(struct drm_device *dev, |
65 | struct drm_plane *plane) | |
66 | { | |
5b560c3a | 67 | drm_plane_create_rotation_property(plane, |
c2c446ad RF |
68 | DRM_MODE_ROTATE_0, |
69 | DRM_MODE_ROTATE_0 | | |
70 | DRM_MODE_ROTATE_180 | | |
71 | DRM_MODE_REFLECT_X | | |
72 | DRM_MODE_REFLECT_Y); | |
8089082f | 73 | } |
74 | ||
06c0dd96 | 75 | /* helper to install properties which are common to planes and crtcs */ |
4ff696ea | 76 | static void mdp5_plane_install_properties(struct drm_plane *plane, |
06c0dd96 RC |
77 | struct drm_mode_object *obj) |
78 | { | |
12987781 | 79 | struct drm_device *dev = plane->dev; |
80 | struct msm_drm_private *dev_priv = dev->dev_private; | |
81 | struct drm_property *prop; | |
82 | ||
83 | #define INSTALL_PROPERTY(name, NAME, init_val, fnc, ...) do { \ | |
84 | prop = dev_priv->plane_property[PLANE_PROP_##NAME]; \ | |
85 | if (!prop) { \ | |
86 | prop = drm_property_##fnc(dev, 0, #name, \ | |
87 | ##__VA_ARGS__); \ | |
88 | if (!prop) { \ | |
89 | dev_warn(dev->dev, \ | |
90 | "Create property %s failed\n", \ | |
91 | #name); \ | |
92 | return; \ | |
93 | } \ | |
94 | dev_priv->plane_property[PLANE_PROP_##NAME] = prop; \ | |
95 | } \ | |
96 | drm_object_attach_property(&plane->base, prop, init_val); \ | |
97 | } while (0) | |
98 | ||
99 | #define INSTALL_RANGE_PROPERTY(name, NAME, min, max, init_val) \ | |
100 | INSTALL_PROPERTY(name, NAME, init_val, \ | |
101 | create_range, min, max) | |
102 | ||
103 | #define INSTALL_ENUM_PROPERTY(name, NAME, init_val) \ | |
104 | INSTALL_PROPERTY(name, NAME, init_val, \ | |
105 | create_enum, name##_prop_enum_list, \ | |
106 | ARRAY_SIZE(name##_prop_enum_list)) | |
107 | ||
108 | INSTALL_RANGE_PROPERTY(zpos, ZPOS, 1, 255, 1); | |
109 | ||
8089082f | 110 | mdp5_plane_install_rotation_property(dev, plane); |
111 | ||
12987781 | 112 | #undef INSTALL_RANGE_PROPERTY |
113 | #undef INSTALL_ENUM_PROPERTY | |
114 | #undef INSTALL_PROPERTY | |
06c0dd96 RC |
115 | } |
116 | ||
12987781 | 117 | static int mdp5_plane_atomic_set_property(struct drm_plane *plane, |
118 | struct drm_plane_state *state, struct drm_property *property, | |
119 | uint64_t val) | |
120 | { | |
121 | struct drm_device *dev = plane->dev; | |
122 | struct mdp5_plane_state *pstate; | |
123 | struct msm_drm_private *dev_priv = dev->dev_private; | |
124 | int ret = 0; | |
125 | ||
126 | pstate = to_mdp5_plane_state(state); | |
127 | ||
128 | #define SET_PROPERTY(name, NAME, type) do { \ | |
129 | if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ | |
130 | pstate->name = (type)val; \ | |
131 | DBG("Set property %s %d", #name, (type)val); \ | |
132 | goto done; \ | |
133 | } \ | |
134 | } while (0) | |
135 | ||
136 | SET_PROPERTY(zpos, ZPOS, uint8_t); | |
137 | ||
138 | dev_err(dev->dev, "Invalid property\n"); | |
139 | ret = -EINVAL; | |
140 | done: | |
141 | return ret; | |
142 | #undef SET_PROPERTY | |
143 | } | |
144 | ||
12987781 | 145 | static int mdp5_plane_atomic_get_property(struct drm_plane *plane, |
146 | const struct drm_plane_state *state, | |
147 | struct drm_property *property, uint64_t *val) | |
148 | { | |
149 | struct drm_device *dev = plane->dev; | |
150 | struct mdp5_plane_state *pstate; | |
151 | struct msm_drm_private *dev_priv = dev->dev_private; | |
152 | int ret = 0; | |
153 | ||
154 | pstate = to_mdp5_plane_state(state); | |
155 | ||
156 | #define GET_PROPERTY(name, NAME, type) do { \ | |
157 | if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ | |
158 | *val = pstate->name; \ | |
159 | DBG("Get property %s %lld", #name, *val); \ | |
160 | goto done; \ | |
161 | } \ | |
162 | } while (0) | |
163 | ||
164 | GET_PROPERTY(zpos, ZPOS, uint8_t); | |
165 | ||
166 | dev_err(dev->dev, "Invalid property\n"); | |
167 | ret = -EINVAL; | |
168 | done: | |
169 | return ret; | |
170 | #undef SET_PROPERTY | |
06c0dd96 RC |
171 | } |
172 | ||
dd701ae9 RC |
173 | static void |
174 | mdp5_plane_atomic_print_state(struct drm_printer *p, | |
175 | const struct drm_plane_state *state) | |
176 | { | |
177 | struct mdp5_plane_state *pstate = to_mdp5_plane_state(state); | |
7a10ee9b | 178 | struct mdp5_kms *mdp5_kms = get_kms(state->plane); |
dd701ae9 | 179 | |
4a0f012d RC |
180 | drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ? |
181 | pstate->hwpipe->name : "(null)"); | |
7a10ee9b AT |
182 | if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT) |
183 | drm_printf(p, "\tright-hwpipe=%s\n", | |
184 | pstate->r_hwpipe ? pstate->r_hwpipe->name : | |
185 | "(null)"); | |
dd701ae9 RC |
186 | drm_printf(p, "\tpremultiplied=%u\n", pstate->premultiplied); |
187 | drm_printf(p, "\tzpos=%u\n", pstate->zpos); | |
188 | drm_printf(p, "\talpha=%u\n", pstate->alpha); | |
189 | drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage)); | |
dd701ae9 RC |
190 | } |
191 | ||
ed851963 RC |
192 | static void mdp5_plane_reset(struct drm_plane *plane) |
193 | { | |
194 | struct mdp5_plane_state *mdp5_state; | |
195 | ||
196 | if (plane->state && plane->state->fb) | |
197 | drm_framebuffer_unreference(plane->state->fb); | |
198 | ||
199 | kfree(to_mdp5_plane_state(plane->state)); | |
200 | mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL); | |
201 | ||
12987781 | 202 | /* assign default blend parameters */ |
203 | mdp5_state->alpha = 255; | |
204 | mdp5_state->premultiplied = 0; | |
205 | ||
206 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) | |
207 | mdp5_state->zpos = STAGE_BASE; | |
208 | else | |
209 | mdp5_state->zpos = STAGE0 + drm_plane_index(plane); | |
210 | ||
07cc0ef6 | 211 | mdp5_state->base.plane = plane; |
ed851963 RC |
212 | |
213 | plane->state = &mdp5_state->base; | |
214 | } | |
215 | ||
216 | static struct drm_plane_state * | |
217 | mdp5_plane_duplicate_state(struct drm_plane *plane) | |
218 | { | |
219 | struct mdp5_plane_state *mdp5_state; | |
220 | ||
221 | if (WARN_ON(!plane->state)) | |
222 | return NULL; | |
223 | ||
224 | mdp5_state = kmemdup(to_mdp5_plane_state(plane->state), | |
225 | sizeof(*mdp5_state), GFP_KERNEL); | |
786813c3 RC |
226 | if (!mdp5_state) |
227 | return NULL; | |
ed851963 | 228 | |
786813c3 | 229 | __drm_atomic_helper_plane_duplicate_state(plane, &mdp5_state->base); |
ed851963 | 230 | |
ed851963 RC |
231 | return &mdp5_state->base; |
232 | } | |
233 | ||
234 | static void mdp5_plane_destroy_state(struct drm_plane *plane, | |
235 | struct drm_plane_state *state) | |
236 | { | |
4a0f012d RC |
237 | struct mdp5_plane_state *pstate = to_mdp5_plane_state(state); |
238 | ||
ed851963 RC |
239 | if (state->fb) |
240 | drm_framebuffer_unreference(state->fb); | |
241 | ||
4a0f012d | 242 | kfree(pstate); |
ed851963 RC |
243 | } |
244 | ||
06c0dd96 | 245 | static const struct drm_plane_funcs mdp5_plane_funcs = { |
ed851963 RC |
246 | .update_plane = drm_atomic_helper_update_plane, |
247 | .disable_plane = drm_atomic_helper_disable_plane, | |
06c0dd96 | 248 | .destroy = mdp5_plane_destroy, |
8089082f | 249 | .set_property = drm_atomic_helper_plane_set_property, |
12987781 | 250 | .atomic_set_property = mdp5_plane_atomic_set_property, |
251 | .atomic_get_property = mdp5_plane_atomic_get_property, | |
ed851963 RC |
252 | .reset = mdp5_plane_reset, |
253 | .atomic_duplicate_state = mdp5_plane_duplicate_state, | |
254 | .atomic_destroy_state = mdp5_plane_destroy_state, | |
dd701ae9 | 255 | .atomic_print_state = mdp5_plane_atomic_print_state, |
06c0dd96 RC |
256 | }; |
257 | ||
10967a06 AT |
258 | static const struct drm_plane_funcs mdp5_cursor_plane_funcs = { |
259 | .update_plane = mdp5_update_cursor_plane_legacy, | |
260 | .disable_plane = drm_atomic_helper_disable_plane, | |
261 | .destroy = mdp5_plane_destroy, | |
262 | .set_property = drm_atomic_helper_plane_set_property, | |
263 | .atomic_set_property = mdp5_plane_atomic_set_property, | |
264 | .atomic_get_property = mdp5_plane_atomic_get_property, | |
265 | .reset = mdp5_plane_reset, | |
266 | .atomic_duplicate_state = mdp5_plane_duplicate_state, | |
267 | .atomic_destroy_state = mdp5_plane_destroy_state, | |
268 | .atomic_print_state = mdp5_plane_atomic_print_state, | |
269 | }; | |
270 | ||
ed851963 | 271 | static int mdp5_plane_prepare_fb(struct drm_plane *plane, |
1832040d | 272 | struct drm_plane_state *new_state) |
06c0dd96 | 273 | { |
06c0dd96 | 274 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
f59f62d5 | 275 | struct msm_kms *kms = &mdp5_kms->base.base; |
844f9111 ML |
276 | struct drm_framebuffer *fb = new_state->fb; |
277 | ||
278 | if (!new_state->fb) | |
279 | return 0; | |
06c0dd96 | 280 | |
0002d30f | 281 | DBG("%s: prepare: FB[%u]", plane->name, fb->base.id); |
8bdcd949 | 282 | return msm_framebuffer_prepare(fb, kms->aspace); |
0deed25b SV |
283 | } |
284 | ||
ed851963 | 285 | static void mdp5_plane_cleanup_fb(struct drm_plane *plane, |
1832040d | 286 | struct drm_plane_state *old_state) |
0deed25b | 287 | { |
0deed25b | 288 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
f59f62d5 | 289 | struct msm_kms *kms = &mdp5_kms->base.base; |
844f9111 ML |
290 | struct drm_framebuffer *fb = old_state->fb; |
291 | ||
292 | if (!fb) | |
293 | return; | |
0deed25b | 294 | |
0002d30f | 295 | DBG("%s: cleanup: FB[%u]", plane->name, fb->base.id); |
8bdcd949 | 296 | msm_framebuffer_cleanup(fb, kms->aspace); |
ed851963 | 297 | } |
0deed25b | 298 | |
3b6acf14 | 299 | #define FRAC_16_16(mult, div) (((mult) << 16) / (div)) |
9142364e AT |
300 | static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, |
301 | struct drm_plane_state *state) | |
ed851963 | 302 | { |
4a0f012d | 303 | struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state); |
9142364e | 304 | struct drm_plane *plane = state->plane; |
ed851963 | 305 | struct drm_plane_state *old_state = plane->state; |
9708ebbe | 306 | struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg); |
4a0f012d | 307 | bool new_hwpipe = false; |
7a10ee9b | 308 | bool need_right_hwpipe = false; |
9708ebbe | 309 | uint32_t max_width, max_height; |
7a10ee9b | 310 | bool out_of_bounds = false; |
4a0f012d | 311 | uint32_t caps = 0; |
3b6acf14 AT |
312 | struct drm_rect clip; |
313 | int min_scale, max_scale; | |
314 | int ret; | |
ed851963 | 315 | |
0002d30f | 316 | DBG("%s: check (%d -> %d)", plane->name, |
ed851963 RC |
317 | plane_enabled(old_state), plane_enabled(state)); |
318 | ||
9708ebbe RC |
319 | max_width = config->hw->lm.max_width << 16; |
320 | max_height = config->hw->lm.max_height << 16; | |
321 | ||
322 | /* Make sure source dimensions are within bounds. */ | |
7a10ee9b AT |
323 | if (state->src_h > max_height) |
324 | out_of_bounds = true; | |
325 | ||
326 | if (state->src_w > max_width) { | |
327 | /* If source split is supported, we can go up to 2x | |
328 | * the max LM width, but we'd need to stage another | |
329 | * hwpipe to the right LM. So, the drm_plane would | |
330 | * consist of 2 hwpipes. | |
331 | */ | |
332 | if (config->hw->mdp.caps & MDP_CAP_SRC_SPLIT && | |
333 | (state->src_w <= 2 * max_width)) | |
334 | need_right_hwpipe = true; | |
335 | else | |
336 | out_of_bounds = true; | |
337 | } | |
338 | ||
339 | if (out_of_bounds) { | |
9708ebbe RC |
340 | struct drm_rect src = drm_plane_state_src(state); |
341 | DBG("Invalid source size "DRM_RECT_FP_FMT, | |
342 | DRM_RECT_FP_ARG(&src)); | |
343 | return -ERANGE; | |
344 | } | |
345 | ||
3b6acf14 AT |
346 | clip.x1 = 0; |
347 | clip.y1 = 0; | |
348 | clip.x2 = crtc_state->adjusted_mode.hdisplay; | |
349 | clip.y2 = crtc_state->adjusted_mode.vdisplay; | |
350 | min_scale = FRAC_16_16(1, 8); | |
351 | max_scale = FRAC_16_16(8, 1); | |
352 | ||
353 | ret = drm_plane_helper_check_state(state, &clip, min_scale, | |
354 | max_scale, true, true); | |
355 | if (ret) | |
356 | return ret; | |
357 | ||
3498409f | 358 | if (plane_enabled(state)) { |
574a37b1 | 359 | unsigned int rotation; |
4a0f012d | 360 | const struct mdp_format *format; |
49ec5b2e RC |
361 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
362 | uint32_t blkcfg = 0; | |
574a37b1 | 363 | |
3498409f | 364 | format = to_mdp_format(msm_framebuffer_format(state->fb)); |
4a0f012d RC |
365 | if (MDP_FORMAT_IS_YUV(format)) |
366 | caps |= MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC; | |
3498409f | 367 | |
4a0f012d RC |
368 | if (((state->src_w >> 16) != state->crtc_w) || |
369 | ((state->src_h >> 16) != state->crtc_h)) | |
370 | caps |= MDP_PIPE_CAP_SCALE; | |
8089082f | 371 | |
574a37b1 | 372 | rotation = drm_rotation_simplify(state->rotation, |
c2c446ad RF |
373 | DRM_MODE_ROTATE_0 | |
374 | DRM_MODE_REFLECT_X | | |
375 | DRM_MODE_REFLECT_Y); | |
c056b55d | 376 | |
c2c446ad | 377 | if (rotation & DRM_MODE_REFLECT_X) |
4a0f012d RC |
378 | caps |= MDP_PIPE_CAP_HFLIP; |
379 | ||
c2c446ad | 380 | if (rotation & DRM_MODE_REFLECT_Y) |
4a0f012d RC |
381 | caps |= MDP_PIPE_CAP_VFLIP; |
382 | ||
5798c8e0 AT |
383 | if (plane->type == DRM_PLANE_TYPE_CURSOR) |
384 | caps |= MDP_PIPE_CAP_CURSOR; | |
385 | ||
4a0f012d RC |
386 | /* (re)allocate hw pipe if we don't have one or caps-mismatch: */ |
387 | if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps)) | |
388 | new_hwpipe = true; | |
389 | ||
7a10ee9b AT |
390 | /* |
391 | * (re)allocte hw pipe if we're either requesting for 2 hw pipes | |
392 | * or we're switching from 2 hw pipes to 1 hw pipe because the | |
393 | * new src_w can be supported by 1 hw pipe itself. | |
394 | */ | |
395 | if ((need_right_hwpipe && !mdp5_state->r_hwpipe) || | |
396 | (!need_right_hwpipe && mdp5_state->r_hwpipe)) | |
397 | new_hwpipe = true; | |
398 | ||
49ec5b2e RC |
399 | if (mdp5_kms->smp) { |
400 | const struct mdp_format *format = | |
401 | to_mdp_format(msm_framebuffer_format(state->fb)); | |
402 | ||
403 | blkcfg = mdp5_smp_calculate(mdp5_kms->smp, format, | |
404 | state->src_w >> 16, false); | |
405 | ||
406 | if (mdp5_state->hwpipe && (mdp5_state->hwpipe->blkcfg != blkcfg)) | |
407 | new_hwpipe = true; | |
8089082f | 408 | } |
3498409f | 409 | |
4a0f012d RC |
410 | /* (re)assign hwpipe if needed, otherwise keep old one: */ |
411 | if (new_hwpipe) { | |
412 | /* TODO maybe we want to re-assign hwpipe sometimes | |
413 | * in cases when we no-longer need some caps to make | |
414 | * it available for other planes? | |
415 | */ | |
416 | struct mdp5_hw_pipe *old_hwpipe = mdp5_state->hwpipe; | |
7a10ee9b AT |
417 | struct mdp5_hw_pipe *old_right_hwpipe = |
418 | mdp5_state->r_hwpipe; | |
419 | ||
49ec5b2e RC |
420 | mdp5_state->hwpipe = mdp5_pipe_assign(state->state, |
421 | plane, caps, blkcfg); | |
4a0f012d RC |
422 | if (IS_ERR(mdp5_state->hwpipe)) { |
423 | DBG("%s: failed to assign hwpipe!", plane->name); | |
424 | return PTR_ERR(mdp5_state->hwpipe); | |
425 | } | |
7a10ee9b AT |
426 | |
427 | if (need_right_hwpipe) { | |
428 | mdp5_state->r_hwpipe = | |
429 | mdp5_pipe_assign(state->state, plane, | |
430 | caps, blkcfg); | |
431 | if (IS_ERR(mdp5_state->r_hwpipe)) { | |
432 | DBG("%s: failed to assign right hwpipe", | |
433 | plane->name); | |
434 | return PTR_ERR(mdp5_state->r_hwpipe); | |
435 | } | |
436 | } else { | |
437 | /* | |
438 | * set it to NULL so that the driver knows we | |
439 | * don't have a right hwpipe when committing a | |
440 | * new state | |
441 | */ | |
442 | mdp5_state->r_hwpipe = NULL; | |
443 | } | |
444 | ||
4a0f012d | 445 | mdp5_pipe_release(state->state, old_hwpipe); |
7a10ee9b | 446 | mdp5_pipe_release(state->state, old_right_hwpipe); |
ed851963 | 447 | } |
adcbae31 RC |
448 | } else { |
449 | mdp5_pipe_release(state->state, mdp5_state->hwpipe); | |
450 | mdp5_pipe_release(state->state, mdp5_state->r_hwpipe); | |
451 | mdp5_state->hwpipe = mdp5_state->r_hwpipe = NULL; | |
ed851963 | 452 | } |
06c0dd96 | 453 | |
ed851963 RC |
454 | return 0; |
455 | } | |
456 | ||
9142364e AT |
457 | static int mdp5_plane_atomic_check(struct drm_plane *plane, |
458 | struct drm_plane_state *state) | |
459 | { | |
460 | struct drm_crtc *crtc; | |
461 | struct drm_crtc_state *crtc_state; | |
462 | ||
463 | crtc = state->crtc ? state->crtc : plane->state->crtc; | |
464 | if (!crtc) | |
465 | return 0; | |
466 | ||
467 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); | |
468 | if (WARN_ON(!crtc_state)) | |
469 | return -EINVAL; | |
470 | ||
471 | return mdp5_plane_atomic_check_with_state(crtc_state, state); | |
472 | } | |
473 | ||
f1c37e1a TR |
474 | static void mdp5_plane_atomic_update(struct drm_plane *plane, |
475 | struct drm_plane_state *old_state) | |
ed851963 | 476 | { |
ed851963 RC |
477 | struct drm_plane_state *state = plane->state; |
478 | ||
0002d30f | 479 | DBG("%s: update", plane->name); |
0deed25b | 480 | |
f5903bad | 481 | if (plane_enabled(state)) { |
ed851963 | 482 | int ret; |
f5903bad | 483 | |
ed851963 RC |
484 | ret = mdp5_plane_mode_set(plane, |
485 | state->crtc, state->fb, | |
3b6acf14 | 486 | &state->src, &state->dst); |
ed851963 RC |
487 | /* atomic_check should have ensured that this doesn't fail */ |
488 | WARN_ON(ret < 0); | |
ed851963 | 489 | } |
0deed25b SV |
490 | } |
491 | ||
ed851963 RC |
492 | static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { |
493 | .prepare_fb = mdp5_plane_prepare_fb, | |
494 | .cleanup_fb = mdp5_plane_cleanup_fb, | |
495 | .atomic_check = mdp5_plane_atomic_check, | |
496 | .atomic_update = mdp5_plane_atomic_update, | |
497 | }; | |
498 | ||
821be43f AT |
499 | static void set_scanout_locked(struct mdp5_kms *mdp5_kms, |
500 | enum mdp5_pipe pipe, | |
501 | struct drm_framebuffer *fb) | |
0deed25b | 502 | { |
f59f62d5 RC |
503 | struct msm_kms *kms = &mdp5_kms->base.base; |
504 | ||
ed851963 RC |
505 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), |
506 | MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | | |
507 | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); | |
0deed25b | 508 | |
ed851963 RC |
509 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), |
510 | MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | | |
511 | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); | |
512 | ||
513 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), | |
8bdcd949 | 514 | msm_framebuffer_iova(fb, kms->aspace, 0)); |
ed851963 | 515 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), |
8bdcd949 | 516 | msm_framebuffer_iova(fb, kms->aspace, 1)); |
ed851963 | 517 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), |
8bdcd949 | 518 | msm_framebuffer_iova(fb, kms->aspace, 2)); |
ed851963 | 519 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), |
8bdcd949 | 520 | msm_framebuffer_iova(fb, kms->aspace, 3)); |
06c0dd96 RC |
521 | } |
522 | ||
f8d9b515 SV |
523 | /* Note: mdp5_plane->pipe_lock must be locked */ |
524 | static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe) | |
525 | { | |
526 | uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) & | |
527 | ~MDP5_PIPE_OP_MODE_CSC_1_EN; | |
528 | ||
529 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value); | |
530 | } | |
531 | ||
532 | /* Note: mdp5_plane->pipe_lock must be locked */ | |
533 | static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, | |
534 | struct csc_cfg *csc) | |
535 | { | |
536 | uint32_t i, mode = 0; /* RGB, no CSC */ | |
537 | uint32_t *matrix; | |
538 | ||
539 | if (unlikely(!csc)) | |
540 | return; | |
541 | ||
542 | if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type)) | |
543 | mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV); | |
544 | if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type)) | |
545 | mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV); | |
546 | mode |= MDP5_PIPE_OP_MODE_CSC_1_EN; | |
547 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode); | |
548 | ||
549 | matrix = csc->matrix; | |
550 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe), | |
551 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) | | |
552 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1])); | |
553 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe), | |
554 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) | | |
555 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3])); | |
556 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe), | |
557 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) | | |
558 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5])); | |
559 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe), | |
560 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) | | |
561 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7])); | |
562 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe), | |
563 | MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8])); | |
564 | ||
565 | for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) { | |
566 | uint32_t *pre_clamp = csc->pre_clamp; | |
567 | uint32_t *post_clamp = csc->post_clamp; | |
568 | ||
569 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i), | |
570 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) | | |
571 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i])); | |
572 | ||
573 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i), | |
574 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) | | |
575 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i])); | |
576 | ||
577 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i), | |
578 | MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i])); | |
579 | ||
580 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i), | |
581 | MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i])); | |
582 | } | |
583 | } | |
584 | ||
585 | #define PHASE_STEP_SHIFT 21 | |
586 | #define DOWN_SCALE_RATIO_MAX 32 /* 2^(26-21) */ | |
587 | ||
588 | static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase) | |
589 | { | |
590 | uint32_t unit; | |
591 | ||
592 | if (src == 0 || dst == 0) | |
593 | return -EINVAL; | |
594 | ||
595 | /* | |
596 | * PHASE_STEP_X/Y is coded on 26 bits (25:0), | |
597 | * where 2^21 represents the unity "1" in fixed-point hardware design. | |
598 | * This leaves 5 bits for the integer part (downscale case): | |
599 | * -> maximum downscale ratio = 0b1_1111 = 31 | |
600 | */ | |
601 | if (src > (dst * DOWN_SCALE_RATIO_MAX)) | |
602 | return -EOVERFLOW; | |
603 | ||
604 | unit = 1 << PHASE_STEP_SHIFT; | |
605 | *out_phase = mult_frac(unit, src, dst); | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
bef799fb SV |
610 | static int calc_scalex_steps(struct drm_plane *plane, |
611 | uint32_t pixel_format, uint32_t src, uint32_t dest, | |
95651cd9 | 612 | uint32_t phasex_steps[COMP_MAX]) |
f8d9b515 | 613 | { |
bef799fb SV |
614 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
615 | struct device *dev = mdp5_kms->dev->dev; | |
f8d9b515 SV |
616 | uint32_t phasex_step; |
617 | unsigned int hsub; | |
618 | int ret; | |
619 | ||
620 | ret = calc_phase_step(src, dest, &phasex_step); | |
bef799fb SV |
621 | if (ret) { |
622 | dev_err(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret); | |
f8d9b515 | 623 | return ret; |
bef799fb | 624 | } |
f8d9b515 SV |
625 | |
626 | hsub = drm_format_horz_chroma_subsampling(pixel_format); | |
627 | ||
95651cd9 SV |
628 | phasex_steps[COMP_0] = phasex_step; |
629 | phasex_steps[COMP_3] = phasex_step; | |
630 | phasex_steps[COMP_1_2] = phasex_step / hsub; | |
f8d9b515 SV |
631 | |
632 | return 0; | |
633 | } | |
634 | ||
bef799fb SV |
635 | static int calc_scaley_steps(struct drm_plane *plane, |
636 | uint32_t pixel_format, uint32_t src, uint32_t dest, | |
95651cd9 | 637 | uint32_t phasey_steps[COMP_MAX]) |
f8d9b515 | 638 | { |
bef799fb SV |
639 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
640 | struct device *dev = mdp5_kms->dev->dev; | |
f8d9b515 SV |
641 | uint32_t phasey_step; |
642 | unsigned int vsub; | |
643 | int ret; | |
644 | ||
645 | ret = calc_phase_step(src, dest, &phasey_step); | |
bef799fb SV |
646 | if (ret) { |
647 | dev_err(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret); | |
f8d9b515 | 648 | return ret; |
bef799fb | 649 | } |
f8d9b515 SV |
650 | |
651 | vsub = drm_format_vert_chroma_subsampling(pixel_format); | |
652 | ||
95651cd9 SV |
653 | phasey_steps[COMP_0] = phasey_step; |
654 | phasey_steps[COMP_3] = phasey_step; | |
655 | phasey_steps[COMP_1_2] = phasey_step / vsub; | |
f8d9b515 SV |
656 | |
657 | return 0; | |
658 | } | |
659 | ||
8e2930c6 SV |
660 | static uint32_t get_scale_config(const struct mdp_format *format, |
661 | uint32_t src, uint32_t dst, bool horz) | |
f8d9b515 | 662 | { |
8e2930c6 SV |
663 | bool scaling = format->is_yuv ? true : (src != dst); |
664 | uint32_t sub, pix_fmt = format->base.pixel_format; | |
665 | uint32_t ya_filter, uv_filter; | |
666 | bool yuv = format->is_yuv; | |
667 | ||
668 | if (!scaling) | |
669 | return 0; | |
670 | ||
671 | if (yuv) { | |
672 | sub = horz ? drm_format_horz_chroma_subsampling(pix_fmt) : | |
673 | drm_format_vert_chroma_subsampling(pix_fmt); | |
674 | uv_filter = ((src / sub) <= dst) ? | |
675 | SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
676 | } | |
677 | ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
678 | ||
679 | if (horz) | |
680 | return MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | | |
681 | MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) | | |
682 | MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) | | |
683 | COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter)); | |
684 | else | |
685 | return MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | | |
686 | MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) | | |
687 | MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) | | |
688 | COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter)); | |
689 | } | |
690 | ||
691 | static void calc_pixel_ext(const struct mdp_format *format, | |
692 | uint32_t src, uint32_t dst, uint32_t phase_step[2], | |
693 | int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX], | |
694 | bool horz) | |
695 | { | |
696 | bool scaling = format->is_yuv ? true : (src != dst); | |
697 | int i; | |
698 | ||
699 | /* | |
700 | * Note: | |
701 | * We assume here that: | |
702 | * 1. PCMN filter is used for downscale | |
703 | * 2. bilinear filter is used for upscale | |
704 | * 3. we are in a single pipe configuration | |
705 | */ | |
706 | ||
707 | for (i = 0; i < COMP_MAX; i++) { | |
708 | pix_ext_edge1[i] = 0; | |
709 | pix_ext_edge2[i] = scaling ? 1 : 0; | |
bef799fb | 710 | } |
8e2930c6 SV |
711 | } |
712 | ||
713 | static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, | |
714 | const struct mdp_format *format, | |
715 | uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX], | |
716 | uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX]) | |
717 | { | |
718 | uint32_t pix_fmt = format->base.pixel_format; | |
719 | uint32_t lr, tb, req; | |
720 | int i; | |
721 | ||
722 | for (i = 0; i < COMP_MAX; i++) { | |
723 | uint32_t roi_w = src_w; | |
724 | uint32_t roi_h = src_h; | |
725 | ||
726 | if (format->is_yuv && i == COMP_1_2) { | |
727 | roi_w /= drm_format_horz_chroma_subsampling(pix_fmt); | |
728 | roi_h /= drm_format_vert_chroma_subsampling(pix_fmt); | |
729 | } | |
f8d9b515 | 730 | |
8e2930c6 SV |
731 | lr = (pe_left[i] >= 0) ? |
732 | MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) : | |
733 | MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]); | |
734 | ||
735 | lr |= (pe_right[i] >= 0) ? | |
736 | MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) : | |
737 | MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]); | |
738 | ||
739 | tb = (pe_top[i] >= 0) ? | |
740 | MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) : | |
741 | MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]); | |
742 | ||
743 | tb |= (pe_bottom[i] >= 0) ? | |
744 | MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) : | |
745 | MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]); | |
746 | ||
747 | req = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w + | |
748 | pe_left[i] + pe_right[i]); | |
749 | ||
750 | req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h + | |
751 | pe_top[i] + pe_bottom[i]); | |
752 | ||
753 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr); | |
754 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb); | |
755 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req); | |
756 | ||
757 | DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i, | |
758 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT), | |
759 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT), | |
760 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF), | |
761 | FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF), | |
762 | FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT)); | |
763 | ||
764 | DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i, | |
765 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT), | |
766 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT), | |
767 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF), | |
768 | FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF), | |
769 | FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM)); | |
770 | } | |
f8d9b515 SV |
771 | } |
772 | ||
821be43f AT |
773 | struct pixel_ext { |
774 | int left[COMP_MAX]; | |
775 | int right[COMP_MAX]; | |
776 | int top[COMP_MAX]; | |
777 | int bottom[COMP_MAX]; | |
778 | }; | |
779 | ||
780 | struct phase_step { | |
781 | u32 x[COMP_MAX]; | |
782 | u32 y[COMP_MAX]; | |
783 | }; | |
784 | ||
785 | static void mdp5_hwpipe_mode_set(struct mdp5_kms *mdp5_kms, | |
786 | struct mdp5_hw_pipe *hwpipe, | |
787 | struct drm_framebuffer *fb, | |
788 | struct phase_step *step, | |
789 | struct pixel_ext *pe, | |
790 | u32 scale_config, u32 hdecm, u32 vdecm, | |
791 | bool hflip, bool vflip, | |
792 | int crtc_x, int crtc_y, | |
793 | unsigned int crtc_w, unsigned int crtc_h, | |
794 | u32 src_img_w, u32 src_img_h, | |
795 | u32 src_x, u32 src_y, | |
796 | u32 src_w, u32 src_h) | |
797 | { | |
798 | enum mdp5_pipe pipe = hwpipe->pipe; | |
799 | bool has_pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT; | |
800 | const struct mdp_format *format = | |
801 | to_mdp_format(msm_framebuffer_format(fb)); | |
802 | ||
803 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), | |
804 | MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_img_w) | | |
805 | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_img_h)); | |
806 | ||
807 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), | |
808 | MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | | |
809 | MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); | |
810 | ||
811 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), | |
812 | MDP5_PIPE_SRC_XY_X(src_x) | | |
813 | MDP5_PIPE_SRC_XY_Y(src_y)); | |
814 | ||
815 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), | |
816 | MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | | |
817 | MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); | |
818 | ||
819 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), | |
820 | MDP5_PIPE_OUT_XY_X(crtc_x) | | |
821 | MDP5_PIPE_OUT_XY_Y(crtc_y)); | |
822 | ||
823 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), | |
824 | MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | | |
825 | MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | | |
826 | MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | | |
827 | MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | | |
828 | COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | | |
829 | MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | | |
830 | MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | | |
831 | COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | | |
832 | MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) | | |
833 | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); | |
834 | ||
835 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), | |
836 | MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | | |
837 | MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | | |
838 | MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | | |
839 | MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); | |
840 | ||
841 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), | |
842 | (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) | | |
843 | (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) | | |
844 | COND(has_pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) | | |
845 | MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); | |
846 | ||
847 | /* not using secure mode: */ | |
848 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); | |
849 | ||
850 | if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) | |
851 | mdp5_write_pixel_ext(mdp5_kms, pipe, format, | |
852 | src_w, pe->left, pe->right, | |
853 | src_h, pe->top, pe->bottom); | |
854 | ||
855 | if (hwpipe->caps & MDP_PIPE_CAP_SCALE) { | |
856 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), | |
857 | step->x[COMP_0]); | |
858 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), | |
859 | step->y[COMP_0]); | |
860 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), | |
861 | step->x[COMP_1_2]); | |
862 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), | |
863 | step->y[COMP_1_2]); | |
864 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), | |
865 | MDP5_PIPE_DECIMATION_VERT(vdecm) | | |
866 | MDP5_PIPE_DECIMATION_HORZ(hdecm)); | |
867 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), | |
868 | scale_config); | |
869 | } | |
870 | ||
871 | if (hwpipe->caps & MDP_PIPE_CAP_CSC) { | |
872 | if (MDP_FORMAT_IS_YUV(format)) | |
873 | csc_enable(mdp5_kms, pipe, | |
874 | mdp_get_default_csc_cfg(CSC_YUV2RGB)); | |
875 | else | |
876 | csc_disable(mdp5_kms, pipe); | |
877 | } | |
878 | ||
879 | set_scanout_locked(mdp5_kms, pipe, fb); | |
880 | } | |
8e2930c6 | 881 | |
ed851963 | 882 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
06c0dd96 | 883 | struct drm_crtc *crtc, struct drm_framebuffer *fb, |
3b6acf14 | 884 | struct drm_rect *src, struct drm_rect *dest) |
06c0dd96 | 885 | { |
8089082f | 886 | struct drm_plane_state *pstate = plane->state; |
4a0f012d | 887 | struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe; |
06c0dd96 | 888 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
c056b55d | 889 | enum mdp5_pipe pipe = hwpipe->pipe; |
c26b4f6c | 890 | struct mdp5_hw_pipe *right_hwpipe; |
06c0dd96 RC |
891 | const struct mdp_format *format; |
892 | uint32_t nplanes, config = 0; | |
821be43f AT |
893 | struct phase_step step = { 0 }; |
894 | struct pixel_ext pe = { 0 }; | |
06c0dd96 | 895 | uint32_t hdecm = 0, vdecm = 0; |
f8d9b515 | 896 | uint32_t pix_format; |
574a37b1 | 897 | unsigned int rotation; |
8089082f | 898 | bool vflip, hflip; |
3b6acf14 AT |
899 | int crtc_x, crtc_y; |
900 | unsigned int crtc_w, crtc_h; | |
901 | uint32_t src_x, src_y; | |
902 | uint32_t src_w, src_h; | |
821be43f | 903 | uint32_t src_img_w, src_img_h; |
c26b4f6c AT |
904 | uint32_t src_x_r; |
905 | int crtc_x_r; | |
bfcdfb0e | 906 | int ret; |
06c0dd96 | 907 | |
bcb0b461 | 908 | nplanes = fb->format->num_planes; |
06c0dd96 RC |
909 | |
910 | /* bad formats should already be rejected: */ | |
911 | if (WARN_ON(nplanes > pipe2nclients(pipe))) | |
912 | return -EINVAL; | |
913 | ||
f8d9b515 SV |
914 | format = to_mdp_format(msm_framebuffer_format(fb)); |
915 | pix_format = format->base.pixel_format; | |
916 | ||
3b6acf14 AT |
917 | src_x = src->x1; |
918 | src_y = src->y1; | |
919 | src_w = drm_rect_width(src); | |
920 | src_h = drm_rect_height(src); | |
921 | ||
922 | crtc_x = dest->x1; | |
923 | crtc_y = dest->y1; | |
924 | crtc_w = drm_rect_width(dest); | |
925 | crtc_h = drm_rect_height(dest); | |
926 | ||
06c0dd96 RC |
927 | /* src values are in Q16 fixed point, convert to integer: */ |
928 | src_x = src_x >> 16; | |
929 | src_y = src_y >> 16; | |
930 | src_w = src_w >> 16; | |
931 | src_h = src_h >> 16; | |
932 | ||
821be43f AT |
933 | src_img_w = min(fb->width, src_w); |
934 | src_img_h = min(fb->height, src_h); | |
935 | ||
0002d30f | 936 | DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", plane->name, |
06c0dd96 RC |
937 | fb->base.id, src_x, src_y, src_w, src_h, |
938 | crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); | |
939 | ||
c26b4f6c AT |
940 | right_hwpipe = to_mdp5_plane_state(pstate)->r_hwpipe; |
941 | if (right_hwpipe) { | |
942 | /* | |
943 | * if the plane comprises of 2 hw pipes, assume that the width | |
944 | * is split equally across them. The only parameters that varies | |
945 | * between the 2 pipes are src_x and crtc_x | |
946 | */ | |
947 | crtc_w /= 2; | |
948 | src_w /= 2; | |
949 | src_img_w /= 2; | |
950 | ||
951 | crtc_x_r = crtc_x + crtc_w; | |
952 | src_x_r = src_x + src_w; | |
953 | } | |
954 | ||
821be43f | 955 | ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x); |
bef799fb SV |
956 | if (ret) |
957 | return ret; | |
f8d9b515 | 958 | |
821be43f | 959 | ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, step.y); |
bef799fb SV |
960 | if (ret) |
961 | return ret; | |
06c0dd96 | 962 | |
c056b55d | 963 | if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) { |
821be43f AT |
964 | calc_pixel_ext(format, src_w, crtc_w, step.x, |
965 | pe.left, pe.right, true); | |
966 | calc_pixel_ext(format, src_h, crtc_h, step.y, | |
967 | pe.top, pe.bottom, false); | |
8e2930c6 SV |
968 | } |
969 | ||
bef799fb SV |
970 | /* TODO calc hdecm, vdecm */ |
971 | ||
972 | /* SCALE is used to both scale and up-sample chroma components */ | |
8e2930c6 SV |
973 | config |= get_scale_config(format, src_w, crtc_w, true); |
974 | config |= get_scale_config(format, src_h, crtc_h, false); | |
bef799fb | 975 | DBG("scale config = %x", config); |
06c0dd96 | 976 | |
574a37b1 | 977 | rotation = drm_rotation_simplify(pstate->rotation, |
c2c446ad RF |
978 | DRM_MODE_ROTATE_0 | |
979 | DRM_MODE_REFLECT_X | | |
980 | DRM_MODE_REFLECT_Y); | |
981 | hflip = !!(rotation & DRM_MODE_REFLECT_X); | |
982 | vflip = !!(rotation & DRM_MODE_REFLECT_Y); | |
8089082f | 983 | |
821be43f AT |
984 | mdp5_hwpipe_mode_set(mdp5_kms, hwpipe, fb, &step, &pe, |
985 | config, hdecm, vdecm, hflip, vflip, | |
986 | crtc_x, crtc_y, crtc_w, crtc_h, | |
987 | src_img_w, src_img_h, | |
988 | src_x, src_y, src_w, src_h); | |
c26b4f6c AT |
989 | if (right_hwpipe) |
990 | mdp5_hwpipe_mode_set(mdp5_kms, right_hwpipe, fb, &step, &pe, | |
991 | config, hdecm, vdecm, hflip, vflip, | |
992 | crtc_x_r, crtc_y, crtc_w, crtc_h, | |
993 | src_img_w, src_img_h, | |
994 | src_x_r, src_y, src_w, src_h); | |
0deed25b | 995 | |
821be43f AT |
996 | plane->fb = fb; |
997 | ||
0deed25b | 998 | return ret; |
06c0dd96 RC |
999 | } |
1000 | ||
10967a06 AT |
1001 | static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, |
1002 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
1003 | int crtc_x, int crtc_y, | |
1004 | unsigned int crtc_w, unsigned int crtc_h, | |
1005 | uint32_t src_x, uint32_t src_y, | |
34a2ab5e DV |
1006 | uint32_t src_w, uint32_t src_h, |
1007 | struct drm_modeset_acquire_ctx *ctx) | |
10967a06 AT |
1008 | { |
1009 | struct drm_plane_state *plane_state, *new_plane_state; | |
1010 | struct mdp5_plane_state *mdp5_pstate; | |
1011 | struct drm_crtc_state *crtc_state = crtc->state; | |
1012 | int ret; | |
1013 | ||
1014 | if (!crtc_state->active || drm_atomic_crtc_needs_modeset(crtc_state)) | |
1015 | goto slow; | |
1016 | ||
1017 | plane_state = plane->state; | |
1018 | mdp5_pstate = to_mdp5_plane_state(plane_state); | |
1019 | ||
1020 | /* don't use fast path if we don't have a hwpipe allocated yet */ | |
1021 | if (!mdp5_pstate->hwpipe) | |
1022 | goto slow; | |
1023 | ||
1024 | /* only allow changing of position(crtc x/y or src x/y) in fast path */ | |
1025 | if (plane_state->crtc != crtc || | |
1026 | plane_state->src_w != src_w || | |
1027 | plane_state->src_h != src_h || | |
1028 | plane_state->crtc_w != crtc_w || | |
1029 | plane_state->crtc_h != crtc_h || | |
1030 | !plane_state->fb || | |
1031 | plane_state->fb != fb) | |
1032 | goto slow; | |
1033 | ||
1034 | new_plane_state = mdp5_plane_duplicate_state(plane); | |
1035 | if (!new_plane_state) | |
1036 | return -ENOMEM; | |
1037 | ||
1038 | new_plane_state->src_x = src_x; | |
1039 | new_plane_state->src_y = src_y; | |
1040 | new_plane_state->src_w = src_w; | |
1041 | new_plane_state->src_h = src_h; | |
1042 | new_plane_state->crtc_x = crtc_x; | |
1043 | new_plane_state->crtc_y = crtc_y; | |
1044 | new_plane_state->crtc_w = crtc_w; | |
1045 | new_plane_state->crtc_h = crtc_h; | |
1046 | ||
1047 | ret = mdp5_plane_atomic_check_with_state(crtc_state, new_plane_state); | |
1048 | if (ret) | |
1049 | goto slow_free; | |
1050 | ||
1051 | if (new_plane_state->visible) { | |
1052 | struct mdp5_ctl *ctl; | |
f316b25a | 1053 | struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(crtc); |
10967a06 AT |
1054 | |
1055 | ret = mdp5_plane_mode_set(plane, crtc, fb, | |
1056 | &new_plane_state->src, | |
1057 | &new_plane_state->dst); | |
1058 | WARN_ON(ret < 0); | |
1059 | ||
1060 | ctl = mdp5_crtc_get_ctl(crtc); | |
1061 | ||
f316b25a | 1062 | mdp5_ctl_commit(ctl, pipeline, mdp5_plane_get_flush(plane)); |
10967a06 AT |
1063 | } |
1064 | ||
1065 | *to_mdp5_plane_state(plane_state) = | |
1066 | *to_mdp5_plane_state(new_plane_state); | |
1067 | ||
1068 | mdp5_plane_destroy_state(plane, new_plane_state); | |
1069 | ||
1070 | return 0; | |
1071 | slow_free: | |
1072 | mdp5_plane_destroy_state(plane, new_plane_state); | |
1073 | slow: | |
1074 | return drm_atomic_helper_update_plane(plane, crtc, fb, | |
1075 | crtc_x, crtc_y, crtc_w, crtc_h, | |
34a2ab5e | 1076 | src_x, src_y, src_w, src_h, ctx); |
10967a06 AT |
1077 | } |
1078 | ||
c26b4f6c AT |
1079 | /* |
1080 | * Use this func and the one below only after the atomic state has been | |
1081 | * successfully swapped | |
1082 | */ | |
06c0dd96 RC |
1083 | enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) |
1084 | { | |
4a0f012d RC |
1085 | struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); |
1086 | ||
1087 | if (WARN_ON(!pstate->hwpipe)) | |
106f9727 | 1088 | return SSPP_NONE; |
4a0f012d RC |
1089 | |
1090 | return pstate->hwpipe->pipe; | |
06c0dd96 RC |
1091 | } |
1092 | ||
c26b4f6c AT |
1093 | enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane) |
1094 | { | |
1095 | struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); | |
1096 | ||
1097 | if (!pstate->r_hwpipe) | |
1098 | return SSPP_NONE; | |
1099 | ||
1100 | return pstate->r_hwpipe->pipe; | |
1101 | } | |
1102 | ||
0deed25b SV |
1103 | uint32_t mdp5_plane_get_flush(struct drm_plane *plane) |
1104 | { | |
4a0f012d | 1105 | struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); |
c26b4f6c | 1106 | u32 mask; |
4a0f012d RC |
1107 | |
1108 | if (WARN_ON(!pstate->hwpipe)) | |
1109 | return 0; | |
0deed25b | 1110 | |
c26b4f6c AT |
1111 | mask = pstate->hwpipe->flush_mask; |
1112 | ||
1113 | if (pstate->r_hwpipe) | |
1114 | mask |= pstate->r_hwpipe->flush_mask; | |
1115 | ||
1116 | return mask; | |
0deed25b SV |
1117 | } |
1118 | ||
06c0dd96 | 1119 | /* initialize plane */ |
5798c8e0 AT |
1120 | struct drm_plane *mdp5_plane_init(struct drm_device *dev, |
1121 | enum drm_plane_type type) | |
06c0dd96 RC |
1122 | { |
1123 | struct drm_plane *plane = NULL; | |
1124 | struct mdp5_plane *mdp5_plane; | |
1125 | int ret; | |
1126 | ||
1127 | mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); | |
1128 | if (!mdp5_plane) { | |
1129 | ret = -ENOMEM; | |
1130 | goto fail; | |
1131 | } | |
1132 | ||
1133 | plane = &mdp5_plane->base; | |
1134 | ||
3498409f | 1135 | mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, |
4a0f012d | 1136 | ARRAY_SIZE(mdp5_plane->formats), false); |
0deed25b | 1137 | |
10967a06 AT |
1138 | if (type == DRM_PLANE_TYPE_CURSOR) |
1139 | ret = drm_universal_plane_init(dev, plane, 0xff, | |
1140 | &mdp5_cursor_plane_funcs, | |
1141 | mdp5_plane->formats, mdp5_plane->nformats, | |
1142 | type, NULL); | |
1143 | else | |
1144 | ret = drm_universal_plane_init(dev, plane, 0xff, | |
1145 | &mdp5_plane_funcs, | |
1146 | mdp5_plane->formats, mdp5_plane->nformats, | |
1147 | type, NULL); | |
ed851963 RC |
1148 | if (ret) |
1149 | goto fail; | |
1150 | ||
1151 | drm_plane_helper_add(plane, &mdp5_plane_helper_funcs); | |
06c0dd96 RC |
1152 | |
1153 | mdp5_plane_install_properties(plane, &plane->base); | |
1154 | ||
1155 | return plane; | |
1156 | ||
1157 | fail: | |
1158 | if (plane) | |
1159 | mdp5_plane_destroy(plane); | |
1160 | ||
1161 | return ERR_PTR(ret); | |
1162 | } |