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