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