]> Git Repo - linux.git/blob - drivers/gpu/drm/sun4i/sun8i_ui_layer.c
Merge tag 'devicetree-for-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / sun4i / sun8i_ui_layer.c
1 /*
2  * Copyright (C) Icenowy Zheng <[email protected]>
3  *
4  * Based on sun4i_layer.h, which is:
5  *   Copyright (C) 2015 Free Electrons
6  *   Copyright (C) 2015 NextThing Co
7  *
8  *   Maxime Ripard <[email protected]>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  */
15
16 #include <drm/drm_atomic.h>
17 #include <drm/drm_atomic_helper.h>
18 #include <drm/drm_crtc.h>
19 #include <drm/drm_crtc_helper.h>
20 #include <drm/drm_fb_cma_helper.h>
21 #include <drm/drm_gem_cma_helper.h>
22 #include <drm/drm_plane_helper.h>
23 #include <drm/drmP.h>
24
25 #include "sun8i_ui_layer.h"
26 #include "sun8i_mixer.h"
27 #include "sun8i_ui_scaler.h"
28
29 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
30                                   int overlay, bool enable)
31 {
32         u32 val;
33
34         DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
35                          enable ? "En" : "Dis", channel, overlay);
36
37         if (enable)
38                 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
39         else
40                 val = 0;
41
42         regmap_update_bits(mixer->engine.regs,
43                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
44                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
45
46         if (enable)
47                 val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
48         else
49                 val = 0;
50
51         regmap_update_bits(mixer->engine.regs,
52                            SUN8I_MIXER_BLEND_PIPE_CTL,
53                            SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
54 }
55
56 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
57                                        int overlay, struct drm_plane *plane)
58 {
59         struct drm_plane_state *state = plane->state;
60         u32 src_w, src_h, dst_w, dst_h;
61         u32 outsize, insize;
62         u32 hphase, vphase;
63
64         DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
65                          channel, overlay);
66
67         src_w = drm_rect_width(&state->src) >> 16;
68         src_h = drm_rect_height(&state->src) >> 16;
69         dst_w = drm_rect_width(&state->dst);
70         dst_h = drm_rect_height(&state->dst);
71
72         hphase = state->src.x1 & 0xffff;
73         vphase = state->src.y1 & 0xffff;
74
75         insize = SUN8I_MIXER_SIZE(src_w, src_h);
76         outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
77
78         if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
79                 bool interlaced = false;
80                 u32 val;
81
82                 DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
83                                  dst_w, dst_h);
84                 regmap_write(mixer->engine.regs,
85                              SUN8I_MIXER_GLOBAL_SIZE,
86                              outsize);
87                 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
88                              outsize);
89
90                 if (state->crtc)
91                         interlaced = state->crtc->state->adjusted_mode.flags
92                                 & DRM_MODE_FLAG_INTERLACE;
93
94                 if (interlaced)
95                         val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
96                 else
97                         val = 0;
98
99                 regmap_update_bits(mixer->engine.regs,
100                                    SUN8I_MIXER_BLEND_OUTCTL,
101                                    SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
102                                    val);
103
104                 DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
105                                  interlaced ? "on" : "off");
106         }
107
108         /* Set height and width */
109         DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
110                          state->src.x1 >> 16, state->src.y1 >> 16);
111         DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
112         regmap_write(mixer->engine.regs,
113                      SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
114                      insize);
115         regmap_write(mixer->engine.regs,
116                      SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
117                      insize);
118
119         if (insize != outsize || hphase || vphase) {
120                 u32 hscale, vscale;
121
122                 DRM_DEBUG_DRIVER("HW scaling is enabled\n");
123
124                 hscale = state->src_w / state->crtc_w;
125                 vscale = state->src_h / state->crtc_h;
126
127                 sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
128                                       dst_h, hscale, vscale, hphase, vphase);
129                 sun8i_ui_scaler_enable(mixer, channel, true);
130         } else {
131                 DRM_DEBUG_DRIVER("HW scaling is not needed\n");
132                 sun8i_ui_scaler_enable(mixer, channel, false);
133         }
134
135         /* Set base coordinates */
136         DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
137                          state->dst.x1, state->dst.y1);
138         DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
139         regmap_write(mixer->engine.regs,
140                      SUN8I_MIXER_BLEND_ATTR_COORD(channel),
141                      SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
142         regmap_write(mixer->engine.regs,
143                      SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
144                      outsize);
145
146         return 0;
147 }
148
149 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
150                                          int overlay, struct drm_plane *plane)
151 {
152         struct drm_plane_state *state = plane->state;
153         const struct de2_fmt_info *fmt_info;
154         u32 val;
155
156         fmt_info = sun8i_mixer_format_info(state->fb->format->format);
157         if (!fmt_info || !fmt_info->rgb) {
158                 DRM_DEBUG_DRIVER("Invalid format\n");
159                 return -EINVAL;
160         }
161
162         val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
163         regmap_update_bits(mixer->engine.regs,
164                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
165                            SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
166
167         return 0;
168 }
169
170 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
171                                         int overlay, struct drm_plane *plane)
172 {
173         struct drm_plane_state *state = plane->state;
174         struct drm_framebuffer *fb = state->fb;
175         struct drm_gem_cma_object *gem;
176         dma_addr_t paddr;
177         int bpp;
178
179         /* Get the physical address of the buffer in memory */
180         gem = drm_fb_cma_get_gem_obj(fb, 0);
181
182         DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
183
184         /* Compute the start of the displayed memory */
185         bpp = fb->format->cpp[0];
186         paddr = gem->paddr + fb->offsets[0];
187
188         /* Fixup framebuffer address for src coordinates */
189         paddr += (state->src.x1 >> 16) * bpp;
190         paddr += (state->src.y1 >> 16) * fb->pitches[0];
191
192         /* Set the line width */
193         DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
194         regmap_write(mixer->engine.regs,
195                      SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
196                      fb->pitches[0]);
197
198         DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
199
200         regmap_write(mixer->engine.regs,
201                      SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay),
202                      lower_32_bits(paddr));
203
204         return 0;
205 }
206
207 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
208                                        struct drm_plane_state *state)
209 {
210         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
211         struct drm_crtc *crtc = state->crtc;
212         struct drm_crtc_state *crtc_state;
213         int min_scale, max_scale;
214
215         if (!crtc)
216                 return 0;
217
218         crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
219         if (WARN_ON(!crtc_state))
220                 return -EINVAL;
221
222         min_scale = DRM_PLANE_HELPER_NO_SCALING;
223         max_scale = DRM_PLANE_HELPER_NO_SCALING;
224
225         if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
226                 min_scale = SUN8I_UI_SCALER_SCALE_MIN;
227                 max_scale = SUN8I_UI_SCALER_SCALE_MAX;
228         }
229
230         return drm_atomic_helper_check_plane_state(state, crtc_state,
231                                                    min_scale, max_scale,
232                                                    true, true);
233 }
234
235 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
236                                           struct drm_plane_state *old_state)
237 {
238         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
239         struct sun8i_mixer *mixer = layer->mixer;
240
241         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false);
242 }
243
244 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
245                                          struct drm_plane_state *old_state)
246 {
247         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
248         struct sun8i_mixer *mixer = layer->mixer;
249
250         if (!plane->state->visible) {
251                 sun8i_ui_layer_enable(mixer, layer->channel,
252                                       layer->overlay, false);
253                 return;
254         }
255
256         sun8i_ui_layer_update_coord(mixer, layer->channel,
257                                     layer->overlay, plane);
258         sun8i_ui_layer_update_formats(mixer, layer->channel,
259                                       layer->overlay, plane);
260         sun8i_ui_layer_update_buffer(mixer, layer->channel,
261                                      layer->overlay, plane);
262         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, true);
263 }
264
265 static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
266         .atomic_check   = sun8i_ui_layer_atomic_check,
267         .atomic_disable = sun8i_ui_layer_atomic_disable,
268         .atomic_update  = sun8i_ui_layer_atomic_update,
269 };
270
271 static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
272         .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
273         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
274         .destroy                = drm_plane_cleanup,
275         .disable_plane          = drm_atomic_helper_disable_plane,
276         .reset                  = drm_atomic_helper_plane_reset,
277         .update_plane           = drm_atomic_helper_update_plane,
278 };
279
280 static const u32 sun8i_ui_layer_formats[] = {
281         DRM_FORMAT_ABGR1555,
282         DRM_FORMAT_ABGR4444,
283         DRM_FORMAT_ABGR8888,
284         DRM_FORMAT_ARGB1555,
285         DRM_FORMAT_ARGB4444,
286         DRM_FORMAT_ARGB8888,
287         DRM_FORMAT_BGR565,
288         DRM_FORMAT_BGR888,
289         DRM_FORMAT_BGRA5551,
290         DRM_FORMAT_BGRA4444,
291         DRM_FORMAT_BGRA8888,
292         DRM_FORMAT_BGRX8888,
293         DRM_FORMAT_RGB565,
294         DRM_FORMAT_RGB888,
295         DRM_FORMAT_RGBA4444,
296         DRM_FORMAT_RGBA5551,
297         DRM_FORMAT_RGBA8888,
298         DRM_FORMAT_RGBX8888,
299         DRM_FORMAT_XBGR8888,
300         DRM_FORMAT_XRGB8888,
301 };
302
303 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
304                                                struct sun8i_mixer *mixer,
305                                                int index)
306 {
307         enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
308         int channel = mixer->cfg->vi_num + index;
309         struct sun8i_ui_layer *layer;
310         int ret;
311
312         layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
313         if (!layer)
314                 return ERR_PTR(-ENOMEM);
315
316         if (index == 0)
317                 type = DRM_PLANE_TYPE_PRIMARY;
318
319         /* possible crtcs are set later */
320         ret = drm_universal_plane_init(drm, &layer->plane, 0,
321                                        &sun8i_ui_layer_funcs,
322                                        sun8i_ui_layer_formats,
323                                        ARRAY_SIZE(sun8i_ui_layer_formats),
324                                        NULL, type, NULL);
325         if (ret) {
326                 dev_err(drm->dev, "Couldn't initialize layer\n");
327                 return ERR_PTR(ret);
328         }
329
330         /* fixed zpos for now */
331         ret = drm_plane_create_zpos_immutable_property(&layer->plane, channel);
332         if (ret) {
333                 dev_err(drm->dev, "Couldn't add zpos property\n");
334                 return ERR_PTR(ret);
335         }
336
337         drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
338         layer->mixer = mixer;
339         layer->channel = channel;
340         layer->overlay = 0;
341
342         return layer;
343 }
This page took 0.055405 seconds and 4 git commands to generate.