GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / gpu / drm / rcar-du / rcar_du_vsp.c
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_plane_helper.h>
21
22 #include <linux/bitops.h>
23 #include <linux/dma-mapping.h>
24 #include <linux/of_platform.h>
25 #include <linux/scatterlist.h>
26 #include <linux/videodev2.h>
27
28 #include <media/vsp1.h>
29
30 #include "rcar_du_drv.h"
31 #include "rcar_du_kms.h"
32 #include "rcar_du_vsp.h"
33
34 static void rcar_du_vsp_complete(void *private, bool completed)
35 {
36         struct rcar_du_crtc *crtc = private;
37
38         if (crtc->vblank_enable)
39                 drm_crtc_handle_vblank(&crtc->crtc);
40
41         if (completed)
42                 rcar_du_crtc_finish_page_flip(crtc);
43 }
44
45 void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
46 {
47         const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
48         struct rcar_du_device *rcdu = crtc->group->dev;
49         struct vsp1_du_lif_config cfg = {
50                 .width = mode->hdisplay,
51                 .height = mode->vdisplay,
52                 .callback = rcar_du_vsp_complete,
53                 .callback_data = crtc,
54         };
55         struct rcar_du_plane_state state = {
56                 .state = {
57                         .crtc = &crtc->crtc,
58                         .crtc_x = 0,
59                         .crtc_y = 0,
60                         .crtc_w = mode->hdisplay,
61                         .crtc_h = mode->vdisplay,
62                         .src_x = 0,
63                         .src_y = 0,
64                         .src_w = mode->hdisplay << 16,
65                         .src_h = mode->vdisplay << 16,
66                         .zpos = 0,
67                 },
68                 .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
69                 .source = RCAR_DU_PLANE_VSPD1,
70                 .alpha = 255,
71                 .colorkey = 0,
72         };
73
74         if (rcdu->info->gen >= 3)
75                 state.hwindex = (crtc->index % 2) ? 2 : 0;
76         else
77                 state.hwindex = crtc->index % 2;
78
79         __rcar_du_plane_setup(crtc->group, &state);
80
81         /*
82          * Ensure that the plane source configuration takes effect by requesting
83          * a restart of the group. See rcar_du_plane_atomic_update() for a more
84          * detailed explanation.
85          *
86          * TODO: Check whether this is still needed on Gen3.
87          */
88         crtc->group->need_restart = true;
89
90         vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
91 }
92
93 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
94 {
95         vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
96 }
97
98 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
99 {
100         vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
101 }
102
103 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
104 {
105         vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe);
106 }
107
108 /* Keep the two tables in sync. */
109 static const u32 formats_kms[] = {
110         DRM_FORMAT_RGB332,
111         DRM_FORMAT_ARGB4444,
112         DRM_FORMAT_XRGB4444,
113         DRM_FORMAT_ARGB1555,
114         DRM_FORMAT_XRGB1555,
115         DRM_FORMAT_RGB565,
116         DRM_FORMAT_BGR888,
117         DRM_FORMAT_RGB888,
118         DRM_FORMAT_BGRA8888,
119         DRM_FORMAT_BGRX8888,
120         DRM_FORMAT_ARGB8888,
121         DRM_FORMAT_XRGB8888,
122         DRM_FORMAT_UYVY,
123         DRM_FORMAT_VYUY,
124         DRM_FORMAT_YUYV,
125         DRM_FORMAT_YVYU,
126         DRM_FORMAT_NV12,
127         DRM_FORMAT_NV21,
128         DRM_FORMAT_NV16,
129         DRM_FORMAT_NV61,
130         DRM_FORMAT_YUV420,
131         DRM_FORMAT_YVU420,
132         DRM_FORMAT_YUV422,
133         DRM_FORMAT_YVU422,
134         DRM_FORMAT_YUV444,
135         DRM_FORMAT_YVU444,
136 };
137
138 static const u32 formats_v4l2[] = {
139         V4L2_PIX_FMT_RGB332,
140         V4L2_PIX_FMT_ARGB444,
141         V4L2_PIX_FMT_XRGB444,
142         V4L2_PIX_FMT_ARGB555,
143         V4L2_PIX_FMT_XRGB555,
144         V4L2_PIX_FMT_RGB565,
145         V4L2_PIX_FMT_RGB24,
146         V4L2_PIX_FMT_BGR24,
147         V4L2_PIX_FMT_ARGB32,
148         V4L2_PIX_FMT_XRGB32,
149         V4L2_PIX_FMT_ABGR32,
150         V4L2_PIX_FMT_XBGR32,
151         V4L2_PIX_FMT_UYVY,
152         V4L2_PIX_FMT_VYUY,
153         V4L2_PIX_FMT_YUYV,
154         V4L2_PIX_FMT_YVYU,
155         V4L2_PIX_FMT_NV12M,
156         V4L2_PIX_FMT_NV21M,
157         V4L2_PIX_FMT_NV16M,
158         V4L2_PIX_FMT_NV61M,
159         V4L2_PIX_FMT_YUV420M,
160         V4L2_PIX_FMT_YVU420M,
161         V4L2_PIX_FMT_YUV422M,
162         V4L2_PIX_FMT_YVU422M,
163         V4L2_PIX_FMT_YUV444M,
164         V4L2_PIX_FMT_YVU444M,
165 };
166
167 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
168 {
169         struct rcar_du_vsp_plane_state *state =
170                 to_rcar_vsp_plane_state(plane->plane.state);
171         struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
172         struct drm_framebuffer *fb = plane->plane.state->fb;
173         struct vsp1_du_atomic_config cfg = {
174                 .pixelformat = 0,
175                 .pitch = fb->pitches[0],
176                 .alpha = state->alpha,
177                 .zpos = state->state.zpos,
178         };
179         unsigned int i;
180
181         cfg.src.left = state->state.src_x >> 16;
182         cfg.src.top = state->state.src_y >> 16;
183         cfg.src.width = state->state.src_w >> 16;
184         cfg.src.height = state->state.src_h >> 16;
185
186         cfg.dst.left = state->state.crtc_x;
187         cfg.dst.top = state->state.crtc_y;
188         cfg.dst.width = state->state.crtc_w;
189         cfg.dst.height = state->state.crtc_h;
190
191         for (i = 0; i < state->format->planes; ++i)
192                 cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
193                            + fb->offsets[i];
194
195         for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
196                 if (formats_kms[i] == state->format->fourcc) {
197                         cfg.pixelformat = formats_v4l2[i];
198                         break;
199                 }
200         }
201
202         vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
203                               plane->index, &cfg);
204 }
205
206 static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
207                                         struct drm_plane_state *state)
208 {
209         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
210         struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
211         struct rcar_du_device *rcdu = vsp->dev;
212         unsigned int i;
213         int ret;
214
215         if (!state->fb)
216                 return 0;
217
218         for (i = 0; i < rstate->format->planes; ++i) {
219                 struct drm_gem_cma_object *gem =
220                         drm_fb_cma_get_gem_obj(state->fb, i);
221                 struct sg_table *sgt = &rstate->sg_tables[i];
222
223                 ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
224                                       gem->base.size);
225                 if (ret)
226                         goto fail;
227
228                 ret = vsp1_du_map_sg(vsp->vsp, sgt);
229                 if (!ret) {
230                         sg_free_table(sgt);
231                         ret = -ENOMEM;
232                         goto fail;
233                 }
234         }
235
236         return 0;
237
238 fail:
239         while (i--) {
240                 struct sg_table *sgt = &rstate->sg_tables[i];
241
242                 vsp1_du_unmap_sg(vsp->vsp, sgt);
243                 sg_free_table(sgt);
244         }
245
246         return ret;
247 }
248
249 static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
250                                          struct drm_plane_state *state)
251 {
252         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
253         struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
254         unsigned int i;
255
256         if (!state->fb)
257                 return;
258
259         for (i = 0; i < rstate->format->planes; ++i) {
260                 struct sg_table *sgt = &rstate->sg_tables[i];
261
262                 vsp1_du_unmap_sg(vsp->vsp, sgt);
263                 sg_free_table(sgt);
264         }
265 }
266
267 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
268                                           struct drm_plane_state *state)
269 {
270         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
271         struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
272         struct rcar_du_device *rcdu = rplane->vsp->dev;
273
274         if (!state->fb || !state->crtc) {
275                 rstate->format = NULL;
276                 return 0;
277         }
278
279         if (state->src_w >> 16 != state->crtc_w ||
280             state->src_h >> 16 != state->crtc_h) {
281                 dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
282                 return -EINVAL;
283         }
284
285         rstate->format = rcar_du_format_info(state->fb->format->format);
286         if (rstate->format == NULL) {
287                 dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
288                         state->fb->format->format);
289                 return -EINVAL;
290         }
291
292         return 0;
293 }
294
295 static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
296                                         struct drm_plane_state *old_state)
297 {
298         struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
299         struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
300
301         if (plane->state->crtc)
302                 rcar_du_vsp_plane_setup(rplane);
303         else
304                 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
305                                       rplane->index, NULL);
306 }
307
308 static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
309         .prepare_fb = rcar_du_vsp_plane_prepare_fb,
310         .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
311         .atomic_check = rcar_du_vsp_plane_atomic_check,
312         .atomic_update = rcar_du_vsp_plane_atomic_update,
313 };
314
315 static struct drm_plane_state *
316 rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
317 {
318         struct rcar_du_vsp_plane_state *state;
319         struct rcar_du_vsp_plane_state *copy;
320
321         if (WARN_ON(!plane->state))
322                 return NULL;
323
324         state = to_rcar_vsp_plane_state(plane->state);
325         copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
326         if (copy == NULL)
327                 return NULL;
328
329         __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
330
331         return &copy->state;
332 }
333
334 static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
335                                                    struct drm_plane_state *state)
336 {
337         __drm_atomic_helper_plane_destroy_state(state);
338         kfree(to_rcar_vsp_plane_state(state));
339 }
340
341 static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
342 {
343         struct rcar_du_vsp_plane_state *state;
344
345         if (plane->state) {
346                 rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
347                 plane->state = NULL;
348         }
349
350         state = kzalloc(sizeof(*state), GFP_KERNEL);
351         if (state == NULL)
352                 return;
353
354         state->alpha = 255;
355         state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
356
357         plane->state = &state->state;
358         plane->state->plane = plane;
359 }
360
361 static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
362         struct drm_plane_state *state, struct drm_property *property,
363         uint64_t val)
364 {
365         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
366         struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
367
368         if (property == rcdu->props.alpha)
369                 rstate->alpha = val;
370         else
371                 return -EINVAL;
372
373         return 0;
374 }
375
376 static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
377         const struct drm_plane_state *state, struct drm_property *property,
378         uint64_t *val)
379 {
380         const struct rcar_du_vsp_plane_state *rstate =
381                 container_of(state, const struct rcar_du_vsp_plane_state, state);
382         struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
383
384         if (property == rcdu->props.alpha)
385                 *val = rstate->alpha;
386         else
387                 return -EINVAL;
388
389         return 0;
390 }
391
392 static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
393         .update_plane = drm_atomic_helper_update_plane,
394         .disable_plane = drm_atomic_helper_disable_plane,
395         .reset = rcar_du_vsp_plane_reset,
396         .destroy = drm_plane_cleanup,
397         .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
398         .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
399         .atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
400         .atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
401 };
402
403 int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
404                      unsigned int crtcs)
405 {
406         struct rcar_du_device *rcdu = vsp->dev;
407         struct platform_device *pdev;
408         unsigned int num_crtcs = hweight32(crtcs);
409         unsigned int i;
410         int ret;
411
412         /* Find the VSP device and initialize it. */
413         pdev = of_find_device_by_node(np);
414         if (!pdev)
415                 return -ENXIO;
416
417         vsp->vsp = &pdev->dev;
418
419         ret = vsp1_du_init(vsp->vsp);
420         if (ret < 0)
421                 return ret;
422
423          /*
424           * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
425           * 4 RPFs.
426           */
427         vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
428
429         vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
430                                    sizeof(*vsp->planes), GFP_KERNEL);
431         if (!vsp->planes)
432                 return -ENOMEM;
433
434         for (i = 0; i < vsp->num_planes; ++i) {
435                 enum drm_plane_type type = i < num_crtcs
436                                          ? DRM_PLANE_TYPE_PRIMARY
437                                          : DRM_PLANE_TYPE_OVERLAY;
438                 struct rcar_du_vsp_plane *plane = &vsp->planes[i];
439
440                 plane->vsp = vsp;
441                 plane->index = i;
442
443                 ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
444                                                &rcar_du_vsp_plane_funcs,
445                                                formats_kms,
446                                                ARRAY_SIZE(formats_kms),
447                                                NULL, type, NULL);
448                 if (ret < 0)
449                         return ret;
450
451                 drm_plane_helper_add(&plane->plane,
452                                      &rcar_du_vsp_plane_helper_funcs);
453
454                 if (type == DRM_PLANE_TYPE_PRIMARY)
455                         continue;
456
457                 drm_object_attach_property(&plane->plane.base,
458                                            rcdu->props.alpha, 255);
459                 drm_plane_create_zpos_property(&plane->plane, 1, 1,
460                                                vsp->num_planes - 1);
461         }
462
463         return 0;
464 }