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