]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/rcar-du/rcar_du_vsp.c
drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
[linux.git] / drivers / gpu / drm / rcar-du / rcar_du_vsp.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * rcar_du_vsp.h  --  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_helper.h>
11 #include <drm/drm_crtc.h>
12 #include <drm/drm_fb_cma_helper.h>
13 #include <drm/drm_gem_cma_helper.h>
14 #include <drm/drm_gem_framebuffer_helper.h>
15 #include <drm/drm_plane_helper.h>
16 #include <drm/drm_vblank.h>
17
18 #include <linux/bitops.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/of_platform.h>
21 #include <linux/scatterlist.h>
22 #include <linux/videodev2.h>
23
24 #include <media/vsp1.h>
25
26 #include "rcar_du_drv.h"
27 #include "rcar_du_kms.h"
28 #include "rcar_du_vsp.h"
29
30 static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
31 {
32         struct rcar_du_crtc *crtc = private;
33
34         if (crtc->vblank_enable)
35                 drm_crtc_handle_vblank(&crtc->crtc);
36
37         if (status & VSP1_DU_STATUS_COMPLETE)
38                 rcar_du_crtc_finish_page_flip(crtc);
39
40         drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
41 }
42
43 void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
44 {
45         const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
46         struct rcar_du_device *rcdu = crtc->group->dev;
47         struct vsp1_du_lif_config cfg = {
48                 .width = mode->hdisplay,
49                 .height = mode->vdisplay,
50                 .interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
51                 .callback = rcar_du_vsp_complete,
52                 .callback_data = crtc,
53         };
54         struct rcar_du_plane_state state = {
55                 .state = {
56                         .alpha = DRM_BLEND_ALPHA_OPAQUE,
57                         .crtc = &crtc->crtc,
58                         .dst.x1 = 0,
59                         .dst.y1 = 0,
60                         .dst.x2 = mode->hdisplay,
61                         .dst.y2 = mode->vdisplay,
62                         .src.x1 = 0,
63                         .src.y1 = 0,
64                         .src.x2 = mode->hdisplay << 16,
65                         .src.y2 = mode->vdisplay << 16,
66                         .zpos = 0,
67                 },
68                 .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
69                 .source = RCAR_DU_PLANE_VSPD1,
70                 .colorkey = 0,
71         };
72
73         if (rcdu->info->gen >= 3)
74                 state.hwindex = (crtc->index % 2) ? 2 : 0;
75         else
76                 state.hwindex = crtc->index % 2;
77
78         __rcar_du_plane_setup(crtc->group, &state);
79
80         /*
81          * Ensure that the plane source configuration takes effect by requesting
82          * a restart of the group. See rcar_du_plane_atomic_update() for a more
83          * detailed explanation.
84          *
85          * TODO: Check whether this is still needed on Gen3.
86          */
87         crtc->group->need_restart = true;
88
89         vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
90 }
91
92 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
93 {
94         vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
95 }
96
97 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
98 {
99         vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
100 }
101
102 void 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         vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
111 }
112
113 static const u32 rcar_du_vsp_formats[] = {
114         DRM_FORMAT_RGB332,
115         DRM_FORMAT_ARGB4444,
116         DRM_FORMAT_XRGB4444,
117         DRM_FORMAT_ARGB1555,
118         DRM_FORMAT_XRGB1555,
119         DRM_FORMAT_RGB565,
120         DRM_FORMAT_BGR888,
121         DRM_FORMAT_RGB888,
122         DRM_FORMAT_BGRA8888,
123         DRM_FORMAT_BGRX8888,
124         DRM_FORMAT_ARGB8888,
125         DRM_FORMAT_XRGB8888,
126         DRM_FORMAT_UYVY,
127         DRM_FORMAT_YUYV,
128         DRM_FORMAT_YVYU,
129         DRM_FORMAT_NV12,
130         DRM_FORMAT_NV21,
131         DRM_FORMAT_NV16,
132         DRM_FORMAT_NV61,
133         DRM_FORMAT_YUV420,
134         DRM_FORMAT_YVU420,
135         DRM_FORMAT_YUV422,
136         DRM_FORMAT_YVU422,
137         DRM_FORMAT_YUV444,
138         DRM_FORMAT_YVU444,
139 };
140
141 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
142 {
143         struct rcar_du_vsp_plane_state *state =
144                 to_rcar_vsp_plane_state(plane->plane.state);
145         struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
146         struct drm_framebuffer *fb = plane->plane.state->fb;
147         const struct rcar_du_format_info *format;
148         struct vsp1_du_atomic_config cfg = {
149                 .pixelformat = 0,
150                 .pitch = fb->pitches[0],
151                 .alpha = state->state.alpha >> 8,
152                 .zpos = state->state.zpos,
153         };
154         unsigned int i;
155
156         cfg.src.left = state->state.src.x1 >> 16;
157         cfg.src.top = state->state.src.y1 >> 16;
158         cfg.src.width = drm_rect_width(&state->state.src) >> 16;
159         cfg.src.height = drm_rect_height(&state->state.src) >> 16;
160
161         cfg.dst.left = state->state.dst.x1;
162         cfg.dst.top = state->state.dst.y1;
163         cfg.dst.width = drm_rect_width(&state->state.dst);
164         cfg.dst.height = drm_rect_height(&state->state.dst);
165
166         for (i = 0; i < state->format->planes; ++i)
167                 cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
168                            + fb->offsets[i];
169
170         format = rcar_du_format_info(state->format->fourcc);
171         cfg.pixelformat = format->v4l2;
172
173         vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
174                               plane->index, &cfg);
175 }
176
177 static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
178                                         struct drm_plane_state *state)
179 {
180         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
181         struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
182         struct rcar_du_device *rcdu = vsp->dev;
183         unsigned int i;
184         int ret;
185
186         /*
187          * There's no need to prepare (and unprepare) the framebuffer when the
188          * plane is not visible, as it will not be displayed.
189          */
190         if (!state->visible)
191                 return 0;
192
193         for (i = 0; i < rstate->format->planes; ++i) {
194                 struct drm_gem_cma_object *gem =
195                         drm_fb_cma_get_gem_obj(state->fb, i);
196                 struct sg_table *sgt = &rstate->sg_tables[i];
197
198                 ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
199                                       gem->base.size);
200                 if (ret)
201                         goto fail;
202
203                 ret = vsp1_du_map_sg(vsp->vsp, sgt);
204                 if (!ret) {
205                         sg_free_table(sgt);
206                         ret = -ENOMEM;
207                         goto fail;
208                 }
209         }
210
211         ret = drm_gem_fb_prepare_fb(plane, state);
212         if (ret)
213                 goto fail;
214
215         return 0;
216
217 fail:
218         while (i--) {
219                 struct sg_table *sgt = &rstate->sg_tables[i];
220
221                 vsp1_du_unmap_sg(vsp->vsp, sgt);
222                 sg_free_table(sgt);
223         }
224
225         return ret;
226 }
227
228 static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
229                                          struct drm_plane_state *state)
230 {
231         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
232         struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
233         unsigned int i;
234
235         if (!state->visible)
236                 return;
237
238         for (i = 0; i < rstate->format->planes; ++i) {
239                 struct sg_table *sgt = &rstate->sg_tables[i];
240
241                 vsp1_du_unmap_sg(vsp->vsp, sgt);
242                 sg_free_table(sgt);
243         }
244 }
245
246 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
247                                           struct drm_plane_state *state)
248 {
249         struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
250
251         return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
252 }
253
254 static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
255                                         struct drm_plane_state *old_state)
256 {
257         struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
258         struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
259
260         if (plane->state->visible)
261                 rcar_du_vsp_plane_setup(rplane);
262         else
263                 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
264                                       rplane->index, NULL);
265 }
266
267 static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
268         .prepare_fb = rcar_du_vsp_plane_prepare_fb,
269         .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
270         .atomic_check = rcar_du_vsp_plane_atomic_check,
271         .atomic_update = rcar_du_vsp_plane_atomic_update,
272 };
273
274 static struct drm_plane_state *
275 rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
276 {
277         struct rcar_du_vsp_plane_state *copy;
278
279         if (WARN_ON(!plane->state))
280                 return NULL;
281
282         copy = kzalloc(sizeof(*copy), GFP_KERNEL);
283         if (copy == NULL)
284                 return NULL;
285
286         __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
287
288         return &copy->state;
289 }
290
291 static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
292                                                    struct drm_plane_state *state)
293 {
294         __drm_atomic_helper_plane_destroy_state(state);
295         kfree(to_rcar_vsp_plane_state(state));
296 }
297
298 static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
299 {
300         struct rcar_du_vsp_plane_state *state;
301
302         if (plane->state) {
303                 rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
304                 plane->state = NULL;
305         }
306
307         state = kzalloc(sizeof(*state), GFP_KERNEL);
308         if (state == NULL)
309                 return;
310
311         __drm_atomic_helper_plane_reset(plane, &state->state);
312         state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
313 }
314
315 static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
316         .update_plane = drm_atomic_helper_update_plane,
317         .disable_plane = drm_atomic_helper_disable_plane,
318         .reset = rcar_du_vsp_plane_reset,
319         .destroy = drm_plane_cleanup,
320         .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
321         .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
322 };
323
324 int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
325                      unsigned int crtcs)
326 {
327         struct rcar_du_device *rcdu = vsp->dev;
328         struct platform_device *pdev;
329         unsigned int num_crtcs = hweight32(crtcs);
330         unsigned int i;
331         int ret;
332
333         /* Find the VSP device and initialize it. */
334         pdev = of_find_device_by_node(np);
335         if (!pdev)
336                 return -ENXIO;
337
338         vsp->vsp = &pdev->dev;
339
340         ret = vsp1_du_init(vsp->vsp);
341         if (ret < 0)
342                 return ret;
343
344          /*
345           * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
346           * 4 RPFs.
347           */
348         vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
349
350         vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
351                                    sizeof(*vsp->planes), GFP_KERNEL);
352         if (!vsp->planes)
353                 return -ENOMEM;
354
355         for (i = 0; i < vsp->num_planes; ++i) {
356                 enum drm_plane_type type = i < num_crtcs
357                                          ? DRM_PLANE_TYPE_PRIMARY
358                                          : DRM_PLANE_TYPE_OVERLAY;
359                 struct rcar_du_vsp_plane *plane = &vsp->planes[i];
360
361                 plane->vsp = vsp;
362                 plane->index = i;
363
364                 ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
365                                                &rcar_du_vsp_plane_funcs,
366                                                rcar_du_vsp_formats,
367                                                ARRAY_SIZE(rcar_du_vsp_formats),
368                                                NULL, type, NULL);
369                 if (ret < 0)
370                         return ret;
371
372                 drm_plane_helper_add(&plane->plane,
373                                      &rcar_du_vsp_plane_helper_funcs);
374
375                 if (type == DRM_PLANE_TYPE_PRIMARY)
376                         continue;
377
378                 drm_plane_create_alpha_property(&plane->plane);
379                 drm_plane_create_zpos_property(&plane->plane, 1, 1,
380                                                vsp->num_planes - 1);
381         }
382
383         return 0;
384 }