]> Git Repo - linux.git/blob - drivers/gpu/drm/sun4i/sun8i_ui_layer.c
Merge tag 'drm-misc-fixes-2022-08-16' of git://anongit.freedesktop.org/drm/drm-misc...
[linux.git] / drivers / gpu / drm / sun4i / sun8i_ui_layer.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Icenowy Zheng <[email protected]>
4  *
5  * Based on sun4i_layer.h, which is:
6  *   Copyright (C) 2015 Free Electrons
7  *   Copyright (C) 2015 NextThing Co
8  *
9  *   Maxime Ripard <[email protected]>
10  */
11
12 #include <drm/drm_atomic.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_blend.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_fb_cma_helper.h>
17 #include <drm/drm_fourcc.h>
18 #include <drm/drm_framebuffer.h>
19 #include <drm/drm_gem_atomic_helper.h>
20 #include <drm/drm_gem_cma_helper.h>
21 #include <drm/drm_plane_helper.h>
22 #include <drm/drm_probe_helper.h>
23
24 #include "sun8i_mixer.h"
25 #include "sun8i_ui_layer.h"
26 #include "sun8i_ui_scaler.h"
27
28 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
29                                   int overlay, bool enable, unsigned int zpos,
30                                   unsigned int old_zpos)
31 {
32         u32 val, bld_base, ch_base;
33
34         bld_base = sun8i_blender_base(mixer);
35         ch_base = sun8i_channel_base(mixer, channel);
36
37         DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
38                          enable ? "En" : "Dis", channel, overlay);
39
40         if (enable)
41                 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
42         else
43                 val = 0;
44
45         regmap_update_bits(mixer->engine.regs,
46                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
47                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
48
49         if (!enable || zpos != old_zpos) {
50                 regmap_update_bits(mixer->engine.regs,
51                                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
52                                    SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
53                                    0);
54
55                 regmap_update_bits(mixer->engine.regs,
56                                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
57                                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
58                                    0);
59         }
60
61         if (enable) {
62                 val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
63
64                 regmap_update_bits(mixer->engine.regs,
65                                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
66                                    val, val);
67
68                 val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
69
70                 regmap_update_bits(mixer->engine.regs,
71                                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
72                                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
73                                    val);
74         }
75 }
76
77 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
78                                         int overlay, struct drm_plane *plane)
79 {
80         u32 mask, val, ch_base;
81
82         ch_base = sun8i_channel_base(mixer, channel);
83
84         mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
85                 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
86
87         val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
88
89         val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
90                 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
91                 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
92
93         regmap_update_bits(mixer->engine.regs,
94                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
95                            mask, val);
96 }
97
98 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
99                                        int overlay, struct drm_plane *plane,
100                                        unsigned int zpos)
101 {
102         struct drm_plane_state *state = plane->state;
103         u32 src_w, src_h, dst_w, dst_h;
104         u32 bld_base, ch_base;
105         u32 outsize, insize;
106         u32 hphase, vphase;
107
108         DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
109                          channel, overlay);
110
111         bld_base = sun8i_blender_base(mixer);
112         ch_base = sun8i_channel_base(mixer, channel);
113
114         src_w = drm_rect_width(&state->src) >> 16;
115         src_h = drm_rect_height(&state->src) >> 16;
116         dst_w = drm_rect_width(&state->dst);
117         dst_h = drm_rect_height(&state->dst);
118
119         hphase = state->src.x1 & 0xffff;
120         vphase = state->src.y1 & 0xffff;
121
122         insize = SUN8I_MIXER_SIZE(src_w, src_h);
123         outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
124
125         /* Set height and width */
126         DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
127                          state->src.x1 >> 16, state->src.y1 >> 16);
128         DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
129         regmap_write(mixer->engine.regs,
130                      SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
131                      insize);
132         regmap_write(mixer->engine.regs,
133                      SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
134                      insize);
135
136         if (insize != outsize || hphase || vphase) {
137                 u32 hscale, vscale;
138
139                 DRM_DEBUG_DRIVER("HW scaling is enabled\n");
140
141                 hscale = state->src_w / state->crtc_w;
142                 vscale = state->src_h / state->crtc_h;
143
144                 sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
145                                       dst_h, hscale, vscale, hphase, vphase);
146                 sun8i_ui_scaler_enable(mixer, channel, true);
147         } else {
148                 DRM_DEBUG_DRIVER("HW scaling is not needed\n");
149                 sun8i_ui_scaler_enable(mixer, channel, false);
150         }
151
152         /* Set base coordinates */
153         DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
154                          state->dst.x1, state->dst.y1);
155         DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
156         regmap_write(mixer->engine.regs,
157                      SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
158                      SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
159         regmap_write(mixer->engine.regs,
160                      SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
161                      outsize);
162
163         return 0;
164 }
165
166 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
167                                          int overlay, struct drm_plane *plane)
168 {
169         struct drm_plane_state *state = plane->state;
170         const struct drm_format_info *fmt;
171         u32 val, ch_base, hw_fmt;
172         int ret;
173
174         ch_base = sun8i_channel_base(mixer, channel);
175
176         fmt = state->fb->format;
177         ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
178         if (ret || fmt->is_yuv) {
179                 DRM_DEBUG_DRIVER("Invalid format\n");
180                 return -EINVAL;
181         }
182
183         val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
184         regmap_update_bits(mixer->engine.regs,
185                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
186                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
187
188         return 0;
189 }
190
191 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
192                                         int overlay, struct drm_plane *plane)
193 {
194         struct drm_plane_state *state = plane->state;
195         struct drm_framebuffer *fb = state->fb;
196         struct drm_gem_cma_object *gem;
197         dma_addr_t paddr;
198         u32 ch_base;
199         int bpp;
200
201         ch_base = sun8i_channel_base(mixer, channel);
202
203         /* Get the physical address of the buffer in memory */
204         gem = drm_fb_cma_get_gem_obj(fb, 0);
205
206         DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
207
208         /* Compute the start of the displayed memory */
209         bpp = fb->format->cpp[0];
210         paddr = gem->paddr + fb->offsets[0];
211
212         /* Fixup framebuffer address for src coordinates */
213         paddr += (state->src.x1 >> 16) * bpp;
214         paddr += (state->src.y1 >> 16) * fb->pitches[0];
215
216         /* Set the line width */
217         DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
218         regmap_write(mixer->engine.regs,
219                      SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
220                      fb->pitches[0]);
221
222         DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
223
224         regmap_write(mixer->engine.regs,
225                      SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
226                      lower_32_bits(paddr));
227
228         return 0;
229 }
230
231 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
232                                        struct drm_atomic_state *state)
233 {
234         struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
235                                                                                  plane);
236         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
237         struct drm_crtc *crtc = new_plane_state->crtc;
238         struct drm_crtc_state *crtc_state;
239         int min_scale, max_scale;
240
241         if (!crtc)
242                 return 0;
243
244         crtc_state = drm_atomic_get_existing_crtc_state(state,
245                                                         crtc);
246         if (WARN_ON(!crtc_state))
247                 return -EINVAL;
248
249         min_scale = DRM_PLANE_HELPER_NO_SCALING;
250         max_scale = DRM_PLANE_HELPER_NO_SCALING;
251
252         if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
253                 min_scale = SUN8I_UI_SCALER_SCALE_MIN;
254                 max_scale = SUN8I_UI_SCALER_SCALE_MAX;
255         }
256
257         return drm_atomic_helper_check_plane_state(new_plane_state,
258                                                    crtc_state,
259                                                    min_scale, max_scale,
260                                                    true, true);
261 }
262
263 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
264                                           struct drm_atomic_state *state)
265 {
266         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
267                                                                            plane);
268         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
269         unsigned int old_zpos = old_state->normalized_zpos;
270         struct sun8i_mixer *mixer = layer->mixer;
271
272         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
273                               old_zpos);
274 }
275
276 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
277                                          struct drm_atomic_state *state)
278 {
279         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
280                                                                            plane);
281         struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
282                                                                            plane);
283         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
284         unsigned int zpos = new_state->normalized_zpos;
285         unsigned int old_zpos = old_state->normalized_zpos;
286         struct sun8i_mixer *mixer = layer->mixer;
287
288         if (!new_state->visible) {
289                 sun8i_ui_layer_enable(mixer, layer->channel,
290                                       layer->overlay, false, 0, old_zpos);
291                 return;
292         }
293
294         sun8i_ui_layer_update_coord(mixer, layer->channel,
295                                     layer->overlay, plane, zpos);
296         sun8i_ui_layer_update_alpha(mixer, layer->channel,
297                                     layer->overlay, plane);
298         sun8i_ui_layer_update_formats(mixer, layer->channel,
299                                       layer->overlay, plane);
300         sun8i_ui_layer_update_buffer(mixer, layer->channel,
301                                      layer->overlay, plane);
302         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay,
303                               true, zpos, old_zpos);
304 }
305
306 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
307         .atomic_check   = sun8i_ui_layer_atomic_check,
308         .atomic_disable = sun8i_ui_layer_atomic_disable,
309         .atomic_update  = sun8i_ui_layer_atomic_update,
310 };
311
312 static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
313         .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
314         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
315         .destroy                = drm_plane_cleanup,
316         .disable_plane          = drm_atomic_helper_disable_plane,
317         .reset                  = drm_atomic_helper_plane_reset,
318         .update_plane           = drm_atomic_helper_update_plane,
319 };
320
321 static const u32 sun8i_ui_layer_formats[] = {
322         DRM_FORMAT_ABGR1555,
323         DRM_FORMAT_ABGR4444,
324         DRM_FORMAT_ABGR8888,
325         DRM_FORMAT_ARGB1555,
326         DRM_FORMAT_ARGB4444,
327         DRM_FORMAT_ARGB8888,
328         DRM_FORMAT_BGR565,
329         DRM_FORMAT_BGR888,
330         DRM_FORMAT_BGRA5551,
331         DRM_FORMAT_BGRA4444,
332         DRM_FORMAT_BGRA8888,
333         DRM_FORMAT_BGRX8888,
334         DRM_FORMAT_RGB565,
335         DRM_FORMAT_RGB888,
336         DRM_FORMAT_RGBA4444,
337         DRM_FORMAT_RGBA5551,
338         DRM_FORMAT_RGBA8888,
339         DRM_FORMAT_RGBX8888,
340         DRM_FORMAT_XBGR8888,
341         DRM_FORMAT_XRGB8888,
342 };
343
344 static const uint64_t sun8i_layer_modifiers[] = {
345         DRM_FORMAT_MOD_LINEAR,
346         DRM_FORMAT_MOD_INVALID
347 };
348
349 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
350                                                struct sun8i_mixer *mixer,
351                                                int index)
352 {
353         enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
354         int channel = mixer->cfg->vi_num + index;
355         struct sun8i_ui_layer *layer;
356         unsigned int plane_cnt;
357         int ret;
358
359         layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
360         if (!layer)
361                 return ERR_PTR(-ENOMEM);
362
363         if (index == 0)
364                 type = DRM_PLANE_TYPE_PRIMARY;
365
366         /* possible crtcs are set later */
367         ret = drm_universal_plane_init(drm, &layer->plane, 0,
368                                        &sun8i_ui_layer_funcs,
369                                        sun8i_ui_layer_formats,
370                                        ARRAY_SIZE(sun8i_ui_layer_formats),
371                                        sun8i_layer_modifiers, type, NULL);
372         if (ret) {
373                 dev_err(drm->dev, "Couldn't initialize layer\n");
374                 return ERR_PTR(ret);
375         }
376
377         plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
378
379         ret = drm_plane_create_alpha_property(&layer->plane);
380         if (ret) {
381                 dev_err(drm->dev, "Couldn't add alpha property\n");
382                 return ERR_PTR(ret);
383         }
384
385         ret = drm_plane_create_zpos_property(&layer->plane, channel,
386                                              0, plane_cnt - 1);
387         if (ret) {
388                 dev_err(drm->dev, "Couldn't add zpos property\n");
389                 return ERR_PTR(ret);
390         }
391
392         drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
393         layer->mixer = mixer;
394         layer->channel = channel;
395         layer->overlay = 0;
396
397         return layer;
398 }
This page took 0.057411 seconds and 4 git commands to generate.