GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / gpu / drm / sun4i / sun8i_vi_layer.c
1 /*
2  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  */
9
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_crtc.h>
13 #include <drm/drm_crtc_helper.h>
14 #include <drm/drm_fb_cma_helper.h>
15 #include <drm/drm_gem_cma_helper.h>
16 #include <drm/drm_plane_helper.h>
17 #include <drm/drmP.h>
18
19 #include "sun8i_vi_layer.h"
20 #include "sun8i_mixer.h"
21 #include "sun8i_vi_scaler.h"
22
23 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
24                                   int overlay, bool enable, unsigned int zpos,
25                                   unsigned int old_zpos)
26 {
27         u32 val;
28
29         DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
30                          enable ? "En" : "Dis", channel, overlay);
31
32         if (enable)
33                 val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
34         else
35                 val = 0;
36
37         regmap_update_bits(mixer->engine.regs,
38                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
39                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
40
41         if (!enable || zpos != old_zpos) {
42                 regmap_update_bits(mixer->engine.regs,
43                                    SUN8I_MIXER_BLEND_PIPE_CTL,
44                                    SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
45                                    0);
46
47                 regmap_update_bits(mixer->engine.regs,
48                                    SUN8I_MIXER_BLEND_ROUTE,
49                                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
50                                    0);
51         }
52
53         if (enable) {
54                 val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
55
56                 regmap_update_bits(mixer->engine.regs,
57                                    SUN8I_MIXER_BLEND_PIPE_CTL, val, val);
58
59                 val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
60
61                 regmap_update_bits(mixer->engine.regs,
62                                    SUN8I_MIXER_BLEND_ROUTE,
63                                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
64                                    val);
65         }
66 }
67
68 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
69                                        int overlay, struct drm_plane *plane,
70                                        unsigned int zpos)
71 {
72         struct drm_plane_state *state = plane->state;
73         const struct drm_format_info *format = state->fb->format;
74         u32 src_w, src_h, dst_w, dst_h;
75         u32 outsize, insize;
76         u32 hphase, vphase;
77         bool subsampled;
78
79         DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
80                          channel, overlay);
81
82         src_w = drm_rect_width(&state->src) >> 16;
83         src_h = drm_rect_height(&state->src) >> 16;
84         dst_w = drm_rect_width(&state->dst);
85         dst_h = drm_rect_height(&state->dst);
86
87         hphase = state->src.x1 & 0xffff;
88         vphase = state->src.y1 & 0xffff;
89
90         /* make coordinates dividable by subsampling factor */
91         if (format->hsub > 1) {
92                 int mask, remainder;
93
94                 mask = format->hsub - 1;
95                 remainder = (state->src.x1 >> 16) & mask;
96                 src_w = (src_w + remainder) & ~mask;
97                 hphase += remainder << 16;
98         }
99
100         if (format->vsub > 1) {
101                 int mask, remainder;
102
103                 mask = format->vsub - 1;
104                 remainder = (state->src.y1 >> 16) & mask;
105                 src_h = (src_h + remainder) & ~mask;
106                 vphase += remainder << 16;
107         }
108
109         insize = SUN8I_MIXER_SIZE(src_w, src_h);
110         outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
111
112         /* Set height and width */
113         DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
114                          (state->src.x1 >> 16) & ~(format->hsub - 1),
115                          (state->src.y1 >> 16) & ~(format->vsub - 1));
116         DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
117         regmap_write(mixer->engine.regs,
118                      SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
119                      insize);
120         regmap_write(mixer->engine.regs,
121                      SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
122                      insize);
123
124         /*
125          * Scaler must be enabled for subsampled formats, so it scales
126          * chroma to same size as luma.
127          */
128         subsampled = format->hsub > 1 || format->vsub > 1;
129
130         if (insize != outsize || subsampled || hphase || vphase) {
131                 u32 hscale, vscale;
132
133                 DRM_DEBUG_DRIVER("HW scaling is enabled\n");
134
135                 hscale = state->src_w / state->crtc_w;
136                 vscale = state->src_h / state->crtc_h;
137
138                 sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
139                                       dst_h, hscale, vscale, hphase, vphase,
140                                       format);
141                 sun8i_vi_scaler_enable(mixer, channel, true);
142         } else {
143                 DRM_DEBUG_DRIVER("HW scaling is not needed\n");
144                 sun8i_vi_scaler_enable(mixer, channel, false);
145         }
146
147         /* Set base coordinates */
148         DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
149                          state->dst.x1, state->dst.y1);
150         DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
151         regmap_write(mixer->engine.regs,
152                      SUN8I_MIXER_BLEND_ATTR_COORD(zpos),
153                      SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
154         regmap_write(mixer->engine.regs,
155                      SUN8I_MIXER_BLEND_ATTR_INSIZE(zpos),
156                      outsize);
157
158         return 0;
159 }
160
161 static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
162                                          int overlay, struct drm_plane *plane)
163 {
164         struct drm_plane_state *state = plane->state;
165         const struct de2_fmt_info *fmt_info;
166         u32 val;
167
168         fmt_info = sun8i_mixer_format_info(state->fb->format->format);
169         if (!fmt_info) {
170                 DRM_DEBUG_DRIVER("Invalid format\n");
171                 return -EINVAL;
172         }
173
174         val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
175         regmap_update_bits(mixer->engine.regs,
176                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
177                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
178
179         if (fmt_info->csc != SUN8I_CSC_MODE_OFF) {
180                 sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc);
181                 sun8i_csc_enable_ccsc(mixer, channel, true);
182         } else {
183                 sun8i_csc_enable_ccsc(mixer, channel, false);
184         }
185
186         if (fmt_info->rgb)
187                 val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
188         else
189                 val = 0;
190
191         regmap_update_bits(mixer->engine.regs,
192                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
193                            SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
194
195         return 0;
196 }
197
198 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
199                                         int overlay, struct drm_plane *plane)
200 {
201         struct drm_plane_state *state = plane->state;
202         struct drm_framebuffer *fb = state->fb;
203         const struct drm_format_info *format = fb->format;
204         struct drm_gem_cma_object *gem;
205         u32 dx, dy, src_x, src_y;
206         dma_addr_t paddr;
207         int i;
208
209         /* Adjust x and y to be dividable by subsampling factor */
210         src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
211         src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
212
213         for (i = 0; i < format->num_planes; i++) {
214                 /* Get the physical address of the buffer in memory */
215                 gem = drm_fb_cma_get_gem_obj(fb, i);
216
217                 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
218
219                 /* Compute the start of the displayed memory */
220                 paddr = gem->paddr + fb->offsets[i];
221
222                 dx = src_x;
223                 dy = src_y;
224
225                 if (i > 0) {
226                         dx /= format->hsub;
227                         dy /= format->vsub;
228                 }
229
230                 /* Fixup framebuffer address for src coordinates */
231                 paddr += dx * format->cpp[i];
232                 paddr += dy * fb->pitches[i];
233
234                 /* Set the line width */
235                 DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
236                                  i + 1, fb->pitches[i]);
237                 regmap_write(mixer->engine.regs,
238                              SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel,
239                                                              overlay, i),
240                fb->pitches[i]);
241
242                 DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
243                                  i + 1, &paddr);
244
245                 regmap_write(mixer->engine.regs,
246                              SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel,
247                                                                  overlay, i),
248                lower_32_bits(paddr));
249         }
250
251         return 0;
252 }
253
254 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
255                                        struct drm_plane_state *state)
256 {
257         struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
258         struct drm_crtc *crtc = state->crtc;
259         struct drm_crtc_state *crtc_state;
260         int min_scale, max_scale;
261
262         if (!crtc)
263                 return 0;
264
265         crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
266         if (WARN_ON(!crtc_state))
267                 return -EINVAL;
268
269         min_scale = DRM_PLANE_HELPER_NO_SCALING;
270         max_scale = DRM_PLANE_HELPER_NO_SCALING;
271
272         if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
273                 min_scale = SUN8I_VI_SCALER_SCALE_MIN;
274                 max_scale = SUN8I_VI_SCALER_SCALE_MAX;
275         }
276
277         return drm_atomic_helper_check_plane_state(state, crtc_state,
278                                                    min_scale, max_scale,
279                                                    true, true);
280 }
281
282 static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
283                                           struct drm_plane_state *old_state)
284 {
285         struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
286         unsigned int old_zpos = old_state->normalized_zpos;
287         struct sun8i_mixer *mixer = layer->mixer;
288
289         sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
290                               old_zpos);
291 }
292
293 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
294                                          struct drm_plane_state *old_state)
295 {
296         struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
297         unsigned int zpos = plane->state->normalized_zpos;
298         unsigned int old_zpos = old_state->normalized_zpos;
299         struct sun8i_mixer *mixer = layer->mixer;
300
301         if (!plane->state->visible) {
302                 sun8i_vi_layer_enable(mixer, layer->channel,
303                                       layer->overlay, false, 0, old_zpos);
304                 return;
305         }
306
307         sun8i_vi_layer_update_coord(mixer, layer->channel,
308                                     layer->overlay, plane, zpos);
309         sun8i_vi_layer_update_formats(mixer, layer->channel,
310                                       layer->overlay, plane);
311         sun8i_vi_layer_update_buffer(mixer, layer->channel,
312                                      layer->overlay, plane);
313         sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
314                               true, zpos, old_zpos);
315 }
316
317 static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
318         .atomic_check   = sun8i_vi_layer_atomic_check,
319         .atomic_disable = sun8i_vi_layer_atomic_disable,
320         .atomic_update  = sun8i_vi_layer_atomic_update,
321 };
322
323 static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
324         .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
325         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
326         .destroy                = drm_plane_cleanup,
327         .disable_plane          = drm_atomic_helper_disable_plane,
328         .reset                  = drm_atomic_helper_plane_reset,
329         .update_plane           = drm_atomic_helper_update_plane,
330 };
331
332 /*
333  * While DE2 VI layer supports same RGB formats as UI layer, alpha
334  * channel is ignored. This structure lists all unique variants
335  * where alpha channel is replaced with "don't care" (X) channel.
336  */
337 static const u32 sun8i_vi_layer_formats[] = {
338         DRM_FORMAT_BGR565,
339         DRM_FORMAT_BGR888,
340         DRM_FORMAT_BGRX4444,
341         DRM_FORMAT_BGRX5551,
342         DRM_FORMAT_BGRX8888,
343         DRM_FORMAT_RGB565,
344         DRM_FORMAT_RGB888,
345         DRM_FORMAT_RGBX4444,
346         DRM_FORMAT_RGBX5551,
347         DRM_FORMAT_RGBX8888,
348         DRM_FORMAT_XBGR1555,
349         DRM_FORMAT_XBGR4444,
350         DRM_FORMAT_XBGR8888,
351         DRM_FORMAT_XRGB1555,
352         DRM_FORMAT_XRGB4444,
353         DRM_FORMAT_XRGB8888,
354
355         DRM_FORMAT_NV16,
356         DRM_FORMAT_NV12,
357         DRM_FORMAT_NV21,
358         DRM_FORMAT_NV61,
359         DRM_FORMAT_UYVY,
360         DRM_FORMAT_VYUY,
361         DRM_FORMAT_YUYV,
362         DRM_FORMAT_YVYU,
363         DRM_FORMAT_YUV411,
364         DRM_FORMAT_YUV420,
365         DRM_FORMAT_YUV422,
366         DRM_FORMAT_YVU411,
367         DRM_FORMAT_YVU420,
368         DRM_FORMAT_YVU422,
369 };
370
371 struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
372                                                struct sun8i_mixer *mixer,
373                                                int index)
374 {
375         struct sun8i_vi_layer *layer;
376         unsigned int plane_cnt;
377         int ret;
378
379         layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
380         if (!layer)
381                 return ERR_PTR(-ENOMEM);
382
383         /* possible crtcs are set later */
384         ret = drm_universal_plane_init(drm, &layer->plane, 0,
385                                        &sun8i_vi_layer_funcs,
386                                        sun8i_vi_layer_formats,
387                                        ARRAY_SIZE(sun8i_vi_layer_formats),
388                                        NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
389         if (ret) {
390                 dev_err(drm->dev, "Couldn't initialize layer\n");
391                 return ERR_PTR(ret);
392         }
393
394         plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
395
396         ret = drm_plane_create_zpos_property(&layer->plane, index,
397                                              0, plane_cnt - 1);
398         if (ret) {
399                 dev_err(drm->dev, "Couldn't add zpos property\n");
400                 return ERR_PTR(ret);
401         }
402
403         drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
404         layer->mixer = mixer;
405         layer->channel = index;
406         layer->overlay = 0;
407
408         return layer;
409 }