]>
Commit | Line | Data |
---|---|---|
c8afe684 RC |
1 | /* |
2 | * Copyright (C) 2013 Red Hat | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include "mdp4_kms.h" | |
19 | ||
b1b1c74e BG |
20 | #define DOWN_SCALE_MAX 8 |
21 | #define UP_SCALE_MAX 8 | |
c8afe684 RC |
22 | |
23 | struct mdp4_plane { | |
24 | struct drm_plane base; | |
25 | const char *name; | |
26 | ||
22ba8b6b | 27 | enum mdp4_pipe pipe; |
c8afe684 | 28 | |
3498409f | 29 | uint32_t caps; |
c8afe684 RC |
30 | uint32_t nformats; |
31 | uint32_t formats[32]; | |
32 | ||
33 | bool enabled; | |
34 | }; | |
35 | #define to_mdp4_plane(x) container_of(x, struct mdp4_plane, base) | |
36 | ||
570655b0 RC |
37 | /* MDP format helper functions */ |
38 | static inline | |
39 | enum mdp4_frame_format mdp4_get_frame_format(struct drm_framebuffer *fb) | |
40 | { | |
41 | bool is_tile = false; | |
42 | ||
bae781b2 | 43 | if (fb->modifier == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE) |
570655b0 RC |
44 | is_tile = true; |
45 | ||
46 | if (fb->pixel_format == DRM_FORMAT_NV12 && is_tile) | |
47 | return FRAME_TILE_YCBCR_420; | |
48 | ||
49 | return FRAME_LINEAR; | |
50 | } | |
51 | ||
e27c54ff RC |
52 | static void mdp4_plane_set_scanout(struct drm_plane *plane, |
53 | struct drm_framebuffer *fb); | |
54 | static int mdp4_plane_mode_set(struct drm_plane *plane, | |
c8afe684 RC |
55 | struct drm_crtc *crtc, struct drm_framebuffer *fb, |
56 | int crtc_x, int crtc_y, | |
57 | unsigned int crtc_w, unsigned int crtc_h, | |
58 | uint32_t src_x, uint32_t src_y, | |
e27c54ff | 59 | uint32_t src_w, uint32_t src_h); |
c8afe684 | 60 | |
e27c54ff | 61 | static struct mdp4_kms *get_kms(struct drm_plane *plane) |
c8afe684 | 62 | { |
e27c54ff RC |
63 | struct msm_drm_private *priv = plane->dev->dev_private; |
64 | return to_mdp4_kms(to_mdp_kms(priv->kms)); | |
c8afe684 RC |
65 | } |
66 | ||
67 | static void mdp4_plane_destroy(struct drm_plane *plane) | |
68 | { | |
69 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); | |
70 | ||
e27c54ff | 71 | drm_plane_helper_disable(plane); |
c8afe684 RC |
72 | drm_plane_cleanup(plane); |
73 | ||
74 | kfree(mdp4_plane); | |
75 | } | |
76 | ||
77 | /* helper to install properties which are common to planes and crtcs */ | |
4ff696ea | 78 | static void mdp4_plane_install_properties(struct drm_plane *plane, |
c8afe684 RC |
79 | struct drm_mode_object *obj) |
80 | { | |
81 | // XXX | |
82 | } | |
83 | ||
14edbde1 | 84 | static int mdp4_plane_set_property(struct drm_plane *plane, |
c8afe684 RC |
85 | struct drm_property *property, uint64_t val) |
86 | { | |
87 | // XXX | |
88 | return -EINVAL; | |
89 | } | |
90 | ||
91 | static const struct drm_plane_funcs mdp4_plane_funcs = { | |
e27c54ff RC |
92 | .update_plane = drm_atomic_helper_update_plane, |
93 | .disable_plane = drm_atomic_helper_disable_plane, | |
c8afe684 RC |
94 | .destroy = mdp4_plane_destroy, |
95 | .set_property = mdp4_plane_set_property, | |
e27c54ff RC |
96 | .reset = drm_atomic_helper_plane_reset, |
97 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
98 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
c8afe684 RC |
99 | }; |
100 | ||
e27c54ff | 101 | static int mdp4_plane_prepare_fb(struct drm_plane *plane, |
1832040d | 102 | struct drm_plane_state *new_state) |
e27c54ff RC |
103 | { |
104 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); | |
105 | struct mdp4_kms *mdp4_kms = get_kms(plane); | |
844f9111 ML |
106 | struct drm_framebuffer *fb = new_state->fb; |
107 | ||
108 | if (!fb) | |
109 | return 0; | |
e27c54ff RC |
110 | |
111 | DBG("%s: prepare: FB[%u]", mdp4_plane->name, fb->base.id); | |
112 | return msm_framebuffer_prepare(fb, mdp4_kms->id); | |
113 | } | |
114 | ||
115 | static void mdp4_plane_cleanup_fb(struct drm_plane *plane, | |
1832040d | 116 | struct drm_plane_state *old_state) |
e27c54ff RC |
117 | { |
118 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); | |
119 | struct mdp4_kms *mdp4_kms = get_kms(plane); | |
844f9111 ML |
120 | struct drm_framebuffer *fb = old_state->fb; |
121 | ||
122 | if (!fb) | |
123 | return; | |
e27c54ff RC |
124 | |
125 | DBG("%s: cleanup: FB[%u]", mdp4_plane->name, fb->base.id); | |
126 | msm_framebuffer_cleanup(fb, mdp4_kms->id); | |
127 | } | |
128 | ||
129 | ||
130 | static int mdp4_plane_atomic_check(struct drm_plane *plane, | |
131 | struct drm_plane_state *state) | |
132 | { | |
133 | return 0; | |
134 | } | |
135 | ||
f1c37e1a TR |
136 | static void mdp4_plane_atomic_update(struct drm_plane *plane, |
137 | struct drm_plane_state *old_state) | |
e27c54ff RC |
138 | { |
139 | struct drm_plane_state *state = plane->state; | |
140 | int ret; | |
141 | ||
142 | ret = mdp4_plane_mode_set(plane, | |
143 | state->crtc, state->fb, | |
144 | state->crtc_x, state->crtc_y, | |
145 | state->crtc_w, state->crtc_h, | |
146 | state->src_x, state->src_y, | |
147 | state->src_w, state->src_h); | |
148 | /* atomic_check should have ensured that this doesn't fail */ | |
149 | WARN_ON(ret < 0); | |
150 | } | |
151 | ||
152 | static const struct drm_plane_helper_funcs mdp4_plane_helper_funcs = { | |
153 | .prepare_fb = mdp4_plane_prepare_fb, | |
154 | .cleanup_fb = mdp4_plane_cleanup_fb, | |
155 | .atomic_check = mdp4_plane_atomic_check, | |
156 | .atomic_update = mdp4_plane_atomic_update, | |
157 | }; | |
158 | ||
159 | static void mdp4_plane_set_scanout(struct drm_plane *plane, | |
c8afe684 RC |
160 | struct drm_framebuffer *fb) |
161 | { | |
162 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); | |
163 | struct mdp4_kms *mdp4_kms = get_kms(plane); | |
22ba8b6b | 164 | enum mdp4_pipe pipe = mdp4_plane->pipe; |
c8afe684 RC |
165 | |
166 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe), | |
167 | MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | | |
168 | MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); | |
169 | ||
170 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe), | |
171 | MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | | |
172 | MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); | |
173 | ||
b1b1c74e BG |
174 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), |
175 | msm_framebuffer_iova(fb, mdp4_kms->id, 0)); | |
176 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP1_BASE(pipe), | |
177 | msm_framebuffer_iova(fb, mdp4_kms->id, 1)); | |
178 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP2_BASE(pipe), | |
179 | msm_framebuffer_iova(fb, mdp4_kms->id, 2)); | |
180 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP3_BASE(pipe), | |
181 | msm_framebuffer_iova(fb, mdp4_kms->id, 3)); | |
c8afe684 RC |
182 | |
183 | plane->fb = fb; | |
184 | } | |
185 | ||
b1b1c74e BG |
186 | static void mdp4_write_csc_config(struct mdp4_kms *mdp4_kms, |
187 | enum mdp4_pipe pipe, struct csc_cfg *csc) | |
188 | { | |
189 | int i; | |
190 | ||
191 | for (i = 0; i < ARRAY_SIZE(csc->matrix); i++) { | |
192 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_MV(pipe, i), | |
193 | csc->matrix[i]); | |
194 | } | |
195 | ||
196 | for (i = 0; i < ARRAY_SIZE(csc->post_bias) ; i++) { | |
197 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_PRE_BV(pipe, i), | |
198 | csc->pre_bias[i]); | |
199 | ||
200 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_POST_BV(pipe, i), | |
201 | csc->post_bias[i]); | |
202 | } | |
203 | ||
204 | for (i = 0; i < ARRAY_SIZE(csc->post_clamp) ; i++) { | |
205 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_PRE_LV(pipe, i), | |
206 | csc->pre_clamp[i]); | |
207 | ||
208 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_POST_LV(pipe, i), | |
209 | csc->post_clamp[i]); | |
210 | } | |
211 | } | |
212 | ||
c8afe684 RC |
213 | #define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000 |
214 | ||
e27c54ff | 215 | static int mdp4_plane_mode_set(struct drm_plane *plane, |
c8afe684 RC |
216 | struct drm_crtc *crtc, struct drm_framebuffer *fb, |
217 | int crtc_x, int crtc_y, | |
218 | unsigned int crtc_w, unsigned int crtc_h, | |
219 | uint32_t src_x, uint32_t src_y, | |
220 | uint32_t src_w, uint32_t src_h) | |
221 | { | |
b1b1c74e | 222 | struct drm_device *dev = plane->dev; |
c8afe684 RC |
223 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); |
224 | struct mdp4_kms *mdp4_kms = get_kms(plane); | |
22ba8b6b | 225 | enum mdp4_pipe pipe = mdp4_plane->pipe; |
10a02eb6 | 226 | const struct mdp_format *format; |
c8afe684 RC |
227 | uint32_t op_mode = 0; |
228 | uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT; | |
229 | uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT; | |
507d71b1 | 230 | enum mdp4_frame_format frame_type; |
c8afe684 | 231 | |
e27c54ff RC |
232 | if (!(crtc && fb)) { |
233 | DBG("%s: disabled!", mdp4_plane->name); | |
234 | return 0; | |
235 | } | |
236 | ||
507d71b1 AT |
237 | frame_type = mdp4_get_frame_format(fb); |
238 | ||
c8afe684 RC |
239 | /* src values are in Q16 fixed point, convert to integer: */ |
240 | src_x = src_x >> 16; | |
241 | src_y = src_y >> 16; | |
242 | src_w = src_w >> 16; | |
243 | src_h = src_h >> 16; | |
244 | ||
a8623918 RC |
245 | DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp4_plane->name, |
246 | fb->base.id, src_x, src_y, src_w, src_h, | |
247 | crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); | |
248 | ||
b1b1c74e BG |
249 | format = to_mdp_format(msm_framebuffer_format(fb)); |
250 | ||
251 | if (src_w > (crtc_w * DOWN_SCALE_MAX)) { | |
252 | dev_err(dev->dev, "Width down scaling exceeds limits!\n"); | |
253 | return -ERANGE; | |
254 | } | |
255 | ||
256 | if (src_h > (crtc_h * DOWN_SCALE_MAX)) { | |
257 | dev_err(dev->dev, "Height down scaling exceeds limits!\n"); | |
258 | return -ERANGE; | |
259 | } | |
260 | ||
261 | if (crtc_w > (src_w * UP_SCALE_MAX)) { | |
262 | dev_err(dev->dev, "Width up scaling exceeds limits!\n"); | |
263 | return -ERANGE; | |
264 | } | |
265 | ||
266 | if (crtc_h > (src_h * UP_SCALE_MAX)) { | |
267 | dev_err(dev->dev, "Height up scaling exceeds limits!\n"); | |
268 | return -ERANGE; | |
269 | } | |
270 | ||
c8afe684 | 271 | if (src_w != crtc_w) { |
b1b1c74e | 272 | uint32_t sel_unit = SCALE_FIR; |
c8afe684 | 273 | op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN; |
b1b1c74e BG |
274 | |
275 | if (MDP_FORMAT_IS_YUV(format)) { | |
276 | if (crtc_w > src_w) | |
277 | sel_unit = SCALE_PIXEL_RPT; | |
278 | else if (crtc_w <= (src_w / 4)) | |
279 | sel_unit = SCALE_MN_PHASE; | |
280 | ||
281 | op_mode |= MDP4_PIPE_OP_MODE_SCALEX_UNIT_SEL(sel_unit); | |
282 | phasex_step = mult_frac(MDP4_VG_PHASE_STEP_DEFAULT, | |
283 | src_w, crtc_w); | |
284 | } | |
c8afe684 RC |
285 | } |
286 | ||
287 | if (src_h != crtc_h) { | |
b1b1c74e | 288 | uint32_t sel_unit = SCALE_FIR; |
c8afe684 | 289 | op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN; |
b1b1c74e BG |
290 | |
291 | if (MDP_FORMAT_IS_YUV(format)) { | |
292 | ||
293 | if (crtc_h > src_h) | |
294 | sel_unit = SCALE_PIXEL_RPT; | |
295 | else if (crtc_h <= (src_h / 4)) | |
296 | sel_unit = SCALE_MN_PHASE; | |
297 | ||
298 | op_mode |= MDP4_PIPE_OP_MODE_SCALEY_UNIT_SEL(sel_unit); | |
299 | phasey_step = mult_frac(MDP4_VG_PHASE_STEP_DEFAULT, | |
300 | src_h, crtc_h); | |
301 | } | |
c8afe684 RC |
302 | } |
303 | ||
304 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe), | |
305 | MDP4_PIPE_SRC_SIZE_WIDTH(src_w) | | |
306 | MDP4_PIPE_SRC_SIZE_HEIGHT(src_h)); | |
307 | ||
308 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe), | |
309 | MDP4_PIPE_SRC_XY_X(src_x) | | |
310 | MDP4_PIPE_SRC_XY_Y(src_y)); | |
311 | ||
312 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe), | |
313 | MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) | | |
314 | MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h)); | |
315 | ||
316 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe), | |
7896052d RC |
317 | MDP4_PIPE_DST_XY_X(crtc_x) | |
318 | MDP4_PIPE_DST_XY_Y(crtc_y)); | |
c8afe684 RC |
319 | |
320 | mdp4_plane_set_scanout(plane, fb); | |
321 | ||
c8afe684 RC |
322 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe), |
323 | MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | | |
324 | MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | | |
325 | MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | | |
326 | MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | | |
327 | COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) | | |
328 | MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | | |
329 | MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | | |
b1b1c74e BG |
330 | MDP4_PIPE_SRC_FORMAT_FETCH_PLANES(format->fetch_type) | |
331 | MDP4_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample) | | |
570655b0 | 332 | MDP4_PIPE_SRC_FORMAT_FRAME_FORMAT(frame_type) | |
c8afe684 RC |
333 | COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT)); |
334 | ||
335 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe), | |
336 | MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | | |
337 | MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | | |
338 | MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | | |
339 | MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); | |
340 | ||
b1b1c74e BG |
341 | if (MDP_FORMAT_IS_YUV(format)) { |
342 | struct csc_cfg *csc = mdp_get_default_csc_cfg(CSC_YUV2RGB); | |
343 | ||
344 | op_mode |= MDP4_PIPE_OP_MODE_SRC_YCBCR; | |
345 | op_mode |= MDP4_PIPE_OP_MODE_CSC_EN; | |
346 | mdp4_write_csc_config(mdp4_kms, pipe, csc); | |
347 | } | |
348 | ||
c8afe684 RC |
349 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode); |
350 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step); | |
351 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step); | |
352 | ||
570655b0 RC |
353 | if (frame_type != FRAME_LINEAR) |
354 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_SSTILE_FRAME_SIZE(pipe), | |
355 | MDP4_PIPE_SSTILE_FRAME_SIZE_WIDTH(src_w) | | |
356 | MDP4_PIPE_SSTILE_FRAME_SIZE_HEIGHT(src_h)); | |
357 | ||
c8afe684 RC |
358 | return 0; |
359 | } | |
360 | ||
361 | static const char *pipe_names[] = { | |
362 | "VG1", "VG2", | |
363 | "RGB1", "RGB2", "RGB3", | |
364 | "VG3", "VG4", | |
365 | }; | |
366 | ||
22ba8b6b | 367 | enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane) |
c8afe684 RC |
368 | { |
369 | struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); | |
370 | return mdp4_plane->pipe; | |
371 | } | |
372 | ||
373 | /* initialize plane */ | |
374 | struct drm_plane *mdp4_plane_init(struct drm_device *dev, | |
22ba8b6b | 375 | enum mdp4_pipe pipe_id, bool private_plane) |
c8afe684 | 376 | { |
c8afe684 RC |
377 | struct drm_plane *plane = NULL; |
378 | struct mdp4_plane *mdp4_plane; | |
379 | int ret; | |
2d82d188 | 380 | enum drm_plane_type type; |
c8afe684 RC |
381 | |
382 | mdp4_plane = kzalloc(sizeof(*mdp4_plane), GFP_KERNEL); | |
383 | if (!mdp4_plane) { | |
384 | ret = -ENOMEM; | |
385 | goto fail; | |
386 | } | |
387 | ||
388 | plane = &mdp4_plane->base; | |
389 | ||
390 | mdp4_plane->pipe = pipe_id; | |
391 | mdp4_plane->name = pipe_names[pipe_id]; | |
3498409f | 392 | mdp4_plane->caps = mdp4_pipe_caps(pipe_id); |
c8afe684 | 393 | |
3498409f | 394 | mdp4_plane->nformats = mdp_get_formats(mdp4_plane->formats, |
395 | ARRAY_SIZE(mdp4_plane->formats), | |
396 | !pipe_supports_yuv(mdp4_plane->caps)); | |
a8623918 | 397 | |
2d82d188 | 398 | type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; |
e27c54ff | 399 | ret = drm_universal_plane_init(dev, plane, 0xff, &mdp4_plane_funcs, |
b0b3b795 VS |
400 | mdp4_plane->formats, mdp4_plane->nformats, |
401 | type, NULL); | |
e27c54ff RC |
402 | if (ret) |
403 | goto fail; | |
404 | ||
405 | drm_plane_helper_add(plane, &mdp4_plane_helper_funcs); | |
c8afe684 RC |
406 | |
407 | mdp4_plane_install_properties(plane, &plane->base); | |
408 | ||
409 | return plane; | |
410 | ||
411 | fail: | |
412 | if (plane) | |
413 | mdp4_plane_destroy(plane); | |
414 | ||
415 | return ERR_PTR(ret); | |
416 | } |