1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * R-Car Display Unit VSP-Based Compositor
4 *
5 * Copyright (C) 2015 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10#include <drm/drm_atomic.h>
11#include <drm/drm_atomic_helper.h>
12#include <drm/drm_blend.h>
13#include <drm/drm_crtc.h>
14#include <drm/drm_fb_dma_helper.h>
15#include <drm/drm_fourcc.h>
16#include <drm/drm_framebuffer.h>
17#include <drm/drm_gem_atomic_helper.h>
18#include <drm/drm_gem_dma_helper.h>
19#include <drm/drm_managed.h>
20#include <drm/drm_vblank.h>
21
22#include <linux/bitops.h>
23#include <linux/dma-mapping.h>
24#include <linux/of_platform.h>
25#include <linux/platform_device.h>
26#include <linux/scatterlist.h>
27#include <linux/slab.h>
28#include <linux/videodev2.h>
29
30#include <media/vsp1.h>
31
32#include "rcar_du_drv.h"
33#include "rcar_du_kms.h"
34#include "rcar_du_vsp.h"
35#include "rcar_du_writeback.h"
36
37static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
38{
39	struct rcar_du_crtc *crtc = private;
40
41	if (crtc->vblank_enable)
42		drm_crtc_handle_vblank(&crtc->crtc);
43
44	if (status & VSP1_DU_STATUS_COMPLETE)
45		rcar_du_crtc_finish_page_flip(crtc);
46	if (status & VSP1_DU_STATUS_WRITEBACK)
47		rcar_du_writeback_complete(crtc);
48
49	drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
50}
51
52void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
53{
54	const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
55	struct rcar_du_device *rcdu = crtc->dev;
56	struct vsp1_du_lif_config cfg = {
57		.width = mode->hdisplay,
58		.height = mode->vdisplay,
59		.interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
60		.callback = rcar_du_vsp_complete,
61		.callback_data = crtc,
62	};
63	struct rcar_du_plane_state state = {
64		.state = {
65			.alpha = DRM_BLEND_ALPHA_OPAQUE,
66			.crtc = &crtc->crtc,
67			.dst.x1 = 0,
68			.dst.y1 = 0,
69			.dst.x2 = mode->hdisplay,
70			.dst.y2 = mode->vdisplay,
71			.src.x1 = 0,
72			.src.y1 = 0,
73			.src.x2 = mode->hdisplay << 16,
74			.src.y2 = mode->vdisplay << 16,
75			.zpos = 0,
76		},
77		.format = rcar_du_format_info(DRM_FORMAT_XRGB8888),
78		.source = RCAR_DU_PLANE_VSPD1,
79		.colorkey = 0,
80	};
81
82	if (rcdu->info->gen >= 3)
83		state.hwindex = (crtc->index % 2) ? 2 : 0;
84	else
85		state.hwindex = crtc->index % 2;
86
87	__rcar_du_plane_setup(crtc->group, &state);
88
89	vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
90}
91
92void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
93{
94	vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
95}
96
97void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
98{
99	vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
100}
101
102void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
103{
104	struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
105	struct rcar_du_crtc_state *state;
106
107	state = to_rcar_crtc_state(crtc->crtc.state);
108	cfg.crc = state->crc;
109
110	rcar_du_writeback_setup(crtc, &cfg.writeback);
111
112	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
113}
114
115static const u32 rcar_du_vsp_formats[] = {
116	DRM_FORMAT_RGB332,
117	DRM_FORMAT_ARGB4444,
118	DRM_FORMAT_XRGB4444,
119	DRM_FORMAT_ARGB1555,
120	DRM_FORMAT_XRGB1555,
121	DRM_FORMAT_RGB565,
122	DRM_FORMAT_BGR888,
123	DRM_FORMAT_RGB888,
124	DRM_FORMAT_BGRA8888,
125	DRM_FORMAT_BGRX8888,
126	DRM_FORMAT_ABGR8888,
127	DRM_FORMAT_XBGR8888,
128	DRM_FORMAT_ARGB8888,
129	DRM_FORMAT_XRGB8888,
130	DRM_FORMAT_UYVY,
131	DRM_FORMAT_YUYV,
132	DRM_FORMAT_YVYU,
133	DRM_FORMAT_NV12,
134	DRM_FORMAT_NV21,
135	DRM_FORMAT_NV16,
136	DRM_FORMAT_NV61,
137	DRM_FORMAT_YUV420,
138	DRM_FORMAT_YVU420,
139	DRM_FORMAT_YUV422,
140	DRM_FORMAT_YVU422,
141	DRM_FORMAT_YUV444,
142	DRM_FORMAT_YVU444,
143};
144
145/*
146 * Gen4 supports the same formats as above, and additionally 2-10-10-10 RGB
147 * formats and Y210 & Y212 formats.
148 */
149static const u32 rcar_du_vsp_formats_gen4[] = {
150	DRM_FORMAT_RGB332,
151	DRM_FORMAT_ARGB4444,
152	DRM_FORMAT_XRGB4444,
153	DRM_FORMAT_ARGB1555,
154	DRM_FORMAT_XRGB1555,
155	DRM_FORMAT_RGB565,
156	DRM_FORMAT_BGR888,
157	DRM_FORMAT_RGB888,
158	DRM_FORMAT_BGRA8888,
159	DRM_FORMAT_BGRX8888,
160	DRM_FORMAT_ABGR8888,
161	DRM_FORMAT_XBGR8888,
162	DRM_FORMAT_ARGB8888,
163	DRM_FORMAT_XRGB8888,
164	DRM_FORMAT_RGBX1010102,
165	DRM_FORMAT_RGBA1010102,
166	DRM_FORMAT_ARGB2101010,
167	DRM_FORMAT_UYVY,
168	DRM_FORMAT_YUYV,
169	DRM_FORMAT_YVYU,
170	DRM_FORMAT_NV12,
171	DRM_FORMAT_NV21,
172	DRM_FORMAT_NV16,
173	DRM_FORMAT_NV61,
174	DRM_FORMAT_YUV420,
175	DRM_FORMAT_YVU420,
176	DRM_FORMAT_YUV422,
177	DRM_FORMAT_YVU422,
178	DRM_FORMAT_YUV444,
179	DRM_FORMAT_YVU444,
180	DRM_FORMAT_Y210,
181	DRM_FORMAT_Y212,
182};
183
184static u32 rcar_du_vsp_state_get_format(struct rcar_du_vsp_plane_state *state)
185{
186	u32 fourcc = state->format->fourcc;
187
188	if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) {
189		switch (fourcc) {
190		case DRM_FORMAT_ARGB1555:
191			fourcc = DRM_FORMAT_XRGB1555;
192			break;
193
194		case DRM_FORMAT_ARGB4444:
195			fourcc = DRM_FORMAT_XRGB4444;
196			break;
197
198		case DRM_FORMAT_ARGB8888:
199			fourcc = DRM_FORMAT_XRGB8888;
200			break;
201
202		case DRM_FORMAT_ABGR8888:
203			fourcc = DRM_FORMAT_XBGR8888;
204			break;
205
206		case DRM_FORMAT_BGRA8888:
207			fourcc = DRM_FORMAT_BGRX8888;
208			break;
209
210		case DRM_FORMAT_RGBA1010102:
211			fourcc = DRM_FORMAT_RGBX1010102;
212			break;
213		}
214	}
215
216	return fourcc;
217}
218
219static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
220{
221	struct rcar_du_vsp_plane_state *state =
222		to_rcar_vsp_plane_state(plane->plane.state);
223	struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
224	struct drm_framebuffer *fb = plane->plane.state->fb;
225	const struct rcar_du_format_info *format;
226	struct vsp1_du_atomic_config cfg = {
227		.pixelformat = 0,
228		.pitch = fb->pitches[0],
229		.alpha = state->state.alpha >> 8,
230		.zpos = state->state.zpos,
231	};
232	u32 fourcc = rcar_du_vsp_state_get_format(state);
233	unsigned int i;
234
235	cfg.src.left = state->state.src.x1 >> 16;
236	cfg.src.top = state->state.src.y1 >> 16;
237	cfg.src.width = drm_rect_width(&state->state.src) >> 16;
238	cfg.src.height = drm_rect_height(&state->state.src) >> 16;
239
240	cfg.dst.left = state->state.dst.x1;
241	cfg.dst.top = state->state.dst.y1;
242	cfg.dst.width = drm_rect_width(&state->state.dst);
243	cfg.dst.height = drm_rect_height(&state->state.dst);
244
245	for (i = 0; i < state->format->planes; ++i)
246		cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
247			   + fb->offsets[i];
248
249	format = rcar_du_format_info(fourcc);
250	cfg.pixelformat = format->v4l2;
251
252	cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI;
253
254	vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
255			      plane->index, &cfg);
256}
257
258int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
259		       struct sg_table sg_tables[3])
260{
261	struct rcar_du_device *rcdu = vsp->dev;
262	unsigned int i, j;
263	int ret;
264
265	for (i = 0; i < fb->format->num_planes; ++i) {
266		struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
267		struct sg_table *sgt = &sg_tables[i];
268
269		if (gem->sgt) {
270			struct scatterlist *src;
271			struct scatterlist *dst;
272
273			/*
274			 * If the GEM buffer has a scatter gather table, it has
275			 * been imported from a dma-buf and has no physical
276			 * address as it might not be physically contiguous.
277			 * Copy the original scatter gather table to map it to
278			 * the VSP.
279			 */
280			ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
281					     GFP_KERNEL);
282			if (ret)
283				goto fail;
284
285			src = gem->sgt->sgl;
286			dst = sgt->sgl;
287			for (j = 0; j < gem->sgt->orig_nents; ++j) {
288				sg_set_page(dst, sg_page(src), src->length,
289					    src->offset);
290				src = sg_next(src);
291				dst = sg_next(dst);
292			}
293		} else {
294			ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr,
295					      gem->dma_addr, gem->base.size);
296			if (ret)
297				goto fail;
298		}
299
300		ret = vsp1_du_map_sg(vsp->vsp, sgt);
301		if (ret) {
302			sg_free_table(sgt);
303			goto fail;
304		}
305	}
306
307	return 0;
308
309fail:
310	while (i--) {
311		struct sg_table *sgt = &sg_tables[i];
312
313		vsp1_du_unmap_sg(vsp->vsp, sgt);
314		sg_free_table(sgt);
315	}
316
317	return ret;
318}
319
320static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
321					struct drm_plane_state *state)
322{
323	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
324	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
325	int ret;
326
327	/*
328	 * There's no need to prepare (and unprepare) the framebuffer when the
329	 * plane is not visible, as it will not be displayed.
330	 */
331	if (!state->visible)
332		return 0;
333
334	ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables);
335	if (ret < 0)
336		return ret;
337
338	return drm_gem_plane_helper_prepare_fb(plane, state);
339}
340
341void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
342			  struct sg_table sg_tables[3])
343{
344	unsigned int i;
345
346	for (i = 0; i < fb->format->num_planes; ++i) {
347		struct sg_table *sgt = &sg_tables[i];
348
349		vsp1_du_unmap_sg(vsp->vsp, sgt);
350		sg_free_table(sgt);
351	}
352}
353
354static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
355					 struct drm_plane_state *state)
356{
357	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
358	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
359
360	if (!state->visible)
361		return;
362
363	rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables);
364}
365
366static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
367					  struct drm_atomic_state *state)
368{
369	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
370										 plane);
371	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(new_plane_state);
372
373	return __rcar_du_plane_atomic_check(plane, new_plane_state,
374					    &rstate->format);
375}
376
377static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
378					struct drm_atomic_state *state)
379{
380	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
381	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
382	struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
383	struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
384
385	if (new_state->visible)
386		rcar_du_vsp_plane_setup(rplane);
387	else if (old_state->crtc)
388		vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
389				      rplane->index, NULL);
390}
391
392static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
393	.prepare_fb = rcar_du_vsp_plane_prepare_fb,
394	.cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
395	.atomic_check = rcar_du_vsp_plane_atomic_check,
396	.atomic_update = rcar_du_vsp_plane_atomic_update,
397};
398
399static struct drm_plane_state *
400rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
401{
402	struct rcar_du_vsp_plane_state *copy;
403
404	if (WARN_ON(!plane->state))
405		return NULL;
406
407	copy = kzalloc(sizeof(*copy), GFP_KERNEL);
408	if (copy == NULL)
409		return NULL;
410
411	__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
412
413	return &copy->state;
414}
415
416static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
417						   struct drm_plane_state *state)
418{
419	__drm_atomic_helper_plane_destroy_state(state);
420	kfree(to_rcar_vsp_plane_state(state));
421}
422
423static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
424{
425	struct rcar_du_vsp_plane_state *state;
426
427	if (plane->state) {
428		rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
429		plane->state = NULL;
430	}
431
432	state = kzalloc(sizeof(*state), GFP_KERNEL);
433	if (state == NULL)
434		return;
435
436	__drm_atomic_helper_plane_reset(plane, &state->state);
437}
438
439static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
440	.update_plane = drm_atomic_helper_update_plane,
441	.disable_plane = drm_atomic_helper_disable_plane,
442	.reset = rcar_du_vsp_plane_reset,
443	.destroy = drm_plane_cleanup,
444	.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
445	.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
446};
447
448static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res)
449{
450	struct rcar_du_vsp *vsp = res;
451	unsigned int i;
452
453	for (i = 0; i < vsp->num_planes; ++i) {
454		struct rcar_du_vsp_plane *plane = &vsp->planes[i];
455
456		drm_plane_cleanup(&plane->plane);
457	}
458
459	kfree(vsp->planes);
460
461	put_device(vsp->vsp);
462}
463
464int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
465		     unsigned int crtcs)
466{
467	struct rcar_du_device *rcdu = vsp->dev;
468	struct platform_device *pdev;
469	unsigned int num_crtcs = hweight32(crtcs);
470	unsigned int num_planes;
471	unsigned int i;
472	int ret;
473
474	/* Find the VSP device and initialize it. */
475	pdev = of_find_device_by_node(np);
476	if (!pdev)
477		return -ENXIO;
478
479	vsp->vsp = &pdev->dev;
480
481	ret = drmm_add_action_or_reset(&rcdu->ddev, rcar_du_vsp_cleanup, vsp);
482	if (ret < 0)
483		return ret;
484
485	ret = vsp1_du_init(vsp->vsp);
486	if (ret < 0)
487		return ret;
488
489	num_planes = rcdu->info->num_rpf;
490
491	vsp->planes = kcalloc(num_planes, sizeof(*vsp->planes), GFP_KERNEL);
492	if (!vsp->planes)
493		return -ENOMEM;
494
495	for (i = 0; i < num_planes; ++i) {
496		enum drm_plane_type type = i < num_crtcs
497					 ? DRM_PLANE_TYPE_PRIMARY
498					 : DRM_PLANE_TYPE_OVERLAY;
499		struct rcar_du_vsp_plane *plane = &vsp->planes[i];
500		unsigned int num_formats;
501		const u32 *formats;
502
503		if (rcdu->info->gen < 4) {
504			num_formats = ARRAY_SIZE(rcar_du_vsp_formats);
505			formats = rcar_du_vsp_formats;
506		} else {
507			num_formats = ARRAY_SIZE(rcar_du_vsp_formats_gen4);
508			formats = rcar_du_vsp_formats_gen4;
509		}
510
511		plane->vsp = vsp;
512		plane->index = i;
513
514		ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
515					       crtcs, &rcar_du_vsp_plane_funcs,
516					       formats, num_formats,
517					       NULL, type, NULL);
518		if (ret < 0)
519			return ret;
520
521		drm_plane_helper_add(&plane->plane,
522				     &rcar_du_vsp_plane_helper_funcs);
523
524		drm_plane_create_alpha_property(&plane->plane);
525		drm_plane_create_zpos_property(&plane->plane, i, 0,
526					       num_planes - 1);
527
528		drm_plane_create_blend_mode_property(&plane->plane,
529					BIT(DRM_MODE_BLEND_PIXEL_NONE) |
530					BIT(DRM_MODE_BLEND_PREMULTI) |
531					BIT(DRM_MODE_BLEND_COVERAGE));
532
533		vsp->num_planes++;
534	}
535
536	return 0;
537}
538