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