• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
get_kms(struct drm_plane * plane)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 
mdp5_plane_update(struct drm_plane * plane,struct drm_crtc * crtc,struct drm_framebuffer * fb,int crtc_x,int crtc_y,unsigned int crtc_w,unsigned int crtc_h,uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h)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 
mdp5_plane_disable(struct drm_plane * plane)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 
mdp5_plane_destroy(struct drm_plane * plane)85 static void mdp5_plane_destroy(struct drm_plane *plane)
86 {
87 	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
88 	struct msm_drm_private *priv = plane->dev->dev_private;
89 
90 	if (priv->kms)
91 		mdp5_plane_disable(plane);
92 
93 	drm_plane_cleanup(plane);
94 
95 	kfree(mdp5_plane);
96 }
97 
98 /* helper to install properties which are common to planes and crtcs */
mdp5_plane_install_properties(struct drm_plane * plane,struct drm_mode_object * obj)99 void mdp5_plane_install_properties(struct drm_plane *plane,
100 		struct drm_mode_object *obj)
101 {
102 	// XXX
103 }
104 
mdp5_plane_set_property(struct drm_plane * plane,struct drm_property * property,uint64_t val)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 
mdp5_plane_set_scanout(struct drm_plane * plane,struct drm_framebuffer * fb)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  */
request_smp_blocks(struct drm_plane * plane,uint32_t format,uint32_t nplanes,uint32_t width)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 
set_fifo_thresholds(struct drm_plane * plane,int nblks)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 
mdp5_plane_mode_set(struct drm_plane * plane,struct drm_crtc * crtc,struct drm_framebuffer * fb,int crtc_x,int crtc_y,unsigned int crtc_w,unsigned int crtc_h,uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h)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 
mdp5_plane_complete_flip(struct drm_plane * plane)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 
mdp5_plane_pipe(struct drm_plane * plane)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 */
mdp5_plane_init(struct drm_device * dev,enum mdp5_pipe pipe,bool private_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;
364 	enum drm_plane_type type;
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 
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);
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 }
395