]>
Commit | Line | Data |
---|---|---|
06c0dd96 | 1 | /* |
0deed25b | 2 | * Copyright (c) 2014 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; | |
29 | ||
30 | uint32_t flush_mask; /* used to commit pipe registers */ | |
31 | ||
06c0dd96 RC |
32 | uint32_t nformats; |
33 | uint32_t formats[32]; | |
34 | ||
35 | bool enabled; | |
36 | }; | |
37 | #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) | |
38 | ||
ed851963 RC |
39 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
40 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
41 | int crtc_x, int crtc_y, | |
42 | unsigned int crtc_w, unsigned int crtc_h, | |
43 | uint32_t src_x, uint32_t src_y, | |
44 | uint32_t src_w, uint32_t src_h); | |
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 | ||
59 | static int mdp5_plane_disable(struct drm_plane *plane) | |
60 | { | |
61 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
62 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
63 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
06c0dd96 RC |
64 | |
65 | DBG("%s: disable", mdp5_plane->name); | |
66 | ||
bfcdfb0e SV |
67 | if (mdp5_kms) { |
68 | /* Release the memory we requested earlier from the SMP: */ | |
42238da8 | 69 | mdp5_smp_release(mdp5_kms->smp, pipe); |
bfcdfb0e | 70 | } |
06c0dd96 | 71 | |
06c0dd96 RC |
72 | return 0; |
73 | } | |
74 | ||
75 | static void mdp5_plane_destroy(struct drm_plane *plane) | |
76 | { | |
77 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
78 | ||
ed851963 | 79 | drm_plane_helper_disable(plane); |
06c0dd96 RC |
80 | drm_plane_cleanup(plane); |
81 | ||
82 | kfree(mdp5_plane); | |
83 | } | |
84 | ||
85 | /* helper to install properties which are common to planes and crtcs */ | |
86 | void mdp5_plane_install_properties(struct drm_plane *plane, | |
87 | struct drm_mode_object *obj) | |
88 | { | |
89 | // XXX | |
90 | } | |
91 | ||
92 | int mdp5_plane_set_property(struct drm_plane *plane, | |
93 | struct drm_property *property, uint64_t val) | |
94 | { | |
95 | // XXX | |
96 | return -EINVAL; | |
97 | } | |
98 | ||
ed851963 RC |
99 | static void mdp5_plane_reset(struct drm_plane *plane) |
100 | { | |
101 | struct mdp5_plane_state *mdp5_state; | |
102 | ||
103 | if (plane->state && plane->state->fb) | |
104 | drm_framebuffer_unreference(plane->state->fb); | |
105 | ||
106 | kfree(to_mdp5_plane_state(plane->state)); | |
107 | mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL); | |
108 | ||
109 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) { | |
110 | mdp5_state->zpos = 0; | |
111 | } else { | |
112 | mdp5_state->zpos = 1 + drm_plane_index(plane); | |
113 | } | |
07cc0ef6 | 114 | mdp5_state->base.plane = plane; |
ed851963 RC |
115 | |
116 | plane->state = &mdp5_state->base; | |
117 | } | |
118 | ||
119 | static struct drm_plane_state * | |
120 | mdp5_plane_duplicate_state(struct drm_plane *plane) | |
121 | { | |
122 | struct mdp5_plane_state *mdp5_state; | |
123 | ||
124 | if (WARN_ON(!plane->state)) | |
125 | return NULL; | |
126 | ||
127 | mdp5_state = kmemdup(to_mdp5_plane_state(plane->state), | |
128 | sizeof(*mdp5_state), GFP_KERNEL); | |
129 | ||
130 | if (mdp5_state && mdp5_state->base.fb) | |
131 | drm_framebuffer_reference(mdp5_state->base.fb); | |
132 | ||
133 | mdp5_state->mode_changed = false; | |
134 | mdp5_state->pending = false; | |
135 | ||
136 | return &mdp5_state->base; | |
137 | } | |
138 | ||
139 | static void mdp5_plane_destroy_state(struct drm_plane *plane, | |
140 | struct drm_plane_state *state) | |
141 | { | |
142 | if (state->fb) | |
143 | drm_framebuffer_unreference(state->fb); | |
144 | ||
145 | kfree(to_mdp5_plane_state(state)); | |
146 | } | |
147 | ||
06c0dd96 | 148 | static const struct drm_plane_funcs mdp5_plane_funcs = { |
ed851963 RC |
149 | .update_plane = drm_atomic_helper_update_plane, |
150 | .disable_plane = drm_atomic_helper_disable_plane, | |
06c0dd96 RC |
151 | .destroy = mdp5_plane_destroy, |
152 | .set_property = mdp5_plane_set_property, | |
ed851963 RC |
153 | .reset = mdp5_plane_reset, |
154 | .atomic_duplicate_state = mdp5_plane_duplicate_state, | |
155 | .atomic_destroy_state = mdp5_plane_destroy_state, | |
06c0dd96 RC |
156 | }; |
157 | ||
ed851963 | 158 | static int mdp5_plane_prepare_fb(struct drm_plane *plane, |
d136dfee TU |
159 | struct drm_framebuffer *fb, |
160 | const struct drm_plane_state *new_state) | |
06c0dd96 | 161 | { |
ed851963 | 162 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); |
06c0dd96 | 163 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
06c0dd96 | 164 | |
ed851963 RC |
165 | DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id); |
166 | return msm_framebuffer_prepare(fb, mdp5_kms->id); | |
0deed25b SV |
167 | } |
168 | ||
ed851963 | 169 | static void mdp5_plane_cleanup_fb(struct drm_plane *plane, |
d136dfee TU |
170 | struct drm_framebuffer *fb, |
171 | const struct drm_plane_state *old_state) | |
0deed25b SV |
172 | { |
173 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
174 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
0deed25b | 175 | |
ed851963 RC |
176 | DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id); |
177 | msm_framebuffer_cleanup(fb, mdp5_kms->id); | |
178 | } | |
0deed25b | 179 | |
ed851963 RC |
180 | static int mdp5_plane_atomic_check(struct drm_plane *plane, |
181 | struct drm_plane_state *state) | |
182 | { | |
183 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
184 | struct drm_plane_state *old_state = plane->state; | |
185 | ||
186 | DBG("%s: check (%d -> %d)", mdp5_plane->name, | |
187 | plane_enabled(old_state), plane_enabled(state)); | |
188 | ||
189 | if (plane_enabled(state) && plane_enabled(old_state)) { | |
190 | /* we cannot change SMP block configuration during scanout: */ | |
191 | bool full_modeset = false; | |
192 | if (state->fb->pixel_format != old_state->fb->pixel_format) { | |
193 | DBG("%s: pixel_format change!", mdp5_plane->name); | |
194 | full_modeset = true; | |
195 | } | |
196 | if (state->src_w != old_state->src_w) { | |
197 | DBG("%s: src_w change!", mdp5_plane->name); | |
198 | full_modeset = true; | |
199 | } | |
200 | if (to_mdp5_plane_state(old_state)->pending) { | |
201 | DBG("%s: still pending!", mdp5_plane->name); | |
202 | full_modeset = true; | |
203 | } | |
204 | if (full_modeset) { | |
205 | struct drm_crtc_state *crtc_state = | |
206 | drm_atomic_get_crtc_state(state->state, state->crtc); | |
207 | crtc_state->mode_changed = true; | |
208 | to_mdp5_plane_state(state)->mode_changed = true; | |
209 | } | |
210 | } else { | |
211 | to_mdp5_plane_state(state)->mode_changed = true; | |
212 | } | |
06c0dd96 | 213 | |
ed851963 RC |
214 | return 0; |
215 | } | |
216 | ||
f1c37e1a TR |
217 | static void mdp5_plane_atomic_update(struct drm_plane *plane, |
218 | struct drm_plane_state *old_state) | |
ed851963 RC |
219 | { |
220 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
221 | struct drm_plane_state *state = plane->state; | |
222 | ||
223 | DBG("%s: update", mdp5_plane->name); | |
0deed25b | 224 | |
ed851963 RC |
225 | if (!plane_enabled(state)) { |
226 | to_mdp5_plane_state(state)->pending = true; | |
227 | mdp5_plane_disable(plane); | |
228 | } else if (to_mdp5_plane_state(state)->mode_changed) { | |
229 | int ret; | |
230 | to_mdp5_plane_state(state)->pending = true; | |
231 | ret = mdp5_plane_mode_set(plane, | |
232 | state->crtc, state->fb, | |
233 | state->crtc_x, state->crtc_y, | |
234 | state->crtc_w, state->crtc_h, | |
235 | state->src_x, state->src_y, | |
236 | state->src_w, state->src_h); | |
237 | /* atomic_check should have ensured that this doesn't fail */ | |
238 | WARN_ON(ret < 0); | |
239 | } else { | |
240 | unsigned long flags; | |
241 | spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); | |
242 | set_scanout_locked(plane, state->fb); | |
243 | spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); | |
244 | } | |
0deed25b SV |
245 | } |
246 | ||
ed851963 RC |
247 | static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { |
248 | .prepare_fb = mdp5_plane_prepare_fb, | |
249 | .cleanup_fb = mdp5_plane_cleanup_fb, | |
250 | .atomic_check = mdp5_plane_atomic_check, | |
251 | .atomic_update = mdp5_plane_atomic_update, | |
252 | }; | |
253 | ||
254 | static void set_scanout_locked(struct drm_plane *plane, | |
0deed25b SV |
255 | struct drm_framebuffer *fb) |
256 | { | |
257 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
ed851963 RC |
258 | struct mdp5_kms *mdp5_kms = get_kms(plane); |
259 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
06c0dd96 | 260 | |
ed851963 RC |
261 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), |
262 | MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | | |
263 | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); | |
0deed25b | 264 | |
ed851963 RC |
265 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), |
266 | MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | | |
267 | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); | |
268 | ||
269 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), | |
270 | msm_framebuffer_iova(fb, mdp5_kms->id, 0)); | |
271 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), | |
272 | msm_framebuffer_iova(fb, mdp5_kms->id, 1)); | |
273 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), | |
274 | msm_framebuffer_iova(fb, mdp5_kms->id, 2)); | |
275 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), | |
755c814a | 276 | msm_framebuffer_iova(fb, mdp5_kms->id, 3)); |
06c0dd96 RC |
277 | |
278 | plane->fb = fb; | |
279 | } | |
280 | ||
f8d9b515 SV |
281 | /* Note: mdp5_plane->pipe_lock must be locked */ |
282 | static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe) | |
283 | { | |
284 | uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) & | |
285 | ~MDP5_PIPE_OP_MODE_CSC_1_EN; | |
286 | ||
287 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value); | |
288 | } | |
289 | ||
290 | /* Note: mdp5_plane->pipe_lock must be locked */ | |
291 | static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, | |
292 | struct csc_cfg *csc) | |
293 | { | |
294 | uint32_t i, mode = 0; /* RGB, no CSC */ | |
295 | uint32_t *matrix; | |
296 | ||
297 | if (unlikely(!csc)) | |
298 | return; | |
299 | ||
300 | if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type)) | |
301 | mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV); | |
302 | if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type)) | |
303 | mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV); | |
304 | mode |= MDP5_PIPE_OP_MODE_CSC_1_EN; | |
305 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode); | |
306 | ||
307 | matrix = csc->matrix; | |
308 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe), | |
309 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) | | |
310 | MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1])); | |
311 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe), | |
312 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) | | |
313 | MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3])); | |
314 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe), | |
315 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) | | |
316 | MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5])); | |
317 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe), | |
318 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) | | |
319 | MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7])); | |
320 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe), | |
321 | MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8])); | |
322 | ||
323 | for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) { | |
324 | uint32_t *pre_clamp = csc->pre_clamp; | |
325 | uint32_t *post_clamp = csc->post_clamp; | |
326 | ||
327 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i), | |
328 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) | | |
329 | MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i])); | |
330 | ||
331 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i), | |
332 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) | | |
333 | MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i])); | |
334 | ||
335 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i), | |
336 | MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i])); | |
337 | ||
338 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i), | |
339 | MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i])); | |
340 | } | |
341 | } | |
342 | ||
343 | #define PHASE_STEP_SHIFT 21 | |
344 | #define DOWN_SCALE_RATIO_MAX 32 /* 2^(26-21) */ | |
345 | ||
346 | static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase) | |
347 | { | |
348 | uint32_t unit; | |
349 | ||
350 | if (src == 0 || dst == 0) | |
351 | return -EINVAL; | |
352 | ||
353 | /* | |
354 | * PHASE_STEP_X/Y is coded on 26 bits (25:0), | |
355 | * where 2^21 represents the unity "1" in fixed-point hardware design. | |
356 | * This leaves 5 bits for the integer part (downscale case): | |
357 | * -> maximum downscale ratio = 0b1_1111 = 31 | |
358 | */ | |
359 | if (src > (dst * DOWN_SCALE_RATIO_MAX)) | |
360 | return -EOVERFLOW; | |
361 | ||
362 | unit = 1 << PHASE_STEP_SHIFT; | |
363 | *out_phase = mult_frac(unit, src, dst); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | static int calc_scalex_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, | |
369 | uint32_t phasex_steps[2]) | |
370 | { | |
371 | uint32_t phasex_step; | |
372 | unsigned int hsub; | |
373 | int ret; | |
374 | ||
375 | ret = calc_phase_step(src, dest, &phasex_step); | |
376 | if (ret) | |
377 | return ret; | |
378 | ||
379 | hsub = drm_format_horz_chroma_subsampling(pixel_format); | |
380 | ||
381 | phasex_steps[0] = phasex_step; | |
382 | phasex_steps[1] = phasex_step / hsub; | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static int calc_scaley_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, | |
388 | uint32_t phasey_steps[2]) | |
389 | { | |
390 | uint32_t phasey_step; | |
391 | unsigned int vsub; | |
392 | int ret; | |
393 | ||
394 | ret = calc_phase_step(src, dest, &phasey_step); | |
395 | if (ret) | |
396 | return ret; | |
397 | ||
398 | vsub = drm_format_vert_chroma_subsampling(pixel_format); | |
399 | ||
400 | phasey_steps[0] = phasey_step; | |
401 | phasey_steps[1] = phasey_step / vsub; | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
406 | static uint32_t get_scalex_config(uint32_t src, uint32_t dest) | |
407 | { | |
408 | uint32_t filter; | |
409 | ||
410 | filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
411 | ||
412 | return MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | | |
413 | MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(filter) | | |
414 | MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(filter) | | |
415 | MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(filter); | |
416 | } | |
417 | ||
418 | static uint32_t get_scaley_config(uint32_t src, uint32_t dest) | |
419 | { | |
420 | uint32_t filter; | |
421 | ||
422 | filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; | |
423 | ||
424 | return MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | | |
425 | MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(filter) | | |
426 | MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(filter) | | |
427 | MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(filter); | |
428 | } | |
429 | ||
ed851963 | 430 | static int mdp5_plane_mode_set(struct drm_plane *plane, |
06c0dd96 RC |
431 | struct drm_crtc *crtc, struct drm_framebuffer *fb, |
432 | int crtc_x, int crtc_y, | |
433 | unsigned int crtc_w, unsigned int crtc_h, | |
434 | uint32_t src_x, uint32_t src_y, | |
435 | uint32_t src_w, uint32_t src_h) | |
436 | { | |
437 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
438 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
f8d9b515 | 439 | struct device *dev = mdp5_kms->dev->dev; |
06c0dd96 RC |
440 | enum mdp5_pipe pipe = mdp5_plane->pipe; |
441 | const struct mdp_format *format; | |
442 | uint32_t nplanes, config = 0; | |
f8d9b515 SV |
443 | /* below array -> index 0: comp 0/3 ; index 1: comp 1/2 */ |
444 | uint32_t phasex_step[2] = {0,}, phasey_step[2] = {0,}; | |
06c0dd96 | 445 | uint32_t hdecm = 0, vdecm = 0; |
f8d9b515 | 446 | uint32_t pix_format; |
0deed25b | 447 | unsigned long flags; |
bfcdfb0e | 448 | int ret; |
06c0dd96 RC |
449 | |
450 | nplanes = drm_format_num_planes(fb->pixel_format); | |
451 | ||
452 | /* bad formats should already be rejected: */ | |
453 | if (WARN_ON(nplanes > pipe2nclients(pipe))) | |
454 | return -EINVAL; | |
455 | ||
f8d9b515 SV |
456 | format = to_mdp_format(msm_framebuffer_format(fb)); |
457 | pix_format = format->base.pixel_format; | |
458 | ||
06c0dd96 RC |
459 | /* src values are in Q16 fixed point, convert to integer: */ |
460 | src_x = src_x >> 16; | |
461 | src_y = src_y >> 16; | |
462 | src_w = src_w >> 16; | |
463 | src_h = src_h >> 16; | |
464 | ||
465 | DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name, | |
466 | fb->base.id, src_x, src_y, src_w, src_h, | |
467 | crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); | |
468 | ||
bfcdfb0e | 469 | /* Request some memory from the SMP: */ |
42238da8 | 470 | ret = mdp5_smp_request(mdp5_kms->smp, |
bfcdfb0e SV |
471 | mdp5_plane->pipe, fb->pixel_format, src_w); |
472 | if (ret) | |
473 | return ret; | |
06c0dd96 RC |
474 | |
475 | /* | |
476 | * Currently we update the hw for allocations/requests immediately, | |
477 | * but once atomic modeset/pageflip is in place, the allocation | |
478 | * would move into atomic->check_plane_state(), while updating the | |
479 | * hw would remain here: | |
480 | */ | |
42238da8 | 481 | mdp5_smp_configure(mdp5_kms->smp, pipe); |
06c0dd96 | 482 | |
f8d9b515 SV |
483 | /* SCALE is used to both scale and up-sample chroma components */ |
484 | ||
485 | if ((src_w != crtc_w) || MDP_FORMAT_IS_YUV(format)) { | |
486 | /* TODO calc hdecm */ | |
487 | ret = calc_scalex_steps(pix_format, src_w, crtc_w, phasex_step); | |
488 | if (ret) { | |
489 | dev_err(dev, "X scaling (%d -> %d) failed: %d\n", | |
490 | src_w, crtc_w, ret); | |
491 | return ret; | |
492 | } | |
493 | config |= get_scalex_config(src_w, crtc_w); | |
06c0dd96 RC |
494 | } |
495 | ||
f8d9b515 SV |
496 | if ((src_h != crtc_h) || MDP_FORMAT_IS_YUV(format)) { |
497 | /* TODO calc vdecm */ | |
498 | ret = calc_scaley_steps(pix_format, src_h, crtc_h, phasey_step); | |
499 | if (ret) { | |
500 | dev_err(dev, "Y scaling (%d -> %d) failed: %d\n", | |
501 | src_h, crtc_h, ret); | |
502 | return ret; | |
503 | } | |
504 | config |= get_scaley_config(src_h, crtc_h); | |
06c0dd96 RC |
505 | } |
506 | ||
0deed25b SV |
507 | spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); |
508 | ||
06c0dd96 | 509 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), |
de31ea69 HL |
510 | MDP5_PIPE_SRC_IMG_SIZE_WIDTH(fb->width) | |
511 | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(fb->height)); | |
06c0dd96 RC |
512 | |
513 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), | |
514 | MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | | |
515 | MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); | |
516 | ||
517 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), | |
518 | MDP5_PIPE_SRC_XY_X(src_x) | | |
519 | MDP5_PIPE_SRC_XY_Y(src_y)); | |
520 | ||
521 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), | |
522 | MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | | |
523 | MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); | |
524 | ||
525 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), | |
526 | MDP5_PIPE_OUT_XY_X(crtc_x) | | |
527 | MDP5_PIPE_OUT_XY_Y(crtc_y)); | |
528 | ||
06c0dd96 RC |
529 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), |
530 | MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | | |
531 | MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | | |
532 | MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | | |
533 | MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | | |
534 | COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | | |
535 | MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | | |
536 | MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | | |
537 | COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | | |
f8d9b515 SV |
538 | MDP5_PIPE_SRC_FORMAT_NUM_PLANES(format->fetch_type) | |
539 | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); | |
06c0dd96 RC |
540 | |
541 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), | |
542 | MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | | |
543 | MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | | |
544 | MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | | |
545 | MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); | |
546 | ||
547 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), | |
548 | MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); | |
549 | ||
550 | /* not using secure mode: */ | |
551 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); | |
552 | ||
f8d9b515 SV |
553 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), |
554 | phasex_step[0]); | |
555 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), | |
556 | phasey_step[0]); | |
557 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), | |
558 | phasex_step[1]); | |
559 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), | |
560 | phasey_step[1]); | |
06c0dd96 RC |
561 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), |
562 | MDP5_PIPE_DECIMATION_VERT(vdecm) | | |
563 | MDP5_PIPE_DECIMATION_HORZ(hdecm)); | |
f8d9b515 SV |
564 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config); |
565 | ||
566 | if (MDP_FORMAT_IS_YUV(format)) | |
567 | csc_enable(mdp5_kms, pipe, | |
568 | mdp_get_default_csc_cfg(CSC_YUV2RGB)); | |
569 | else | |
570 | csc_disable(mdp5_kms, pipe); | |
06c0dd96 | 571 | |
ed851963 | 572 | set_scanout_locked(plane, fb); |
0deed25b SV |
573 | |
574 | spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); | |
575 | ||
0deed25b | 576 | return ret; |
06c0dd96 RC |
577 | } |
578 | ||
579 | void mdp5_plane_complete_flip(struct drm_plane *plane) | |
580 | { | |
581 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
ed851963 RC |
582 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); |
583 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
584 | ||
585 | DBG("%s: complete flip", mdp5_plane->name); | |
06c0dd96 | 586 | |
42238da8 | 587 | mdp5_smp_commit(mdp5_kms->smp, pipe); |
ed851963 RC |
588 | |
589 | to_mdp5_plane_state(plane->state)->pending = false; | |
06c0dd96 RC |
590 | } |
591 | ||
592 | enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) | |
593 | { | |
594 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
595 | return mdp5_plane->pipe; | |
596 | } | |
597 | ||
0deed25b SV |
598 | uint32_t mdp5_plane_get_flush(struct drm_plane *plane) |
599 | { | |
600 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
601 | ||
602 | return mdp5_plane->flush_mask; | |
603 | } | |
604 | ||
06c0dd96 RC |
605 | /* initialize plane */ |
606 | struct drm_plane *mdp5_plane_init(struct drm_device *dev, | |
0deed25b | 607 | enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset) |
06c0dd96 RC |
608 | { |
609 | struct drm_plane *plane = NULL; | |
610 | struct mdp5_plane *mdp5_plane; | |
611 | int ret; | |
2d82d188 | 612 | enum drm_plane_type type; |
06c0dd96 RC |
613 | |
614 | mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); | |
615 | if (!mdp5_plane) { | |
616 | ret = -ENOMEM; | |
617 | goto fail; | |
618 | } | |
619 | ||
620 | plane = &mdp5_plane->base; | |
621 | ||
622 | mdp5_plane->pipe = pipe; | |
623 | mdp5_plane->name = pipe2name(pipe); | |
624 | ||
625 | mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats, | |
626 | ARRAY_SIZE(mdp5_plane->formats)); | |
627 | ||
0deed25b SV |
628 | mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe); |
629 | mdp5_plane->reg_offset = reg_offset; | |
630 | spin_lock_init(&mdp5_plane->pipe_lock); | |
631 | ||
2d82d188 | 632 | type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; |
ed851963 | 633 | ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, |
2d82d188 MR |
634 | mdp5_plane->formats, mdp5_plane->nformats, |
635 | type); | |
ed851963 RC |
636 | if (ret) |
637 | goto fail; | |
638 | ||
639 | drm_plane_helper_add(plane, &mdp5_plane_helper_funcs); | |
06c0dd96 RC |
640 | |
641 | mdp5_plane_install_properties(plane, &plane->base); | |
642 | ||
643 | return plane; | |
644 | ||
645 | fail: | |
646 | if (plane) | |
647 | mdp5_plane_destroy(plane); | |
648 | ||
649 | return ERR_PTR(ret); | |
650 | } |