Merge branch 'for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
[linux-2.6-microblaze.git] / drivers / gpu / drm / armada / armada_plane.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 Russell King
4  *  Rewritten from the dovefb driver, and Armada510 manuals.
5  */
6
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_fourcc.h>
10 #include <drm/drm_plane_helper.h>
11
12 #include "armada_crtc.h"
13 #include "armada_drm.h"
14 #include "armada_fb.h"
15 #include "armada_gem.h"
16 #include "armada_hw.h"
17 #include "armada_plane.h"
18 #include "armada_trace.h"
19
20 static const uint32_t armada_primary_formats[] = {
21         DRM_FORMAT_UYVY,
22         DRM_FORMAT_YUYV,
23         DRM_FORMAT_VYUY,
24         DRM_FORMAT_YVYU,
25         DRM_FORMAT_ARGB8888,
26         DRM_FORMAT_ABGR8888,
27         DRM_FORMAT_XRGB8888,
28         DRM_FORMAT_XBGR8888,
29         DRM_FORMAT_RGB888,
30         DRM_FORMAT_BGR888,
31         DRM_FORMAT_ARGB1555,
32         DRM_FORMAT_ABGR1555,
33         DRM_FORMAT_RGB565,
34         DRM_FORMAT_BGR565,
35 };
36
37 void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
38         u16 pitches[3], bool interlaced)
39 {
40         struct drm_framebuffer *fb = state->fb;
41         const struct drm_format_info *format = fb->format;
42         unsigned int num_planes = format->num_planes;
43         unsigned int x = state->src.x1 >> 16;
44         unsigned int y = state->src.y1 >> 16;
45         u32 addr = drm_fb_obj(fb)->dev_addr;
46         int i;
47
48         DRM_DEBUG_KMS("pitch %u x %d y %d bpp %d\n",
49                       fb->pitches[0], x, y, format->cpp[0] * 8);
50
51         if (num_planes > 3)
52                 num_planes = 3;
53
54         addrs[0][0] = addr + fb->offsets[0] + y * fb->pitches[0] +
55                       x * format->cpp[0];
56         pitches[0] = fb->pitches[0];
57
58         y /= format->vsub;
59         x /= format->hsub;
60
61         for (i = 1; i < num_planes; i++) {
62                 addrs[0][i] = addr + fb->offsets[i] + y * fb->pitches[i] +
63                               x * format->cpp[i];
64                 pitches[i] = fb->pitches[i];
65         }
66         for (; i < 3; i++) {
67                 addrs[0][i] = 0;
68                 pitches[i] = 0;
69         }
70         if (interlaced) {
71                 for (i = 0; i < 3; i++) {
72                         addrs[1][i] = addrs[0][i] + pitches[i];
73                         pitches[i] *= 2;
74                 }
75         } else {
76                 for (i = 0; i < 3; i++)
77                         addrs[1][i] = addrs[0][i];
78         }
79 }
80
81 int armada_drm_plane_prepare_fb(struct drm_plane *plane,
82         struct drm_plane_state *state)
83 {
84         DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
85                 plane->base.id, plane->name,
86                 state->fb ? state->fb->base.id : 0);
87
88         /*
89          * Take a reference on the new framebuffer - we want to
90          * hold on to it while the hardware is displaying it.
91          */
92         if (state->fb)
93                 drm_framebuffer_get(state->fb);
94         return 0;
95 }
96
97 void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
98         struct drm_plane_state *old_state)
99 {
100         DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
101                 plane->base.id, plane->name,
102                 old_state->fb ? old_state->fb->base.id : 0);
103
104         if (old_state->fb)
105                 drm_framebuffer_put(old_state->fb);
106 }
107
108 int armada_drm_plane_atomic_check(struct drm_plane *plane,
109         struct drm_plane_state *state)
110 {
111         struct armada_plane_state *st = to_armada_plane_state(state);
112         struct drm_crtc *crtc = state->crtc;
113         struct drm_crtc_state *crtc_state;
114         bool interlace;
115         int ret;
116
117         if (!state->fb || WARN_ON(!state->crtc)) {
118                 state->visible = false;
119                 return 0;
120         }
121
122         if (state->state)
123                 crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
124         else
125                 crtc_state = crtc->state;
126
127         ret = drm_atomic_helper_check_plane_state(state, crtc_state, 0,
128                                                   INT_MAX, true, false);
129         if (ret)
130                 return ret;
131
132         interlace = crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE;
133         if (interlace) {
134                 if ((state->dst.y1 | state->dst.y2) & 1)
135                         return -EINVAL;
136                 st->src_hw = drm_rect_height(&state->src) >> 17;
137                 st->dst_yx = state->dst.y1 >> 1;
138                 st->dst_hw = drm_rect_height(&state->dst) >> 1;
139         } else {
140                 st->src_hw = drm_rect_height(&state->src) >> 16;
141                 st->dst_yx = state->dst.y1;
142                 st->dst_hw = drm_rect_height(&state->dst);
143         }
144
145         st->src_hw <<= 16;
146         st->src_hw |= drm_rect_width(&state->src) >> 16;
147         st->dst_yx <<= 16;
148         st->dst_yx |= state->dst.x1 & 0x0000ffff;
149         st->dst_hw <<= 16;
150         st->dst_hw |= drm_rect_width(&state->dst) & 0x0000ffff;
151
152         armada_drm_plane_calc(state, st->addrs, st->pitches, interlace);
153         st->interlace = interlace;
154
155         return 0;
156 }
157
158 static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
159         struct drm_plane_state *old_state)
160 {
161         struct drm_plane_state *state = plane->state;
162         struct armada_crtc *dcrtc;
163         struct armada_regs *regs;
164         u32 cfg, cfg_mask, val;
165         unsigned int idx;
166
167         DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
168
169         if (!state->fb || WARN_ON(!state->crtc))
170                 return;
171
172         DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n",
173                 plane->base.id, plane->name,
174                 state->crtc->base.id, state->crtc->name,
175                 state->fb->base.id,
176                 old_state->visible, state->visible);
177
178         dcrtc = drm_to_armada_crtc(state->crtc);
179         regs = dcrtc->regs + dcrtc->regs_idx;
180
181         idx = 0;
182         if (!old_state->visible && state->visible) {
183                 val = CFG_PDWN64x66;
184                 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
185                         val |= CFG_PDWN256x24;
186                 armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
187         }
188         val = armada_src_hw(state);
189         if (armada_src_hw(old_state) != val)
190                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
191         val = armada_dst_yx(state);
192         if (armada_dst_yx(old_state) != val)
193                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
194         val = armada_dst_hw(state);
195         if (armada_dst_hw(old_state) != val)
196                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
197         if (old_state->src.x1 != state->src.x1 ||
198             old_state->src.y1 != state->src.y1 ||
199             old_state->fb != state->fb ||
200             state->crtc->state->mode_changed) {
201                 armada_reg_queue_set(regs, idx, armada_addr(state, 0, 0),
202                                      LCD_CFG_GRA_START_ADDR0);
203                 armada_reg_queue_set(regs, idx, armada_addr(state, 1, 0),
204                                      LCD_CFG_GRA_START_ADDR1);
205                 armada_reg_queue_mod(regs, idx, armada_pitch(state, 0), 0xffff,
206                                      LCD_CFG_GRA_PITCH);
207         }
208         if (old_state->fb != state->fb ||
209             state->crtc->state->mode_changed) {
210                 cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) |
211                       CFG_GRA_MOD(drm_fb_to_armada_fb(state->fb)->mod);
212                 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
213                         cfg |= CFG_PALETTE_ENA;
214                 if (state->visible)
215                         cfg |= CFG_GRA_ENA;
216                 if (to_armada_plane_state(state)->interlace)
217                         cfg |= CFG_GRA_FTOGGLE;
218                 cfg_mask = CFG_GRAFORMAT |
219                            CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
220                                        CFG_SWAPYU | CFG_YUV2RGB) |
221                            CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
222                            CFG_GRA_ENA;
223         } else if (old_state->visible != state->visible) {
224                 cfg = state->visible ? CFG_GRA_ENA : 0;
225                 cfg_mask = CFG_GRA_ENA;
226         } else {
227                 cfg = cfg_mask = 0;
228         }
229         if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) ||
230             drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) {
231                 cfg_mask |= CFG_GRA_HSMOOTH;
232                 if (drm_rect_width(&state->src) >> 16 !=
233                     drm_rect_width(&state->dst))
234                         cfg |= CFG_GRA_HSMOOTH;
235         }
236
237         if (cfg_mask)
238                 armada_reg_queue_mod(regs, idx, cfg, cfg_mask,
239                                      LCD_SPU_DMA_CTRL0);
240
241         dcrtc->regs_idx += idx;
242 }
243
244 static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
245         struct drm_plane_state *old_state)
246 {
247         struct armada_crtc *dcrtc;
248         struct armada_regs *regs;
249         unsigned int idx = 0;
250
251         DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
252
253         if (!old_state->crtc)
254                 return;
255
256         DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n",
257                 plane->base.id, plane->name,
258                 old_state->crtc->base.id, old_state->crtc->name,
259                 old_state->fb->base.id);
260
261         dcrtc = drm_to_armada_crtc(old_state->crtc);
262         regs = dcrtc->regs + dcrtc->regs_idx;
263
264         /* Disable plane and power down most RAMs and FIFOs */
265         armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
266         armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
267                              CFG_PDWN32x32 | CFG_PDWN64x66,
268                              0, LCD_SPU_SRAM_PARA1);
269
270         dcrtc->regs_idx += idx;
271 }
272
273 static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
274         .prepare_fb     = armada_drm_plane_prepare_fb,
275         .cleanup_fb     = armada_drm_plane_cleanup_fb,
276         .atomic_check   = armada_drm_plane_atomic_check,
277         .atomic_update  = armada_drm_primary_plane_atomic_update,
278         .atomic_disable = armada_drm_primary_plane_atomic_disable,
279 };
280
281 void armada_plane_reset(struct drm_plane *plane)
282 {
283         struct armada_plane_state *st;
284         if (plane->state)
285                 __drm_atomic_helper_plane_destroy_state(plane->state);
286         kfree(plane->state);
287         st = kzalloc(sizeof(*st), GFP_KERNEL);
288         if (st)
289                 __drm_atomic_helper_plane_reset(plane, &st->base);
290 }
291
292 struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane)
293 {
294         struct armada_plane_state *st;
295
296         if (WARN_ON(!plane->state))
297                 return NULL;
298
299         st = kmemdup(plane->state, sizeof(*st), GFP_KERNEL);
300         if (st)
301                 __drm_atomic_helper_plane_duplicate_state(plane, &st->base);
302
303         return &st->base;
304 }
305
306 static const struct drm_plane_funcs armada_primary_plane_funcs = {
307         .update_plane   = drm_atomic_helper_update_plane,
308         .disable_plane  = drm_atomic_helper_disable_plane,
309         .destroy        = drm_primary_helper_destroy,
310         .reset          = armada_plane_reset,
311         .atomic_duplicate_state = armada_plane_duplicate_state,
312         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
313 };
314
315 int armada_drm_primary_plane_init(struct drm_device *drm,
316         struct drm_plane *primary)
317 {
318         int ret;
319
320         drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs);
321
322         ret = drm_universal_plane_init(drm, primary, 0,
323                                        &armada_primary_plane_funcs,
324                                        armada_primary_formats,
325                                        ARRAY_SIZE(armada_primary_formats),
326                                        NULL,
327                                        DRM_PLANE_TYPE_PRIMARY, NULL);
328
329         return ret;
330 }