Commit | Line | Data |
---|---|---|
06c0dd96 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 "mdp5_kms.h" | |
19 | ||
20 | ||
21 | struct mdp5_plane { | |
22 | struct drm_plane base; | |
23 | const char *name; | |
24 | ||
25 | enum mdp5_pipe pipe; | |
26 | ||
27 | uint32_t nformats; | |
28 | uint32_t formats[32]; | |
29 | ||
30 | bool enabled; | |
31 | }; | |
32 | #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) | |
33 | ||
34 | static struct mdp5_kms *get_kms(struct drm_plane *plane) | |
35 | { | |
36 | struct msm_drm_private *priv = plane->dev->dev_private; | |
37 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
38 | } | |
39 | ||
40 | static int mdp5_plane_update(struct drm_plane *plane, | |
41 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
42 | int crtc_x, int crtc_y, | |
43 | unsigned int crtc_w, unsigned int crtc_h, | |
44 | uint32_t src_x, uint32_t src_y, | |
45 | uint32_t src_w, uint32_t src_h) | |
46 | { | |
47 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
48 | ||
49 | mdp5_plane->enabled = true; | |
50 | ||
51 | if (plane->fb) | |
52 | drm_framebuffer_unreference(plane->fb); | |
53 | ||
54 | drm_framebuffer_reference(fb); | |
55 | ||
56 | return mdp5_plane_mode_set(plane, crtc, fb, | |
57 | crtc_x, crtc_y, crtc_w, crtc_h, | |
58 | src_x, src_y, src_w, src_h); | |
59 | } | |
60 | ||
61 | static int mdp5_plane_disable(struct drm_plane *plane) | |
62 | { | |
63 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
64 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
65 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
66 | int i; | |
67 | ||
68 | DBG("%s: disable", mdp5_plane->name); | |
69 | ||
70 | /* update our SMP request to zero (release all our blks): */ | |
71 | for (i = 0; i < pipe2nclients(pipe); i++) | |
72 | mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), 0); | |
73 | ||
74 | /* TODO detaching now will cause us not to get the last | |
75 | * vblank and mdp5_smp_commit().. so other planes will | |
76 | * still see smp blocks previously allocated to us as | |
77 | * in-use.. | |
78 | */ | |
79 | if (plane->crtc) | |
80 | mdp5_crtc_detach(plane->crtc, plane); | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | static void mdp5_plane_destroy(struct drm_plane *plane) | |
86 | { | |
87 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
88 | ||
89 | mdp5_plane_disable(plane); | |
90 | drm_plane_cleanup(plane); | |
91 | ||
92 | kfree(mdp5_plane); | |
93 | } | |
94 | ||
95 | /* helper to install properties which are common to planes and crtcs */ | |
96 | void mdp5_plane_install_properties(struct drm_plane *plane, | |
97 | struct drm_mode_object *obj) | |
98 | { | |
99 | // XXX | |
100 | } | |
101 | ||
102 | int mdp5_plane_set_property(struct drm_plane *plane, | |
103 | struct drm_property *property, uint64_t val) | |
104 | { | |
105 | // XXX | |
106 | return -EINVAL; | |
107 | } | |
108 | ||
109 | static const struct drm_plane_funcs mdp5_plane_funcs = { | |
110 | .update_plane = mdp5_plane_update, | |
111 | .disable_plane = mdp5_plane_disable, | |
112 | .destroy = mdp5_plane_destroy, | |
113 | .set_property = mdp5_plane_set_property, | |
114 | }; | |
115 | ||
116 | void mdp5_plane_set_scanout(struct drm_plane *plane, | |
117 | struct drm_framebuffer *fb) | |
118 | { | |
119 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
120 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
121 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
122 | uint32_t nplanes = drm_format_num_planes(fb->pixel_format); | |
123 | uint32_t iova[4]; | |
124 | int i; | |
125 | ||
126 | for (i = 0; i < nplanes; i++) { | |
127 | struct drm_gem_object *bo = msm_framebuffer_bo(fb, i); | |
128 | msm_gem_get_iova(bo, mdp5_kms->id, &iova[i]); | |
129 | } | |
130 | for (; i < 4; i++) | |
131 | iova[i] = 0; | |
132 | ||
133 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), | |
134 | MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | | |
135 | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); | |
136 | ||
137 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), | |
138 | MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | | |
139 | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); | |
140 | ||
141 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), iova[0]); | |
142 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), iova[1]); | |
143 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), iova[2]); | |
144 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), iova[3]); | |
145 | ||
146 | plane->fb = fb; | |
147 | } | |
148 | ||
149 | /* NOTE: looks like if horizontal decimation is used (if we supported that) | |
150 | * then the width used to calculate SMP block requirements is the post- | |
151 | * decimated width. Ie. SMP buffering sits downstream of decimation (which | |
152 | * presumably happens during the dma from scanout buffer). | |
153 | */ | |
154 | static int request_smp_blocks(struct drm_plane *plane, uint32_t format, | |
155 | uint32_t nplanes, uint32_t width) | |
156 | { | |
157 | struct drm_device *dev = plane->dev; | |
158 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
159 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
160 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
161 | int i, hsub, nlines, nblks, ret; | |
162 | ||
163 | hsub = drm_format_horz_chroma_subsampling(format); | |
164 | ||
165 | /* different if BWC (compressed framebuffer?) enabled: */ | |
166 | nlines = 2; | |
167 | ||
168 | for (i = 0, nblks = 0; i < nplanes; i++) { | |
169 | int n, fetch_stride, cpp; | |
170 | ||
171 | cpp = drm_format_plane_cpp(format, i); | |
172 | fetch_stride = width * cpp / (i ? hsub : 1); | |
173 | ||
174 | n = DIV_ROUND_UP(fetch_stride * nlines, SMP_BLK_SIZE); | |
175 | ||
176 | /* for hw rev v1.00 */ | |
177 | if (mdp5_kms->rev == 0) | |
178 | n = roundup_pow_of_two(n); | |
179 | ||
180 | DBG("%s[%d]: request %d SMP blocks", mdp5_plane->name, i, n); | |
181 | ret = mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), n); | |
182 | if (ret) { | |
183 | dev_err(dev->dev, "Could not allocate %d SMP blocks: %d\n", | |
184 | n, ret); | |
185 | return ret; | |
186 | } | |
187 | ||
188 | nblks += n; | |
189 | } | |
190 | ||
191 | /* in success case, return total # of blocks allocated: */ | |
192 | return nblks; | |
193 | } | |
194 | ||
195 | static void set_fifo_thresholds(struct drm_plane *plane, int nblks) | |
196 | { | |
197 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
198 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
199 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
200 | uint32_t val; | |
201 | ||
202 | /* 1/4 of SMP pool that is being fetched */ | |
203 | val = (nblks * SMP_ENTRIES_PER_BLK) / 4; | |
204 | ||
205 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1); | |
206 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2); | |
207 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3); | |
208 | ||
209 | } | |
210 | ||
211 | int mdp5_plane_mode_set(struct drm_plane *plane, | |
212 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
213 | int crtc_x, int crtc_y, | |
214 | unsigned int crtc_w, unsigned int crtc_h, | |
215 | uint32_t src_x, uint32_t src_y, | |
216 | uint32_t src_w, uint32_t src_h) | |
217 | { | |
218 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
219 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
220 | enum mdp5_pipe pipe = mdp5_plane->pipe; | |
221 | const struct mdp_format *format; | |
222 | uint32_t nplanes, config = 0; | |
223 | uint32_t phasex_step = 0, phasey_step = 0; | |
224 | uint32_t hdecm = 0, vdecm = 0; | |
225 | int i, nblks; | |
226 | ||
227 | nplanes = drm_format_num_planes(fb->pixel_format); | |
228 | ||
229 | /* bad formats should already be rejected: */ | |
230 | if (WARN_ON(nplanes > pipe2nclients(pipe))) | |
231 | return -EINVAL; | |
232 | ||
233 | /* src values are in Q16 fixed point, convert to integer: */ | |
234 | src_x = src_x >> 16; | |
235 | src_y = src_y >> 16; | |
236 | src_w = src_w >> 16; | |
237 | src_h = src_h >> 16; | |
238 | ||
239 | DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name, | |
240 | fb->base.id, src_x, src_y, src_w, src_h, | |
241 | crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); | |
242 | ||
243 | /* | |
244 | * Calculate and request required # of smp blocks: | |
245 | */ | |
246 | nblks = request_smp_blocks(plane, fb->pixel_format, nplanes, src_w); | |
247 | if (nblks < 0) | |
248 | return nblks; | |
249 | ||
250 | /* | |
251 | * Currently we update the hw for allocations/requests immediately, | |
252 | * but once atomic modeset/pageflip is in place, the allocation | |
253 | * would move into atomic->check_plane_state(), while updating the | |
254 | * hw would remain here: | |
255 | */ | |
256 | for (i = 0; i < pipe2nclients(pipe); i++) | |
257 | mdp5_smp_configure(mdp5_kms, pipe2client(pipe, i)); | |
258 | ||
259 | if (src_w != crtc_w) { | |
260 | config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN; | |
261 | /* TODO calc phasex_step, hdecm */ | |
262 | } | |
263 | ||
264 | if (src_h != crtc_h) { | |
265 | config |= MDP5_PIPE_SCALE_CONFIG_SCALEY_EN; | |
266 | /* TODO calc phasey_step, vdecm */ | |
267 | } | |
268 | ||
269 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), | |
270 | MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) | | |
271 | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h)); | |
272 | ||
273 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), | |
274 | MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | | |
275 | MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); | |
276 | ||
277 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), | |
278 | MDP5_PIPE_SRC_XY_X(src_x) | | |
279 | MDP5_PIPE_SRC_XY_Y(src_y)); | |
280 | ||
281 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), | |
282 | MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | | |
283 | MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); | |
284 | ||
285 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), | |
286 | MDP5_PIPE_OUT_XY_X(crtc_x) | | |
287 | MDP5_PIPE_OUT_XY_Y(crtc_y)); | |
288 | ||
289 | mdp5_plane_set_scanout(plane, fb); | |
290 | ||
291 | format = to_mdp_format(msm_framebuffer_format(fb)); | |
292 | ||
293 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), | |
294 | MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | | |
295 | MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | | |
296 | MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | | |
297 | MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | | |
298 | COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | | |
299 | MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | | |
300 | MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | | |
301 | COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | | |
302 | MDP5_PIPE_SRC_FORMAT_NUM_PLANES(nplanes - 1) | | |
303 | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(CHROMA_RGB)); | |
304 | ||
305 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), | |
306 | MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | | |
307 | MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | | |
308 | MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | | |
309 | MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); | |
310 | ||
311 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), | |
312 | MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); | |
313 | ||
314 | /* not using secure mode: */ | |
315 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); | |
316 | ||
317 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step); | |
318 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), phasey_step); | |
319 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), | |
320 | MDP5_PIPE_DECIMATION_VERT(vdecm) | | |
321 | MDP5_PIPE_DECIMATION_HORZ(hdecm)); | |
322 | mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), | |
323 | MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(SCALE_FILTER_NEAREST) | | |
324 | MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(SCALE_FILTER_NEAREST) | | |
325 | MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(SCALE_FILTER_NEAREST) | | |
326 | MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(SCALE_FILTER_NEAREST) | | |
327 | MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) | | |
328 | MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST)); | |
329 | ||
330 | set_fifo_thresholds(plane, nblks); | |
331 | ||
332 | /* TODO detach from old crtc (if we had more than one) */ | |
333 | mdp5_crtc_attach(crtc, plane); | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
338 | void mdp5_plane_complete_flip(struct drm_plane *plane) | |
339 | { | |
340 | struct mdp5_kms *mdp5_kms = get_kms(plane); | |
341 | enum mdp5_pipe pipe = to_mdp5_plane(plane)->pipe; | |
342 | int i; | |
343 | ||
344 | for (i = 0; i < pipe2nclients(pipe); i++) | |
345 | mdp5_smp_commit(mdp5_kms, pipe2client(pipe, i)); | |
346 | } | |
347 | ||
348 | enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) | |
349 | { | |
350 | struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); | |
351 | return mdp5_plane->pipe; | |
352 | } | |
353 | ||
354 | /* initialize plane */ | |
355 | struct drm_plane *mdp5_plane_init(struct drm_device *dev, | |
356 | enum mdp5_pipe pipe, bool private_plane) | |
357 | { | |
358 | struct drm_plane *plane = NULL; | |
359 | struct mdp5_plane *mdp5_plane; | |
360 | int ret; | |
2d82d188 | 361 | enum drm_plane_type type; |
06c0dd96 RC |
362 | |
363 | mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); | |
364 | if (!mdp5_plane) { | |
365 | ret = -ENOMEM; | |
366 | goto fail; | |
367 | } | |
368 | ||
369 | plane = &mdp5_plane->base; | |
370 | ||
371 | mdp5_plane->pipe = pipe; | |
372 | mdp5_plane->name = pipe2name(pipe); | |
373 | ||
374 | mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats, | |
375 | ARRAY_SIZE(mdp5_plane->formats)); | |
376 | ||
2d82d188 MR |
377 | type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; |
378 | drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, | |
379 | mdp5_plane->formats, mdp5_plane->nformats, | |
380 | type); | |
06c0dd96 RC |
381 | |
382 | mdp5_plane_install_properties(plane, &plane->base); | |
383 | ||
384 | return plane; | |
385 | ||
386 | fail: | |
387 | if (plane) | |
388 | mdp5_plane_destroy(plane); | |
389 | ||
390 | return ERR_PTR(ret); | |
391 | } |