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