]> Git Repo - linux.git/blob - drivers/gpu/drm/sun4i/sun8i_ui_layer.c
Merge branch 'fixes' into misc
[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         struct drm_rect clip;
215
216         if (!crtc)
217                 return 0;
218
219         crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
220         if (WARN_ON(!crtc_state))
221                 return -EINVAL;
222
223         clip.x1 = 0;
224         clip.y1 = 0;
225         clip.x2 = crtc_state->adjusted_mode.hdisplay;
226         clip.y2 = crtc_state->adjusted_mode.vdisplay;
227
228         min_scale = DRM_PLANE_HELPER_NO_SCALING;
229         max_scale = DRM_PLANE_HELPER_NO_SCALING;
230
231         if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
232                 min_scale = SUN8I_UI_SCALER_SCALE_MIN;
233                 max_scale = SUN8I_UI_SCALER_SCALE_MAX;
234         }
235
236         return drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
237                                                    min_scale, max_scale,
238                                                    true, true);
239 }
240
241 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
242                                           struct drm_plane_state *old_state)
243 {
244         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
245         struct sun8i_mixer *mixer = layer->mixer;
246
247         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false);
248 }
249
250 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
251                                          struct drm_plane_state *old_state)
252 {
253         struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
254         struct sun8i_mixer *mixer = layer->mixer;
255
256         if (!plane->state->visible) {
257                 sun8i_ui_layer_enable(mixer, layer->channel,
258                                       layer->overlay, false);
259                 return;
260         }
261
262         sun8i_ui_layer_update_coord(mixer, layer->channel,
263                                     layer->overlay, plane);
264         sun8i_ui_layer_update_formats(mixer, layer->channel,
265                                       layer->overlay, plane);
266         sun8i_ui_layer_update_buffer(mixer, layer->channel,
267                                      layer->overlay, plane);
268         sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, true);
269 }
270
271 static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
272         .atomic_check   = sun8i_ui_layer_atomic_check,
273         .atomic_disable = sun8i_ui_layer_atomic_disable,
274         .atomic_update  = sun8i_ui_layer_atomic_update,
275 };
276
277 static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
278         .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
279         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
280         .destroy                = drm_plane_cleanup,
281         .disable_plane          = drm_atomic_helper_disable_plane,
282         .reset                  = drm_atomic_helper_plane_reset,
283         .update_plane           = drm_atomic_helper_update_plane,
284 };
285
286 static const u32 sun8i_ui_layer_formats[] = {
287         DRM_FORMAT_ABGR1555,
288         DRM_FORMAT_ABGR4444,
289         DRM_FORMAT_ABGR8888,
290         DRM_FORMAT_ARGB1555,
291         DRM_FORMAT_ARGB4444,
292         DRM_FORMAT_ARGB8888,
293         DRM_FORMAT_BGR565,
294         DRM_FORMAT_BGR888,
295         DRM_FORMAT_BGRA5551,
296         DRM_FORMAT_BGRA4444,
297         DRM_FORMAT_BGRA8888,
298         DRM_FORMAT_BGRX8888,
299         DRM_FORMAT_RGB565,
300         DRM_FORMAT_RGB888,
301         DRM_FORMAT_RGBA4444,
302         DRM_FORMAT_RGBA5551,
303         DRM_FORMAT_RGBA8888,
304         DRM_FORMAT_RGBX8888,
305         DRM_FORMAT_XBGR8888,
306         DRM_FORMAT_XRGB8888,
307 };
308
309 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
310                                                struct sun8i_mixer *mixer,
311                                                int index)
312 {
313         enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
314         int channel = mixer->cfg->vi_num + index;
315         struct sun8i_ui_layer *layer;
316         int ret;
317
318         layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
319         if (!layer)
320                 return ERR_PTR(-ENOMEM);
321
322         if (index == 0)
323                 type = DRM_PLANE_TYPE_PRIMARY;
324
325         /* possible crtcs are set later */
326         ret = drm_universal_plane_init(drm, &layer->plane, 0,
327                                        &sun8i_ui_layer_funcs,
328                                        sun8i_ui_layer_formats,
329                                        ARRAY_SIZE(sun8i_ui_layer_formats),
330                                        NULL, type, NULL);
331         if (ret) {
332                 dev_err(drm->dev, "Couldn't initialize layer\n");
333                 return ERR_PTR(ret);
334         }
335
336         /* fixed zpos for now */
337         ret = drm_plane_create_zpos_immutable_property(&layer->plane, channel);
338         if (ret) {
339                 dev_err(drm->dev, "Couldn't add zpos property\n");
340                 return ERR_PTR(ret);
341         }
342
343         drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
344         layer->mixer = mixer;
345         layer->channel = channel;
346         layer->overlay = 0;
347
348         return layer;
349 }
This page took 0.057631 seconds and 4 git commands to generate.