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