• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
3  *
4  * Copyright (C) 2015 Renesas Electronics Corporation
5  *
6  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13 
14 #include <drm/drmP.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_crtc.h>
17 #include <drm/drm_crtc_helper.h>
18 #include <drm/drm_fb_cma_helper.h>
19 #include <drm/drm_gem_cma_helper.h>
20 #include <drm/drm_gem_framebuffer_helper.h>
21 #include <drm/drm_plane_helper.h>
22 
23 #include <linux/bitops.h>
24 #include <linux/dma-mapping.h>
25 #include <linux/of_platform.h>
26 #include <linux/scatterlist.h>
27 #include <linux/videodev2.h>
28 
29 #include <media/vsp1.h>
30 
31 #include "rcar_du_drv.h"
32 #include "rcar_du_kms.h"
33 #include "rcar_du_vsp.h"
34 
rcar_du_vsp_complete(void * private,bool completed,u32 crc)35 static void rcar_du_vsp_complete(void *private, bool completed, u32 crc)
36 {
37 	struct rcar_du_crtc *crtc = private;
38 
39 	if (crtc->vblank_enable)
40 		drm_crtc_handle_vblank(&crtc->crtc);
41 
42 	if (completed)
43 		rcar_du_crtc_finish_page_flip(crtc);
44 
45 	drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
46 }
47 
rcar_du_vsp_enable(struct rcar_du_crtc * crtc)48 void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
49 {
50 	const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
51 	struct rcar_du_device *rcdu = crtc->group->dev;
52 	struct vsp1_du_lif_config cfg = {
53 		.width = mode->hdisplay,
54 		.height = mode->vdisplay,
55 		.callback = rcar_du_vsp_complete,
56 		.callback_data = crtc,
57 	};
58 	struct rcar_du_plane_state state = {
59 		.state = {
60 			.alpha = DRM_BLEND_ALPHA_OPAQUE,
61 			.crtc = &crtc->crtc,
62 			.dst.x1 = 0,
63 			.dst.y1 = 0,
64 			.dst.x2 = mode->hdisplay,
65 			.dst.y2 = mode->vdisplay,
66 			.src.x1 = 0,
67 			.src.y1 = 0,
68 			.src.x2 = mode->hdisplay << 16,
69 			.src.y2 = mode->vdisplay << 16,
70 			.zpos = 0,
71 		},
72 		.format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
73 		.source = RCAR_DU_PLANE_VSPD1,
74 		.colorkey = 0,
75 	};
76 
77 	if (rcdu->info->gen >= 3)
78 		state.hwindex = (crtc->index % 2) ? 2 : 0;
79 	else
80 		state.hwindex = crtc->index % 2;
81 
82 	__rcar_du_plane_setup(crtc->group, &state);
83 
84 	/*
85 	 * Ensure that the plane source configuration takes effect by requesting
86 	 * a restart of the group. See rcar_du_plane_atomic_update() for a more
87 	 * detailed explanation.
88 	 *
89 	 * TODO: Check whether this is still needed on Gen3.
90 	 */
91 	crtc->group->need_restart = true;
92 
93 	vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
94 }
95 
rcar_du_vsp_disable(struct rcar_du_crtc * crtc)96 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
97 {
98 	vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
99 }
100 
rcar_du_vsp_atomic_begin(struct rcar_du_crtc * crtc)101 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
102 {
103 	vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
104 }
105 
rcar_du_vsp_atomic_flush(struct rcar_du_crtc * crtc)106 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
107 {
108 	struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
109 	struct rcar_du_crtc_state *state;
110 
111 	state = to_rcar_crtc_state(crtc->crtc.state);
112 	cfg.crc = state->crc;
113 
114 	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
115 }
116 
117 /* Keep the two tables in sync. */
118 static const u32 formats_kms[] = {
119 	DRM_FORMAT_RGB332,
120 	DRM_FORMAT_ARGB4444,
121 	DRM_FORMAT_XRGB4444,
122 	DRM_FORMAT_ARGB1555,
123 	DRM_FORMAT_XRGB1555,
124 	DRM_FORMAT_RGB565,
125 	DRM_FORMAT_BGR888,
126 	DRM_FORMAT_RGB888,
127 	DRM_FORMAT_BGRA8888,
128 	DRM_FORMAT_BGRX8888,
129 	DRM_FORMAT_ARGB8888,
130 	DRM_FORMAT_XRGB8888,
131 	DRM_FORMAT_UYVY,
132 	DRM_FORMAT_VYUY,
133 	DRM_FORMAT_YUYV,
134 	DRM_FORMAT_YVYU,
135 	DRM_FORMAT_NV12,
136 	DRM_FORMAT_NV21,
137 	DRM_FORMAT_NV16,
138 	DRM_FORMAT_NV61,
139 	DRM_FORMAT_YUV420,
140 	DRM_FORMAT_YVU420,
141 	DRM_FORMAT_YUV422,
142 	DRM_FORMAT_YVU422,
143 	DRM_FORMAT_YUV444,
144 	DRM_FORMAT_YVU444,
145 };
146 
147 static const u32 formats_v4l2[] = {
148 	V4L2_PIX_FMT_RGB332,
149 	V4L2_PIX_FMT_ARGB444,
150 	V4L2_PIX_FMT_XRGB444,
151 	V4L2_PIX_FMT_ARGB555,
152 	V4L2_PIX_FMT_XRGB555,
153 	V4L2_PIX_FMT_RGB565,
154 	V4L2_PIX_FMT_RGB24,
155 	V4L2_PIX_FMT_BGR24,
156 	V4L2_PIX_FMT_ARGB32,
157 	V4L2_PIX_FMT_XRGB32,
158 	V4L2_PIX_FMT_ABGR32,
159 	V4L2_PIX_FMT_XBGR32,
160 	V4L2_PIX_FMT_UYVY,
161 	V4L2_PIX_FMT_VYUY,
162 	V4L2_PIX_FMT_YUYV,
163 	V4L2_PIX_FMT_YVYU,
164 	V4L2_PIX_FMT_NV12M,
165 	V4L2_PIX_FMT_NV21M,
166 	V4L2_PIX_FMT_NV16M,
167 	V4L2_PIX_FMT_NV61M,
168 	V4L2_PIX_FMT_YUV420M,
169 	V4L2_PIX_FMT_YVU420M,
170 	V4L2_PIX_FMT_YUV422M,
171 	V4L2_PIX_FMT_YVU422M,
172 	V4L2_PIX_FMT_YUV444M,
173 	V4L2_PIX_FMT_YVU444M,
174 };
175 
rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane * plane)176 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
177 {
178 	struct rcar_du_vsp_plane_state *state =
179 		to_rcar_vsp_plane_state(plane->plane.state);
180 	struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
181 	struct drm_framebuffer *fb = plane->plane.state->fb;
182 	struct vsp1_du_atomic_config cfg = {
183 		.pixelformat = 0,
184 		.pitch = fb->pitches[0],
185 		.alpha = state->state.alpha >> 8,
186 		.zpos = state->state.zpos,
187 	};
188 	unsigned int i;
189 
190 	cfg.src.left = state->state.src.x1 >> 16;
191 	cfg.src.top = state->state.src.y1 >> 16;
192 	cfg.src.width = drm_rect_width(&state->state.src) >> 16;
193 	cfg.src.height = drm_rect_height(&state->state.src) >> 16;
194 
195 	cfg.dst.left = state->state.dst.x1;
196 	cfg.dst.top = state->state.dst.y1;
197 	cfg.dst.width = drm_rect_width(&state->state.dst);
198 	cfg.dst.height = drm_rect_height(&state->state.dst);
199 
200 	for (i = 0; i < state->format->planes; ++i)
201 		cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
202 			   + fb->offsets[i];
203 
204 	for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
205 		if (formats_kms[i] == state->format->fourcc) {
206 			cfg.pixelformat = formats_v4l2[i];
207 			break;
208 		}
209 	}
210 
211 	vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
212 			      plane->index, &cfg);
213 }
214 
rcar_du_vsp_plane_prepare_fb(struct drm_plane * plane,struct drm_plane_state * state)215 static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
216 					struct drm_plane_state *state)
217 {
218 	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
219 	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
220 	struct rcar_du_device *rcdu = vsp->dev;
221 	unsigned int i;
222 	int ret;
223 
224 	/*
225 	 * There's no need to prepare (and unprepare) the framebuffer when the
226 	 * plane is not visible, as it will not be displayed.
227 	 */
228 	if (!state->visible)
229 		return 0;
230 
231 	for (i = 0; i < rstate->format->planes; ++i) {
232 		struct drm_gem_cma_object *gem =
233 			drm_fb_cma_get_gem_obj(state->fb, i);
234 		struct sg_table *sgt = &rstate->sg_tables[i];
235 
236 		ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
237 				      gem->base.size);
238 		if (ret)
239 			goto fail;
240 
241 		ret = vsp1_du_map_sg(vsp->vsp, sgt);
242 		if (!ret) {
243 			sg_free_table(sgt);
244 			ret = -ENOMEM;
245 			goto fail;
246 		}
247 	}
248 
249 	ret = drm_gem_fb_prepare_fb(plane, state);
250 	if (ret)
251 		goto fail;
252 
253 	return 0;
254 
255 fail:
256 	while (i--) {
257 		struct sg_table *sgt = &rstate->sg_tables[i];
258 
259 		vsp1_du_unmap_sg(vsp->vsp, sgt);
260 		sg_free_table(sgt);
261 	}
262 
263 	return ret;
264 }
265 
rcar_du_vsp_plane_cleanup_fb(struct drm_plane * plane,struct drm_plane_state * state)266 static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
267 					 struct drm_plane_state *state)
268 {
269 	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
270 	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
271 	unsigned int i;
272 
273 	if (!state->visible)
274 		return;
275 
276 	for (i = 0; i < rstate->format->planes; ++i) {
277 		struct sg_table *sgt = &rstate->sg_tables[i];
278 
279 		vsp1_du_unmap_sg(vsp->vsp, sgt);
280 		sg_free_table(sgt);
281 	}
282 }
283 
rcar_du_vsp_plane_atomic_check(struct drm_plane * plane,struct drm_plane_state * state)284 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
285 					  struct drm_plane_state *state)
286 {
287 	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
288 
289 	return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
290 }
291 
rcar_du_vsp_plane_atomic_update(struct drm_plane * plane,struct drm_plane_state * old_state)292 static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
293 					struct drm_plane_state *old_state)
294 {
295 	struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
296 	struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
297 
298 	if (plane->state->visible)
299 		rcar_du_vsp_plane_setup(rplane);
300 	else
301 		vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
302 				      rplane->index, NULL);
303 }
304 
305 static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
306 	.prepare_fb = rcar_du_vsp_plane_prepare_fb,
307 	.cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
308 	.atomic_check = rcar_du_vsp_plane_atomic_check,
309 	.atomic_update = rcar_du_vsp_plane_atomic_update,
310 };
311 
312 static struct drm_plane_state *
rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane * plane)313 rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
314 {
315 	struct rcar_du_vsp_plane_state *copy;
316 
317 	if (WARN_ON(!plane->state))
318 		return NULL;
319 
320 	copy = kzalloc(sizeof(*copy), GFP_KERNEL);
321 	if (copy == NULL)
322 		return NULL;
323 
324 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
325 
326 	return &copy->state;
327 }
328 
rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane * plane,struct drm_plane_state * state)329 static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
330 						   struct drm_plane_state *state)
331 {
332 	__drm_atomic_helper_plane_destroy_state(state);
333 	kfree(to_rcar_vsp_plane_state(state));
334 }
335 
rcar_du_vsp_plane_reset(struct drm_plane * plane)336 static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
337 {
338 	struct rcar_du_vsp_plane_state *state;
339 
340 	if (plane->state) {
341 		rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
342 		plane->state = NULL;
343 	}
344 
345 	state = kzalloc(sizeof(*state), GFP_KERNEL);
346 	if (state == NULL)
347 		return;
348 
349 	state->state.alpha = DRM_BLEND_ALPHA_OPAQUE;
350 	state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
351 
352 	plane->state = &state->state;
353 	plane->state->plane = plane;
354 }
355 
356 static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
357 	.update_plane = drm_atomic_helper_update_plane,
358 	.disable_plane = drm_atomic_helper_disable_plane,
359 	.reset = rcar_du_vsp_plane_reset,
360 	.destroy = drm_plane_cleanup,
361 	.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
362 	.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
363 };
364 
rcar_du_vsp_init(struct rcar_du_vsp * vsp,struct device_node * np,unsigned int crtcs)365 int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
366 		     unsigned int crtcs)
367 {
368 	struct rcar_du_device *rcdu = vsp->dev;
369 	struct platform_device *pdev;
370 	unsigned int num_crtcs = hweight32(crtcs);
371 	unsigned int i;
372 	int ret;
373 
374 	/* Find the VSP device and initialize it. */
375 	pdev = of_find_device_by_node(np);
376 	if (!pdev)
377 		return -ENXIO;
378 
379 	vsp->vsp = &pdev->dev;
380 
381 	ret = vsp1_du_init(vsp->vsp);
382 	if (ret < 0)
383 		return ret;
384 
385 	 /*
386 	  * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
387 	  * 4 RPFs.
388 	  */
389 	vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
390 
391 	vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
392 				   sizeof(*vsp->planes), GFP_KERNEL);
393 	if (!vsp->planes)
394 		return -ENOMEM;
395 
396 	for (i = 0; i < vsp->num_planes; ++i) {
397 		enum drm_plane_type type = i < num_crtcs
398 					 ? DRM_PLANE_TYPE_PRIMARY
399 					 : DRM_PLANE_TYPE_OVERLAY;
400 		struct rcar_du_vsp_plane *plane = &vsp->planes[i];
401 
402 		plane->vsp = vsp;
403 		plane->index = i;
404 
405 		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
406 					       &rcar_du_vsp_plane_funcs,
407 					       formats_kms,
408 					       ARRAY_SIZE(formats_kms),
409 					       NULL, type, NULL);
410 		if (ret < 0)
411 			return ret;
412 
413 		drm_plane_helper_add(&plane->plane,
414 				     &rcar_du_vsp_plane_helper_funcs);
415 
416 		if (type == DRM_PLANE_TYPE_PRIMARY)
417 			continue;
418 
419 		drm_plane_create_alpha_property(&plane->plane);
420 		drm_plane_create_zpos_property(&plane->plane, 1, 1,
421 					       vsp->num_planes - 1);
422 	}
423 
424 	return 0;
425 }
426